package ym485

import (
	"encoding/hex"
	"errors"
	"fmt"
	"math"
	"strconv"
	"strings"

	"github.com/valyala/bytebufferpool"
)

var ErrorInvalidChecksum = errors.New("数据校验错误")
var ErrorInvalidLenght = errors.New("数据长度不够")
var ErrorEncode = errors.New("编码错误")

type DataPack struct {
	Start   byte   //帧起始符，恒为0x68，1字节
	Addr    string //地址域，6字节,整数，大端传输
	Start2  byte   //帧起始符，恒为0x68，1字节
	Ctl     uint8  //控制码，1字节
	DataLen uint8  //数据域长度，1字节
	Data    []byte //数据域
	Cs      uint8  //Start到Cs之前的所有字节模256
	End     byte   //结束符号，恒为0x16，1字节
}

func (o *DataPack) EnCode(Addr string, Ctl uint8, Data []byte) (*bytebufferpool.ByteBuffer, error) {
	//初始化成员
	o.Start = 0x68
	o.Addr = Addr
	o.Start2 = 0x68
	o.Ctl = Ctl
	o.DataLen = uint8(len(Data))
	o.Data = Data
	o.End = 0x16
	//addr
	tmpAddr := EncodeAddr(o.Addr)
	if tmpAddr == nil {
		return nil, ErrorEncode
	}
	//序列化
	buf := bytebufferpool.Get()
	buf.WriteByte(o.Start)
	buf.Write(tmpAddr)
	buf.WriteByte(o.Start2)
	buf.WriteByte(o.Ctl)
	buf.WriteByte(o.DataLen)

	if o.DataLen > 0 {
		TmpData := make([]byte, o.DataLen)
		for i, b := range o.Data {
			TmpData[i] = b + 0x33
		}
		buf.Write(TmpData)
	}

	//计算校验码
	o.Cs = CheckSum(buf.Bytes())
	buf.WriteByte(o.Cs)
	buf.WriteByte(o.End)
	return buf, nil
}

func CheckSum(data []byte) uint8 {
	var sum uint64
	for _, c := range data {
		sum += uint64(c)
	}
	return uint8(sum % 256)
}

func VerifySum(data []byte) bool {
	if len(data) >= 12 {
		sum := data[len(data)-2] //倒数第二个字节
		sum_ := CheckSum(data[0 : len(data)-2])
		if sum == sum_ {
			return true
		}
	}
	return false
}

func (o *DataPack) DeCode(data []byte) error {
	if len(data) >= 12 {
		o.Start = data[0]
		o.Addr = DecodeAddr(data[1:7])
		o.Start2 = data[7]
		o.Ctl = data[8]
		o.DataLen = data[9]
		if o.DataLen > 0 {
			if o.DataLen > uint8(len(data)-12) {
				return ErrorInvalidLenght
			}
			o.Data = make([]byte, o.DataLen)
			for i, b := range data[10 : 10+o.DataLen] {
				o.Data[i] = b - 0x33
			}
		}
		o.Cs = data[10+o.DataLen]
		o.End = data[10+o.DataLen+1]
		cs := CheckSum(data[0 : 10+o.DataLen])
		if o.Cs != cs {
			return ErrorInvalidChecksum
		}
		return nil
	}
	return ErrorInvalidLenght
}

func EncodeAddr(Addr string) []byte {
	len0 := len(Addr)
	repeat := 12 - len0 //地址长度最多12个字符
	if repeat > 0 {
		Addr = strings.Repeat("0", repeat) + Addr
	}
	buf, err := ASCII2Lbcd([]byte(Addr))
	if err != nil {
		return nil
	}
	buf = Rotate(buf)
	return buf
}

func DecodeAddr(addr []byte) string {
	if len(addr) == 0 {
		return ""
	}
	buf := Rotate(addr)
	buf = Lbcd2ASCII(buf)
	return strings.TrimLeft(string(buf), "0")
}

// DecodeFloat64 prec为小数部分的长度
func DecodeFloat64(dat []byte, prec int) (float64, error) {
	if len(dat) == 0 {
		return 0.0, errors.New("参数为空")
	}
	data := Rotate(dat)
	ss := []byte(hex.EncodeToString(data))
	if prec == 0 {
		return strconv.ParseFloat(string(ss), 64)
	}
	fss := make([]byte, 0, len(ss)+1)
	fss = append(fss, ss[0:len(ss)-prec]...)
	fss = append(fss, '.')
	fss = append(fss, ss[len(ss)-prec:]...)
	return strconv.ParseFloat(string(fss), 64)
}

// EncodeFloat64 prec0为整数部分的长度
//prec1为小数部分的长度
func EncodeFloat64(fval float64, prec0, prec1 int) ([]byte, error) {
	if (prec0+prec1)%2 != 0 {
		return nil, errors.New("整数和小数总长度非双数")
	}
	ui := uint(fval * math.Pow10(prec1))
	ss := fmt.Sprintf("%d", ui)
	repeat := prec0 + prec1 - len(ss)
	ss = strings.Repeat("0", repeat) + ss
	buf, err := ASCII2Lbcd([]byte(ss))
	if err != nil {
		return nil, err
	}
	buf = Rotate(buf)
	return buf, nil
}

func ReleaseByteBuffer(p *bytebufferpool.ByteBuffer) {
	if p != nil {
		bytebufferpool.Put(p)
	}
}
