package ws

import (
	"common/log"
	"common/proto/sc/sc_pb"
	"context"
	"crypto/tls"
	"errors"
	"fmt"
	"github.com/gorilla/websocket"
	"google.golang.org/protobuf/proto"
)

type Client struct {
	addr  string
	token string

	conn       *websocket.Conn
	ctx        context.Context
	cancel     context.CancelFunc
	writeChaen chan []byte
}

func NewClient(addr string, token string) *Client {
	c := &Client{
		addr:       addr,
		token:      token,
		writeChaen: make(chan []byte, 1024),
	}
	c.ctx, c.cancel = context.WithCancel(context.Background())
	return c
}

func (c *Client) Start() {
	go func() {
		dialer := websocket.DefaultDialer
		dialer.TLSClientConfig = &tls.Config{InsecureSkipVerify: true}
		conn, _, err := dialer.Dial(fmt.Sprintf("%v?token=%v", c.addr, c.token), nil)
		if err != nil {
			log.Errorf("connect error %v", err.Error())
			return
		}
		c.conn = conn
		_ = c.WriteMessage(sc_pb.MessageID_MESSAGE_ID_ENTER_INSTANCE, &sc_pb.C2S_EnterInstance{
			InstanceID: 1,
		})
		go c.startReader()
		go c.startWriter()
		select {
		case <-c.ctx.Done():
			c.onStop()
			return
		}
	}()
}

func (c *Client) startReader() {
	for {
		select {
		case <-c.ctx.Done():
			return
		default:
			_, msgByte, err := c.conn.ReadMessage()
			if err != nil {
				log.Errorf("read error quit %s", err)
				c.Stop()
				return
			}

			msg := &sc_pb.Message{}
			if err = proto.Unmarshal(msgByte, msg); err != nil {
				continue
			}
			if r, ok := router[msg.ID]; ok {
				r.Handle(msg.Payload, c)
			}
		}
	}
}

func (c *Client) startWriter() {
	for {
		select {
		case data, ok := <-c.writeChaen:
			if ok {
				if err := c.conn.WriteMessage(websocket.BinaryMessage, data); err != nil {
					log.Errorf("Send Buff Data error:, %s Conn Writer exit", err)
					return
				}
			} else {
				log.Errorf("msgBuffChan is Closed")
			}
		case <-c.ctx.Done():
			return
		}
	}
}

// WriteMessage 发送消息给服务器
func (c *Client) WriteMessage(msgID sc_pb.MessageID, data proto.Message) error {
	d, err := proto.Marshal(data)
	if err != nil {
		return err
	}
	p := &sc_pb.Message{
		ID:      msgID,
		Payload: d,
	}
	marshal, _ := proto.Marshal(p)
	select {
	case c.writeChaen <- marshal:
	default:
		return errors.New("send buff chan is full")
	}
	return nil
}

func (c *Client) Stop() {
	c.cancel()
}

func (c *Client) onStop() {
	if err := c.conn.Close(); err != nil {
		log.Errorf("close client error %v", err)
	}
}
