package modbus

import (
	"bytes"
	"encoding/binary"
	"encoding/hex"
	"math"

	"git.simanc.com/software/golib/v4/gnet"
)

type Helper struct{}

func (h Helper) 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 Helper) SetValueAt(buff []byte, pos int, data any) error {
	return h.SetValueCustom(binary.BigEndian, buff, pos, data)
}

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

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

func (h Helper) 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 Helper) SetBitAt(buff []byte, pos, bitPos, bit int) {
	value := binary.BigEndian.Uint16(buff[pos : pos+2])
	if bit == 0 {
		gnet.ClearBit(&value, uint(bitPos))
	} else {
		gnet.SetBit(&value, uint(bitPos))
	}
	binary.BigEndian.PutUint16(buff[pos:pos+2], value)
}

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

func (h Helper) GetValueCustom(order binary.ByteOrder, buff []byte, pos, at int, value any) error {
	buf := bytes.NewReader(buff[pos:at])
	return binary.Read(buf, order, value)
}

func (h Helper) GetValueAt(buf []byte, pos int, value any) error {
	return h.GetValueCustom(binary.BigEndian, buf, pos, pos+RegisterSize, value)
}

func (h Helper) GetFloat32At(buf []byte, pos int) float32 {
	var value uint32
	if err := h.GetValueAt(buf, pos, &value); err != nil {
		return 0.0
	}
	float := math.Float32frombits(value)
	return float
}

func (h Helper) GetFloat64At(buf []byte, pos int) float64 {
	var value uint64
	if err := h.GetValueAt(buf, pos, &value); err != nil {
		return 0.0
	}
	float := math.Float64frombits(value)
	return float
}

func (h Helper) GetBoolAt(buf []byte, pos, bitPos int) bool {
	return gnet.LittleEndian.BitSplit(buf[pos : pos+2]).Is1(bitPos)
}

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

var bh = &Helper{}

const (
	RegisterSize = 2
)

type ReadHelper []byte

func (b ReadHelper) Len() int {
	return len(b)
}

func (b ReadHelper) GetUint16(register int) (v uint16) {
	_ = bh.GetValueAt(b, register*RegisterSize, &v)
	return
}

func (b ReadHelper) GetUint32(register int) (v uint32) {
	_ = bh.GetValueAt(b, register*RegisterSize, &v)
	return
}

func (b ReadHelper) GetUint64(register int) (v uint64) {
	_ = bh.GetValueAt(b, register*RegisterSize, &v)
	return
}

func (b ReadHelper) GetInt16(register int) (v int16) {
	_ = bh.GetValueAt(b, register*RegisterSize, &v)
	return
}

func (b ReadHelper) GetInt32(register int) (v int32) {
	_ = bh.GetValueAt(b, register*RegisterSize, &v)
	return
}

func (b ReadHelper) GetInt64(register int) (v int64) {
	_ = bh.GetValueAt(b, register*RegisterSize, &v)
	return
}

func (b ReadHelper) GetFloat32(register int) (v float32) {
	v = (&Helper{}).GetFloat32At(b, register*RegisterSize)
	return
}

func (b ReadHelper) GetFloat64(register int) (v float64) {
	v = (&Helper{}).GetFloat64At(b, register*RegisterSize)
	return
}

func (b ReadHelper) GetBool(register, bitPos int) (v bool) {
	v = (&Helper{}).GetBoolAt(b, register*RegisterSize, bitPos)
	return
}

func (b ReadHelper) GetRaw(register, quantity int) []byte {
	pos := register * RegisterSize
	at := pos + quantity*RegisterSize
	return b[pos:at]
}

func (b ReadHelper) GetIntCustom(order binary.ByteOrder, register, quantity int) int {
	pos := register * RegisterSize
	at := pos + quantity*RegisterSize
	switch quantity {
	case 1:
		var i int16
		_ = bh.GetValueCustom(order, b, pos, at, &i)
		return int(i)
	case 2:
		var i int32
		_ = bh.GetValueCustom(order, b, pos, at, &i)
		return int(i)
	case 4:
		var i int64
		_ = bh.GetValueCustom(order, b, pos, at, &i)
		return int(i)
	default:
		return 0
	}
}

func (b ReadHelper) GetUintCustom(order binary.ByteOrder, register, quantity int) int {
	pos := register * RegisterSize
	at := pos + quantity*RegisterSize
	switch quantity {
	case 1:
		var i uint16
		_ = bh.GetValueCustom(order, b, pos, at, &i)
		return int(i)
	case 2:
		var i uint16
		_ = bh.GetValueCustom(order, b, pos, at, &i)
		return int(i)
	case 4:
		var i uint64
		_ = bh.GetValueCustom(order, b, pos, at, &i)
		return int(i)
	default:
		return 0
	}
}

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