package server

import (
	"errors"
	"fmt"
	"github.com/gogf/gf/encoding/gbinary"
	"github.com/gogf/gf/net/gtcp"
	"github.com/gogf/gf/os/glog"
	"github.com/gogf/gf/util/gconv"
	"io"
	"math"
	"net"
	"pt100-gateway/protocol"
	"strings"
	"syscall"
	"time"
)

type Client struct {
	Id           string
	srv          *Server
	conn         *gtcp.Conn
	sendChan     chan []byte
	closeChan    chan struct{}
	closeHandler func(id string, c *Client)
	regHandler   func(id string, c *Client)
	isReg        bool
}

func NewClient(s *Server, conn *gtcp.Conn) *Client {
	return &Client{
		srv:       s,
		conn:      conn,
		sendChan:  make(chan []byte),
		closeChan: make(chan struct{}),
	}
}

func (c *Client) SetId(id string) {
	c.Id = id
}

func (c *Client) SendLoop() {
	for {
		select {
		case buf := <-c.sendChan:
			err := c.send(buf)
			if err != nil {
				glog.Errorf("指令发送失败:%s", err.Error())
				continue
			}
			timer := time.NewTimer(5 * time.Second)
			for {
				select {
				case <-c.closeChan:
					return
				case <-timer.C:
					glog.Error("接收指令超时")
					break
				default:
					receiveBuf, err := c.conn.Recv(-1)
					if err != nil {
						c.readError(err)
						break
					}
					if !c.isReg {
						id := gbinary.DecodeToString(receiveBuf)
						glog.Debugf("收到注册包!id:%s", id)
						c.SetId(id)
						c.isReg = true
						if c.regHandler != nil {
							c.regHandler(c.Id, c)
						}
						continue
					}
					glog.Debugf("收到数据：%2X", receiveBuf)
					if err := c.decodeAndReport(receiveBuf); err != nil {
						glog.Debugf("处理数据失败:%s", err.Error())
						break
					}
				}
				break
			}
		}
	}
}

// decodeAndReport 收到数据：01 03 10 01 05 01 09 FF FF 01 06 FF FF 01 03 FF FF FF FF E7 EB
func (c *Client) decodeAndReport(buf []byte) error {
	length := len(buf)
	var crc crc
	crc.reset().pushBytes(buf[0 : length-2])
	checksum := uint16(buf[length-1])<<8 | uint16(buf[length-2])
	if checksum != crc.value() {
		return errors.New(fmt.Sprintf("modbus: response crc '%v' does not match expected '%v'", checksum, crc.value()))
	}
	if buf[1] == 0x03 {
		data := &protocol.Data{}
		data.Address = gconv.String(gbinary.BeDecodeToInt(buf[0:1]))
		data.Tem1 = caleTemperature(buf[3:5])
		data.Tem2 = caleTemperature(buf[5:7])
		data.Tem3 = caleTemperature(buf[7:9])
		data.Tem4 = caleTemperature(buf[9:11])
		data.Tem5 = caleTemperature(buf[11:13])
		data.Tem6 = caleTemperature(buf[13:15])
		data.Tem7 = caleTemperature(buf[15:17])
		data.Tem8 = caleTemperature(buf[17:19])

		if err := c.srv.ReportStatus(c.Id, data, "status"); err != nil {
			return err
		}
	}
	return nil
}

func caleTemperature(buf []byte) float32 {
	if gbinary.BeDecodeToUint16(buf) == 0xFFFF {
		return 0
	}
	value := gconv.Float32(gbinary.BeDecodeToUint16(buf)&0x7FFF) / 10
	if value < 1 {
		value = float32(math.Floor(float64(value) + 0.5))
	}
	if gbinary.BeDecodeToUint16(buf)&0x8000 == 0x8000 {
		value = value * -1
	}
	return value
}

func (c *Client) readError(err error) {
	defer c.closeConnection()
	if err == io.EOF || isErrConnReset(err) {
		return
	}
	glog.Errorf("读取数据发生错误：%s", err.Error())
}
func (c *Client) closeConnection() {
	_ = c.conn.Close()
	c.conn = nil
	close(c.closeChan)
	c.isReg = false
	if c.closeHandler != nil {
		c.closeHandler(c.Id, c)
	}
}

// isErrConnReset read: connection reset by peer
func isErrConnReset(err error) bool {
	if ne, ok := err.(*net.OpError); ok {
		return strings.Contains(ne.Err.Error(), syscall.ECONNRESET.Error())
	}
	return false
}

func (c *Client) GetSendByte() {
	for {
		c.sendChan <- []byte{0x01, 0x03, 0x00, 0x00, 0x00, 0x08, 0x44, 0x0C}
		time.Sleep(10 * time.Second)
	}
}

func (c *Client) send(buf []byte) error {
	if c.conn == nil {
		return nil
	}
	glog.Debugf("----->%2X", buf)
	err := c.conn.Send(buf)
	if err != nil {
		glog.Error(c.srv.ctx, err)
		c.closeConnection()
		return err
	}
	return nil
}
