package gnet

import (
	"bytes"
	"encoding/binary"
	"encoding/hex"
	"encoding/xml"
	"errors"
	"math"
	"strings"
)

type Byte byte

func (b Byte) Hex() string {
	return hex.EncodeToString([]byte{byte(b)})
}

func (b Byte) String() string {
	return string(b)
}

//goland:noinspection ALL
type Bytes []byte

// Prepend 将 p 添加到 Bytes 前面
//
//goland:noinspection ALL
func (b Bytes) Prepend(p ...byte) Bytes {
	return append(p, b...)
}

// Append 将 p 添加到 Bytes 后面
//
//goland:noinspection ALL
func (b Bytes) Append(p ...byte) Bytes {
	return append(b, p...)
}

// AppendStr 将 s 转换成 []byte 后添加到 Bytes 后面
//
//goland:noinspection ALL
func (b Bytes) AppendStr(s string) Bytes {
	return append(b, []byte(s)...)
}

// From 从 Bytes 返回第 i 个字节
//
//goland:noinspection ALL
func (b Bytes) From(i int) Byte {
	return Byte(b[i])
}

// Trim 循环 p 并将其从 Bytes 中移除
//
//goland:noinspection ALL
func (b Bytes) Trim(p ...[]byte) Bytes {
	np := b
	for _, x := range p {
		bytes.ReplaceAll(b, x, nil)
	}
	return np
}

// TrimStr 循环 s 并将其转换为 []byte 后从 Bytes 中移除
//
//goland:noinspection ALL
func (b Bytes) TrimStr(s ...string) Bytes {
	ns := b
	for _, x := range s {
		ns = bytes.ReplaceAll(b, []byte(x), nil)
	}
	return ns
}

// TrimNUL 移除 b 字符串内的 NUL 符号
// 参考 https://stackoverflow.com/questions/54285346/remove-null-character-from-string
//
//goland:noinspection ALL
func (b Bytes) TrimNUL() Bytes {
	return bytes.ReplaceAll(b, []byte{'\x00'}, nil)
}

// TrimEnter 移除 b 字符串内的回车符号
//
//goland:noinspection ALL
func (b Bytes) TrimEnter() Bytes {
	return bytes.ReplaceAll(b, []byte{'\r'}, nil)
}

// Remake 将 b 重新分配并返回新的变量
//
//goland:noinspection ALL
func (b Bytes) Remake() Bytes {
	if len(b) == 0 {
		return []byte{}
	}
	n := make([]byte, len(b))
	for i := 0; i < len(b); i++ {
		n[i] = b[i]
	}
	return n
}

// Equal 与 dst 进行比较
//
//goland:noinspection ALL
func (b Bytes) Equal(dst Bytes) bool {
	if len(b) != len(dst) {
		return false
	}
	return bytes.Equal(b.Remake(), dst.Remake())
}

// CRC16 使用 Bytes 创建用于 Modbus/TCP 协议 2 个字节的 CRC 校验码(CRC16)
// 具体应用时需要使用 BigEndian (大端模式) 或 LittleEndian 转换
//
//goland:noinspection ALL
func (b Bytes) CRC16() uint16 {
	var crc uint16 = 0xFFFF
	for _, n := range b {
		crc ^= uint16(n)
		for i := 0; i < 8; i++ {
			if crc&1 != 0 {
				crc >>= 1
				crc ^= 0xA001
			} else {
				crc >>= 1
			}
		}
	}
	return crc
}

// Hex 返回不包含空格的 hex
//
//goland:noinspection ALL
func (b Bytes) Hex() string {
	if len(b) <= 0 {
		return ""
	}
	return hex.EncodeToString(b)
}

// HexTo 返回包含空格的 hex
//
//goland:noinspection ALL
func (b Bytes) HexTo() string {
	if len(b) <= 0 {
		return ""
	}
	src := b.Hex()
	dst := strings.Builder{}
	for i := 0; i < len(src); i++ {
		dst.WriteByte(src[i])
		if i%2 == 1 && len(src)-1 != i {
			dst.WriteByte(32)
		}
	}
	return dst.String()
}

//goland:noinspection ALL
func (b Bytes) Bytes() []byte {
	return b
}

//goland:noinspection ALL
func (b Bytes) String() string {
	return string(b)
}

//goland:noinspection ALL
func (b Bytes) ToString() String {
	return String(b)
}

//goland:noinspection ALL
func (b *Bytes) UnmarshalXMLAttr(attr xml.Attr) error {
	body := String(attr.Value).Hex()
	*b = make(Bytes, len(body))
	copy(*b, body)
	return nil
}

//goland:noinspection ALL
func (b Bytes) Len() int {
	return len(b)
}

// ByteReadHelper TODO 全部改成使用 Pos 和 at 的方式解析. ByteReadHelper 将作为底层操作. 修改 modbus 包, 修改为引用此结构
type ByteReadHelper struct {
	registerSize int // 支持 2 或 1 两种处理模式
	start        int // 从第几个地址开始读取
	body         []byte
}

func (b *ByteReadHelper) SetBuffer(buff []byte) {
	b.body = make([]byte, len(buff))
	copy(b.body, buff)
}

func (b *ByteReadHelper) SetBlockSize(n int) {
	switch n {
	case 1, 2, 3, 4:
		b.registerSize = n
	default:
		panic("gnet.ByteReadHelper: SetBlockSize expecting 1-4")
	}
}

func (b *ByteReadHelper) SetStartRegister(n int) {
	b.start = n
}

func (b *ByteReadHelper) Len() int {
	return len(b.body)
}

func (b *ByteReadHelper) IsNonStandard(data []byte) bool {
	if len(data) == 1 {
		return false // binary.Read 可以处理 1 个字节
	}
	return len(data)%2 != 0
}

func (b *ByteReadHelper) patchTo(order binary.ByteOrder, data []byte, value any) ([]byte, error) {
	switch len(data) {
	case 3:
		switch value.(type) {
		case *int32, *uint32:
			switch order {
			case BigEndian, binary.BigEndian:
				return append(append([]byte{0}, data...)), nil
			case LittleEndian, binary.LittleEndian:
				return append(data, 0), nil
			}
		}
	}
	return nil, errors.New("gnet.ByteReadHelper: GetValueCustom expecting int64 or uint64")
}

func (b *ByteReadHelper) GetValueCustom(order binary.ByteOrder, pos, at int, value any) error {
	data := b.body[pos:at]
	if b.IsNonStandard(data) {
		var err error
		data, err = b.patchTo(order, data, value)
		if err != nil {
			return err
		}
	}
	buf := bytes.NewReader(data)
	return binary.Read(buf, order, value)
}

func (b *ByteReadHelper) GetValueAt(pos int, value any) error {
	return b.GetValueCustom(binary.BigEndian, pos, pos+b.registerSize, value)
}

func (b *ByteReadHelper) GetFloat32At(pos int) float32 {
	var value uint32
	if err := b.GetValueAt(pos, &value); err != nil {
		return 0.0
	}
	float := math.Float32frombits(value)
	return float
}

func (b *ByteReadHelper) GetFloat64At(pos int) float64 {
	var value uint64
	if err := b.GetValueAt(pos, &value); err != nil {
		return 0.0
	}
	float := math.Float64frombits(value)
	return float
}

func (b *ByteReadHelper) GetUint16(register int) (v uint16) {
	_ = b.GetValueAt(register*b.registerSize, &v)
	return
}

func (b *ByteReadHelper) GetUint32(register int) (v uint32) {
	_ = b.GetValueAt(register*b.registerSize, &v)
	return
}

func (b *ByteReadHelper) GetUint64(register int) (v uint64) {
	_ = b.GetValueAt(register*b.registerSize, &v)
	return
}

func (b *ByteReadHelper) GetInt16(register int) (v int16) {
	_ = b.GetValueAt(register*b.registerSize, &v)
	return
}

func (b *ByteReadHelper) GetInt32(register int) (v int32) {
	_ = b.GetValueAt(register*b.registerSize, &v)
	return
}

func (b *ByteReadHelper) GetInt64(register int) (v int64) {
	_ = b.GetValueAt(register*b.registerSize, &v)
	return
}

func (b *ByteReadHelper) GetFloat32(register int) (v float32) {
	v = b.GetFloat32At(register * b.registerSize)
	return
}

func (b *ByteReadHelper) GetFloat64(register int) (v float64) {
	v = b.GetFloat64At(register * b.registerSize)
	return
}

func (b *ByteReadHelper) GetBoolAt(pos, bitPos int) bool {
	return LittleEndian.BitSplit(b.body[pos : pos+b.registerSize]).Is1(bitPos)
}

func (b *ByteReadHelper) GetStringAt(pos, maxLen int) string {
	cache := make([]byte, maxLen)
	if err := b.GetValueAt(pos, cache); err != nil {
		return ""
	}
	return hex.EncodeToString(cache)
}

func (b *ByteReadHelper) GetBool(register, bitPos int) (v bool) {
	v = b.GetBoolAt(register*b.registerSize, bitPos)
	return
}

func (b *ByteReadHelper) GetRaw(register, quantity int) []byte {
	pos := register*b.registerSize - b.start*b.registerSize
	at := pos + quantity*b.registerSize
	return b.body[pos:at]
}

func (b *ByteReadHelper) GetIntCustom(order binary.ByteOrder, register, quantity int) int {
	pos := register*b.registerSize - b.start*b.registerSize
	at := pos + quantity*b.registerSize
	switch b.registerSize {
	case 1:
		switch quantity {
		case 1:
			var i int8
			_ = b.GetValueCustom(order, pos, at, &i)
			return int(i)
		case 2:
			var i int16
			_ = b.GetValueCustom(order, pos, at, &i)
			return int(i)
		case 3, 4:
			var i int32
			_ = b.GetValueCustom(order, pos, at, &i)
			return int(i)
		case 8:
			var i int64
			_ = b.GetValueCustom(order, pos, at, &i)
			return int(i)
		default:
			return 0
		}
	case 2:
		switch quantity {
		case 1:
			var i int16
			_ = b.GetValueCustom(order, pos, at, &i)
			return int(i)
		case 2:
			var i int32
			_ = b.GetValueCustom(order, pos, at, &i)
			return int(i)
		case 4:
			var i int64
			_ = b.GetValueCustom(order, pos, at, &i)
			return int(i)
		default:
			return 0
		}
	case 3:
		switch quantity {
		case 1:
			var i uint32
			_ = b.GetValueCustom(order, pos, at, &i)
			return int(i)
		case 2:
			var i int64
			_ = b.GetValueCustom(order, pos, at, &i)
			return int(i)
		default:
			return 0
		}
	case 4:
		switch quantity {
		case 1:
			var i int64
			_ = b.GetValueCustom(order, pos, at, &i)
			return int(i)
		default:
			return 0
		}
	default:
		return 0
	}
}

func (b *ByteReadHelper) GetUintCustom(order binary.ByteOrder, register, quantity int) int {
	pos := register*b.registerSize - b.start*b.registerSize
	at := pos + quantity*b.registerSize
	switch b.registerSize {
	case 1:
		switch quantity {
		case 1:
			var i uint8
			_ = b.GetValueCustom(order, pos, at, &i)
			return int(i)
		case 2:
			var i uint16
			_ = b.GetValueCustom(order, pos, at, &i)
			return int(i)
		case 3, 4:
			var i uint32
			_ = b.GetValueCustom(order, pos, at, &i)
			return int(i)
		case 5, 6, 7, 8:
			var i uint64
			_ = b.GetValueCustom(order, pos, at, &i)
			return int(i)
		default:
			return 0
		}
	case 2:
		switch quantity {
		case 1:
			var i uint16
			_ = b.GetValueCustom(order, pos, at, &i)
			return int(i)
		case 2:
			var i uint32
			_ = b.GetValueCustom(order, pos, at, &i)
			return int(i)
		case 3, 4:
			var i uint64
			_ = b.GetValueCustom(order, pos, at, &i)
			return int(i)
		default:
			return 0
		}
	case 3:
		switch quantity {
		case 1:
			var i uint32
			_ = b.GetValueCustom(order, pos, at, &i)
			return int(i)
		case 2:
			var i uint64
			_ = b.GetValueCustom(order, pos, at, &i)
			return int(i)
		default:
			return 0
		}
	case 4:
		switch quantity {
		case 1:
			var i uint64
			_ = b.GetValueCustom(order, pos, at, &i)
			return int(i)
		default:
			return 0
		}
	default:
		return 0
	}
}

func (b *ByteReadHelper) GetFloatCustom(order binary.ByteOrder, register, quantity int) float64 {
	pos := register*b.registerSize - b.start*b.registerSize
	at := pos + quantity*b.registerSize
	switch b.registerSize {
	case 1:
		switch quantity {
		case 4:
			var value uint32
			_ = b.GetValueCustom(order, pos, at, &value)
			float := math.Float32frombits(value)
			return float64(float)
		case 8:
			var value uint64
			_ = b.GetValueCustom(order, pos, at, &value)
			float := math.Float64frombits(value)
			return float
		default:
			return 0
		}
	case 2:
		switch quantity {
		case 2:
			var value uint32
			_ = b.GetValueCustom(order, pos, at, &value)
			float := math.Float32frombits(value)
			return float64(float)
		case 4:
			var value uint64
			_ = b.GetValueCustom(order, pos, at, &value)
			float := math.Float64frombits(value)
			return float
		default:
			return 0
		}
	case 3:
		switch quantity {
		case 1:
			var value uint32
			_ = b.GetValueCustom(order, pos, at, &value)
			float := math.Float32frombits(value)
			return float64(float)
		case 2:
			var value uint64
			_ = b.GetValueCustom(order, pos, at, &value)
			float := math.Float64frombits(value)
			return float
		default:
			return 0
		}
	case 4:
		switch quantity {
		case 1:
			var value uint64
			_ = b.GetValueCustom(order, pos, at, &value)
			float := math.Float64frombits(value)
			return float
		default:
			return 0
		}
	default:
		return 0
	}
}

type ByteWriteHelper struct {
	registerSize int
}

func (h *ByteWriteHelper) SetRegisterSize(n int) {
	if n != 2 && n != 1 {
		panic("gnet.ByteWriteHelper: SetBlockSize expecting 2 or 1")
	}
	h.registerSize = n
}

func (h *ByteWriteHelper) SetValueCustom(order binary.ByteOrder, buff []byte, pos int, data any) error {
	buf := new(bytes.Buffer)
	if err := binary.Write(buf, order, data); err != nil {
		return err
	}
	copy(buff[pos:], buf.Bytes())
	return nil
}

func (h *ByteWriteHelper) SetValueAt(buff []byte, pos int, data any) error {
	return h.SetValueCustom(binary.BigEndian, buff, pos, data)
}

func (h *ByteWriteHelper) SetFloat32At(buf []byte, pos int, value float32) error {
	return h.SetValueAt(buf, pos, math.Float32bits(value))
}

func (h *ByteWriteHelper) SetFloat64At(buf []byte, pos int, value float64) error {
	return h.SetValueAt(buf, pos, math.Float64bits(value))
}

func (h *ByteWriteHelper) SetStringAt(buff []byte, pos, maxLen int, data string) error {
	s, err := hex.DecodeString(data)
	if err != nil {
		return err
	}
	copy(buff[pos:maxLen], s)
	return nil
}

func (h *ByteWriteHelper) SetBitAt(buff []byte, pos, bitPos, bit int) {
	value := binary.BigEndian.Uint16(buff[pos : pos+h.registerSize])
	if bit == 0 {
		ClearBit(&value, uint(bitPos))
	} else {
		SetBit(&value, uint(bitPos))
	}
	BigEndian.PutUint16(buff[pos:pos+h.registerSize], value)
}

func (h *ByteWriteHelper) SetBoolAt(buff []byte, pos, bitPos int, b bool) {
	if b {
		h.SetBitAt(buff, pos, bitPos, 1)
	} else {
		h.SetBitAt(buff, pos, bitPos, 0)
	}
}
