package bx

import (
	"bytes"
	"encoding/binary"
	"fmt"
)

type BxDataPack struct {
	WRAP_A5_NUM int
	WRAP_5A_NUM int
	dstAddr     uint16
	srcAddr     uint16
	r0          byte
	r1          byte
	r2          byte
	option      byte
	crcMode     byte
	dispMode    byte
	deviceType  byte
	version     byte
	dataLen     uint16
	data        []byte
	crc         uint16
}

func NewBxDataPackData(data []byte) BxDataPack {
	return BxDataPack{
		data:        data,
		dataLen:     uint16(len(data)),
		WRAP_A5_NUM: 8,
		WRAP_5A_NUM: 1,
		dstAddr:     0xfffe,
		srcAddr:     0x8000,
		deviceType:  0xfe,
		version:     0x02,
	}
}
func NewBxDataPackCmd(cmd BxCmd) BxDataPack {
	b := cmd.Build()
	fmt.Printf("build Data:% 02x\n", b)
	return BxDataPack{
		data:        b,
		dataLen:     uint16(len(b)),
		WRAP_A5_NUM: 8,
		WRAP_5A_NUM: 1,
		dstAddr:     0x0002,
		srcAddr:     0x8000,
		deviceType:  0xfe,
		version:     0x02,
	}
}

// SetDispType 注：特殊动态区不支持动态模式
// 0x00：普通模式，动态区与节目可同时显示，但各区域不可重叠。
// 0x01：动态模式，优先显示动态区，无动态区则显示节目，动态区与节目区可重叠。
func (dp *BxDataPack) SetDispType(typ byte) {
	dp.dispMode = typ
}

func (dp *BxDataPack) wrap(src []byte) []byte {
	len := len(src)
	for _, v := range src { //每次转义会使长度+1
		if v == 0xa5 || v == 0x5a || v == 0xa6 || v == 0x5b {
			len++
		}
	}
	len += dp.WRAP_5A_NUM
	len += dp.WRAP_A5_NUM
	dst := make([]byte, len)
	offset := 0
	for i := 0; i < dp.WRAP_A5_NUM; i++ {
		dst[offset] = 0xa5
		offset++
	}
	//转义
	for _, v := range src {
		if v == 0xa5 {
			dst[offset] = 0xa6
			offset++
			dst[offset] = 0xa2
			offset++
		} else if v == 0xa6 {
			dst[offset] = 0xa6
			offset++
			dst[offset] = 0xa1
			offset++
		} else if v == 0x5a {
			dst[offset] = 0x5b
			offset++
			dst[offset] = 0xa2
			offset++
		} else if v == 0x5b {
			dst[offset] = 0x5b
			offset++
			dst[offset] = 0x01
			offset++
		} else {
			dst[offset] = v
			offset++
		}
	}

	for i := 0; i < dp.WRAP_5A_NUM; i++ {
		dst[offset] = 0x5a
		offset++
	}
	return dst
}

func (dp *BxDataPack) Pack() []byte {
	w := bytes.NewBuffer(make([]byte, 0, 1024))
	//目标地址
	binary.Write(w, binary.LittleEndian, dp.dstAddr)
	//源地址
	binary.Write(w, binary.LittleEndian, dp.srcAddr)
	binary.Write(w, binary.LittleEndian, dp.r0)
	binary.Write(w, binary.LittleEndian, dp.r1)
	binary.Write(w, binary.LittleEndian, dp.r2)
	binary.Write(w, binary.LittleEndian, dp.option)
	binary.Write(w, binary.LittleEndian, dp.crcMode)
	binary.Write(w, binary.LittleEndian, dp.dispMode)
	binary.Write(w, binary.LittleEndian, dp.deviceType)
	binary.Write(w, binary.LittleEndian, dp.version)
	binary.Write(w, binary.LittleEndian, dp.dataLen)
	binary.Write(w, binary.LittleEndian, dp.data)
	dp.crc = 0x0
	binary.Write(w, binary.LittleEndian, dp.crc)
	data := w.Bytes()
	len := w.Len()
	dp.crc = CRC16(data, 0, len-2)
	data[len-2] = byte(dp.crc & 0xff)
	data[len-1] = byte(dp.crc >> 8)
	return dp.wrap(data)
}

func dpParse(src []byte, length int) *BxDataPack {
	dst := unwrap(src, length)
	if dst == nil {
		return nil
	}
	crcCalculated := CRC16(dst, 0, len(dst)-2)
	crcGot := bytesToUint16(dst, len(dst)-2, binary.LittleEndian)

	if crcCalculated != crcGot {
		return nil
	}
	dp := BxDataPack{}
	offset := 0

	//目标地址
	dp.dstAddr = bytesToUint16(dst, offset, binary.LittleEndian)
	offset += 2
	//源地址
	dp.srcAddr = bytesToUint16(dst, offset, binary.LittleEndian)
	offset += 2
	//保留字 r0,r1,r2
	dp.r0 = dst[offset]
	offset++
	dp.r1 = dst[offset]
	offset++
	dp.r2 = dst[offset]
	offset++

	dp.option = dst[offset]
	offset++
	dp.crcMode = dst[offset]
	offset++
	dp.dispMode = dst[offset]
	offset++
	dp.deviceType = dst[offset]
	offset++
	dp.version = dst[offset]
	offset++
	dp.dataLen = bytesToUint16(dst, offset, binary.LittleEndian)
	offset += 2
	//数据
	dp.data = dst[offset : offset+int(dp.dataLen)]
	offset += int(dp.dataLen)
	dp.crc = bytesToUint16(dst, offset, binary.LittleEndian)
	return &dp
}

func unwrap(src []byte, length int) []byte {
	len := length
	for _, v := range src {
		if v == 0xa5 || v == 0x5a || v == 0xa6 {
			len--
		}
	}
	//如果计算的帧长度为0, 说明数据不正确
	if len == 0 {
		return nil
	}
	dst := make([]byte, len)
	offset := 0
	for i := 0; i < length; {
		if src[i] == 0xa5 || src[i] == 0x5a {
			i++
		} else if src[i] == 0xa6 {
			if src[i+1] == 0x01 {
				dst[offset] = 0xa6
				offset++
				i = i + 2
			} else if src[i+1] == 0x02 {
				dst[offset] = 0xa5
				offset++
				i = i + 2
			} else {
				return nil
			}
		} else if src[i] == 0x5b {
			if src[i+1] == 0x01 {
				dst[offset] = 0x5b
				offset++
				i = i + 2
			} else if src[i+1] == 0x02 {
				dst[offset] = 0x5a
				offset++
				i = i + 2
			} else {
				return nil
			}
		} else {
			dst[offset] = src[i]
			offset++
			i++
		}
	}
	return dst
}
