package main

import (
	"encoding/binary"
	"errors"
	"io"
	"sync"
	"time"

	"github.com/goburrow/serial"

	"lc/edge/ipole/modbus"
	"lc/edge/ipole/ym485"
	"lc/edge/ipole/zigbee"
)

const (
	// SerialDefaultTimeout Serial Default timeout
	SerialDefaultTimeout = 2000 * time.Millisecond
	// SerialDefaultAutoReconnect Serial Default auto reconnect count
	SerialDefaultAutoReconnect = 6

	SerialReadMaxLength = 40

	FlagModbusRtu = 0
	FlagChZigbee  = 1
	FlagYm485     = 2
)

var ErrClosedConnection = errors.New("use of closed connection")

// serialPort has configuration and I/O controller.
type serialPort struct {
	// Serial port configuration.
	serial.Config
	mu   sync.Mutex
	port io.ReadWriteCloser
	// if > 0, when disconnected,it will try to reconnect the remote
	// but if we activate close self,it will not to reconnect
	// if == 0 auto reconnect not active
	autoReconnect byte
}

// Connect try to connect the remote server
func (sf *serialPort) Connect() (err error) {
	sf.mu.Lock()
	err = sf.connect()
	sf.mu.Unlock()
	return
}

// Caller must hold the mutex before calling this method.
func (sf *serialPort) connect() error {
	port, err := serial.Open(&sf.Config)
	if err != nil {
		return err
	}
	sf.port = port
	return nil
}

// IsConnected returns a bool signifying whether the client is connected or not.
func (sf *serialPort) IsConnected() (b bool) {
	sf.mu.Lock()
	b = sf.isConnected()
	sf.mu.Unlock()
	return b
}

// Caller must hold the mutex before calling this method.
func (sf *serialPort) isConnected() bool {
	return sf.port != nil
}

// SetAutoReconnect set auto reconnect count
// if cnt == 0, disable auto reconnect
// if cnt > 0 ,enable auto reconnect,but max 6
func (sf *serialPort) SetAutoReconnect(cnt byte) {
	sf.mu.Lock()
	sf.autoReconnect = cnt
	if sf.autoReconnect > 6 {
		sf.autoReconnect = 6
	}
	sf.mu.Unlock()
}

// setSerialConfig set serial config
func (sf *serialPort) setSerialConfig(config serial.Config) {
	sf.Config = config
}

// Close current connection.
func (sf *serialPort) Close() (err error) {
	sf.mu.Lock()
	if sf.port != nil {
		err = sf.port.Close()
		sf.port = nil
	}
	sf.mu.Unlock()
	return
}

func (sf *serialPort) send(aduRequest []byte) error {
	var err error
	var tryCnt byte
	for {
		if _, err = sf.port.Write(aduRequest); err == nil {
			break
		}
		if sf.autoReconnect == 0 {
			break
		}
		for {
			if err = sf.connect(); err == nil {
				break
			}
			if tryCnt++; tryCnt <= sf.autoReconnect {
				return err //超过最大重试次数则退出
			}
			time.Sleep(1 * time.Second) //1秒后重试
		}
	}
	return err
}

func (sf *serialPort) recv(flag uint8) ([]byte, error) {
	var n int
	var aduResponse []byte
	var err error
	data := make([]byte, SerialReadMaxLength, SerialReadMaxLength)
	for {
		if n, err = sf.port.Read(data); err != nil {
			if VerifyCrc16(aduResponse, flag) {
				err = nil //读取完整
				break
			}
			break
		}
		if n > 0 {
			aduResponse = append(aduResponse, data[:n]...)
			if n <= SerialReadMaxLength { //读取少于等于SerialReadMaxLength字节，可能已经读完，判断CRC16确认
				if VerifyCrc16(aduResponse, flag) {
					break //读取完整
				}
			}
		} else if n == 0 {
			break
		}
	}
	return aduResponse, err
}

func (sf *serialPort) waitSendRecv(aduRequest []byte, flag uint8, wait uint) {
	var bytesToRead = 0
	if flag == 0 { //标准modbusRTU协议
		bytesToRead = modbus.CalculateResponseLength(aduRequest)
	} else if flag == 1 { //长和单灯控制器，zigbee协议
		bytesToRead = zigbee.CalculateChddjkZigbeeResponselength(aduRequest)
	}
	time.Sleep(sf.calculateDelay(len(aduRequest) + bytesToRead))
	if wait > 0 {
		time.Sleep(time.Duration(wait) * time.Millisecond)
	}
}

// SendRecvData 发送和接收
func (sf *serialPort) SendRecvData(aduRequest []byte, flag uint8, wait uint) (aduResponse []byte, err error) {
	sf.mu.Lock()
	defer sf.mu.Unlock()
	//连接检查
	if !sf.isConnected() {
		//重试一次
		if err := sf.connect(); err != nil {
			return nil, ErrClosedConnection
		}
	}

	//发送请求
	if err := sf.send(aduRequest); err != nil {
		return nil, err
	}
	//等待发完和从机处理返回
	sf.waitSendRecv(aduRequest, flag, wait)
	//接收响应
	return sf.recv(flag)
}

// SendData 仅发送，用于广播
func (sf *serialPort) SendData(aduRequest []byte, flag uint8, wait uint) error {
	sf.mu.Lock()
	defer sf.mu.Unlock()
	//连接检查
	if !sf.isConnected() {
		//重试一次
		if err := sf.connect(); err != nil {
			return ErrClosedConnection
		}
	}
	err := sf.send(aduRequest)
	if err == nil {
		sf.waitSendRecv(aduRequest, flag, wait) //等待发完
	}
	return err
}

func (sf *serialPort) calculateDelay(chars int) time.Duration {
	var characterDelay, frameDelay int // us

	if sf.BaudRate <= 0 || sf.BaudRate > 19200 {
		characterDelay = 750
		frameDelay = 1750
	} else {
		characterDelay = 15000000 / sf.BaudRate
		frameDelay = 35000000 / sf.BaudRate
	}
	return time.Duration(characterDelay*chars+frameDelay) * time.Microsecond
}

// VerifyCrc16 标准Modbus-RTU，末尾2字节是crc16,大端
// 长和单灯控制器zigbee协议，末尾2字节是crc16
func VerifyCrc16(data []byte, flag uint8) bool {
	if flag == FlagYm485 {
		return ym485.VerifySum(data)
	}
	datalen := len(data)
	if datalen > 4 {
		var crc_ uint16
		crc := modbus.CRC16(data[:datalen-2])
		if flag == FlagModbusRtu {
			crc_ = binary.LittleEndian.Uint16(data[datalen-2 : datalen])
		} else if flag == FlagChZigbee {
			crc_ = binary.BigEndian.Uint16(data[datalen-2 : datalen])
		} else {
			crc_ = binary.LittleEndian.Uint16(data[datalen-2 : datalen])
		}
		if crc == crc_ {
			return true
		}
	}
	return false
}
