package maincontrollerClient

import (
	"bytes"
	"context"
	"encoding/binary"
	"encoding/json"
	"github.com/gogf/gf/v2/errors/gerror"

	"io"
	"reflect"
	"sync"
	"time"

	"log"

	"go.bug.st/serial"
)

var serialPortMode bool
var serialPort *SerialPort

// SerialPort 串口对象
type SerialPort struct {
	config   *serial.Mode
	address  string
	port     serial.Port
	isOpen   bool
	ctx      context.Context
	cancel   context.CancelFunc
	dataChan chan byte
	onData   []func([]byte)
	ticket   *time.Ticker

	isBIO        bool
	bioChan      chan []byte
	bioWriteLock sync.Mutex
	bioLocker    sync.Mutex
}

// NewSerialPort 创建串口实例
func NewSerialPort(portName string, baudRate int) *SerialPort {
	return &SerialPort{
		address: portName,

		config: &serial.Mode{

			BaudRate: baudRate,
		},
	}
}

func UseSerialPort(portName string, baudRate int) error {
	serialPortMode = true
	serialPort = &SerialPort{
		address: portName,

		config: &serial.Mode{
			BaudRate: baudRate,
		},
	}

	err := serialPort.Open()
	if err != nil {
		return gerror.Wrap(err, "串口连接打开失败")
	}

	serialPort.OnDataReceived(func(data []byte) {
		t := time.NewTimer(time.Millisecond * 100)
		if serialPort.IsBIO() {
			<-t.C
			for {
				select {
				case <-serialPort.bioChan:
					continue
				default:
					serialPort.bioChan <- data
				}
				break
			}
		} else {
			log.Printf("无效上传的数据(仅支持BIO模式),丢弃:% X", (data))
		}
	})

	return nil
}

// Open 打开串口连接
func (sp *SerialPort) Open() error {

	if sp.isOpen {
		return gerror.New("port is already open")
	}

	port, err := serial.Open(sp.address, sp.config)
	if err != nil {
		return gerror.Wrap(err, "串口打开失败")
	}

	sp.dataChan = make(chan byte, 1)
	sp.bioChan = make(chan []byte, 1)
	sp.port = port
	sp.ctx, sp.cancel = context.WithCancel(context.Background())
	sp.isOpen = true
	go sp.readLoop()
	go sp.readChanLoop()
	return nil
}

func (sp *SerialPort) BIO(data []byte) ([]byte, error) {
	sp.bioWriteLock.Lock()
	defer sp.bioWriteLock.Unlock()
	sp.SetisBIO(true)
	_, err := sp.port.Write(data)
	if err != nil {
		return nil, gerror.New("数据写入失败")
	}
	timer := time.NewTimer(time.Second * 3)
	select {
	case <-timer.C:
		sp.SetisBIO(false)
		return nil, gerror.New("数据读取超时")
	case message := <-sp.bioChan:
		sp.SetisBIO(false)
		return message, nil
	}
}

// Close 关闭串口连接
func (sp *SerialPort) Close() error {

	if !sp.isOpen {
		return gerror.New("port is already closed")
	}

	// 执行关闭操作

	err := sp.port.Close()
	sp.isOpen = false
	return err
}

// OnDataReceived 注册数据接收事件回调
func (sp *SerialPort) OnDataReceived(callback func([]byte)) {
	sp.onData = append(sp.onData, callback)
}

func (sp *SerialPort) readChanLoop() {
	sp.ticket = time.NewTicker(time.Millisecond * 100)

	var buf []byte
	for {
		if !sp.isOpen {
			return
		}
		select {
		case <-sp.ctx.Done():
			return
		case <-sp.ticket.C:
			if len(buf) > 0 {
				callback := sp.onData
				for _, f := range callback {
					go f(buf)
				}
				buf = []byte{}
			}
		case b := <-sp.dataChan:
			buf = append(buf, b)
		}
	}
}

// 读取循环
func (sp *SerialPort) readLoop() {
	buf := make([]byte, 1)
	for {
		select {
		case <-sp.ctx.Done():
			return
		default:
			_, err := sp.port.Read(buf)
			sp.ticket.Reset(time.Millisecond * 100)
			if err != nil {
				if err != io.EOF {
					if sp.isOpen {
						sp.port.Close()
						sp.isOpen = false
					}
				}
				return
			}

			sp.dataChan <- buf[0]
		}
	}
}

func ToBytes(data interface{}) ([]byte, error) {
	if data == nil {
		return nil, nil
	}

	// 处理 []byte 和字符串
	switch v := data.(type) {
	case []byte:
		return v, nil
	case string:
		return []byte(v), nil
	}

	// 处理基本类型
	val := reflect.ValueOf(data)
	for {
		if val.Kind() == reflect.Interface {
			val = val.Elem()
			continue
		}
		break
	}

	switch val.Kind() {
	case reflect.Bool,
		reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64,
		reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64,
		reflect.Float32, reflect.Float64:
		buf := new(bytes.Buffer)
		if err := binary.Write(buf, binary.BigEndian, data); err != nil {
			return nil, err
		}
		return buf.Bytes(), nil

	default:
		// 复杂类型使用JSON序列化
		return json.Marshal(data)
	}

}

func (sp *SerialPort) SetisBIO(isBio bool) {
	sp.bioLocker.Lock()
	sp.isBIO = isBio
	sp.bioLocker.Unlock()
}
func (sp *SerialPort) IsBIO() bool {
	sp.bioLocker.Lock()
	defer sp.bioLocker.Unlock()
	return sp.isBIO
}
