package main

import (
	"log"
	"time"

	"git.ali33.ru/fcg-xvii/go-tools/json"
	"github.com/gorilla/websocket"
	"golang.org/x/net/context"
)

type WSClient struct {
	clients *WSClients
	conn    *websocket.Conn
	ctx     context.Context
	cancel  context.CancelFunc
	//callClose    func(*WSClient)
	//callIncoming func(*WSClient, json.Map)
}

func (s *WSClient) SendData(data json.Map) error {
	w, err := s.conn.NextWriter(websocket.TextMessage)
	if err != nil {
		return err
	}
	_, err = w.Write(data.JSON())
	w.Close()
	return err
}

func (s *WSClient) SendSingle(data json.Map) error {
	jm := json.Map{
		"data": data,
	}
	return s.SendData(jm)
}

func (s *WSClient) SendMessage(data json.Map, deadline time.Duration) (<-chan json.Map, error) {
	dl := time.Now().Add(deadline)
	id := s.clients.nextID()
	jm := json.Map{
		"id":   id,
		"data": data,
	}
	if err := s.SendData(jm); err != nil {
		return nil, err
	}
	ctx, _ := context.WithDeadline(context.Background(), dl)
	ch := make(chan json.Map)
	rch := make(chan json.Map)
	s.clients.rStore.Store(id, ch)
	go func() {
		select {
		case <-ctx.Done():
			s.clients.rStore.Delete(id)
			close(rch)
		case rData := <-ch:
			select {
			case rch <- rData:
			default:
			}
		}
	}()
	return rch, nil
}

func (s *WSClient) Context() context.Context {
	return s.ctx
}

func (s *WSClient) Read() <-chan *Message {
	ch := make(chan *Message, 10)
	go func() {
		defer func() {
			s.cancel()
			close(ch)
		}()
		for {
			t, src, err := s.conn.ReadMessage()
			log.Println(t, string(src), err)
			if err != nil {
				s.clients.clientDisconnected(s)
				return
			}
			var jm json.Map
			if err = json.Unmarshal(src, &jm); err == nil {
				if jm.Bool("is_response", false) {
					//log.Println("AAAAAAAAAAAAAAAAAAA")
					if rch, check := s.clients.rStore.LoadAndDelete(jm.Int("id", -1)); check {
						rch.(chan json.Map) <- jm.Map("data", json.Map{})
					}
				} else {
					//log.Println("UUUUUUUUUUUUUUUU")
					mes := MessageFromMap(jm, s)
					select {
					case ch <- mes:
					default:
					}
				}
			}
		}
	}()
	return ch
}

/////////////////////////
