package frame

import (
	"primesys_producer/src/logger"
	. "primesys_producer/src/types"
)

const (
	Incomplete ExtractStatus = iota
	Complete
	Skip
)

// smallest valid content: protocol(1) + serial(2) + crc(2)
const minContentLen = 5

type Frame struct {
	Raw []byte
}

// Extracting Frame from bytes
func ExtractFrame(buf []byte) (frame Frame, rest []byte, status ExtractStatus) {
	if len(buf) < 2 {
		logger.Warn("length of buf is less than 2 %d", len(buf))
		return frame, buf, Incomplete
	}

	switch {
	case buf[0] == StartLongByte && buf[1] == StartLongByte:
		if len(buf) < PacketLong {
			return Frame{}, buf, Incomplete
		}
		// read length from the 4
		length := int(buf[2])<<8 | int(buf[3])
		if length < minContentLen {
			return Frame{}, buf[1:], Skip
		}
		total := PacketLong + length + 2
		if len(buf) < total {
			return Frame{}, buf, Incomplete
		}
		return Frame{Raw: buf[:total]}, buf[total:], Complete
	case buf[0] == StartShortByte && buf[1] == StartShortByte:
		if len(buf) < PacketShort {
			return Frame{}, buf, Incomplete
		}
		length := int(buf[2])
		if length < minContentLen {
			return Frame{}, buf[1:], Skip
		}
		total := PacketShort + length + 2
		if len(buf) < total {
			logger.Debug("total frame length becuase %d", total)
			return Frame{}, buf, Incomplete
		}
		return Frame{Raw: buf[:total]}, buf[total:], Complete
	default:
		return Frame{}, buf[1:], Skip
	}
}

func (f Frame) PacketStartLength() int8 {
	if len(f.Raw) < 2 {
		return 0
	}

	if f.Raw[0] == StartLongByte && f.Raw[1] == StartLongByte {
		return int8(PacketLong)
	} else if f.Raw[0] == StartShortByte && f.Raw[1] == StartShortByte {
		return int8(PacketShort)
	} else {
		return 0
	}
}

// Protocol Number frame
func (f Frame) ProtocolNumber() ProtocolNumber {
	return ProtocolNumber(f.Raw[f.PacketStartLength()])
}

// body frame
func (f Frame) Body() []byte {
	return f.Raw[f.PacketStartLength() : len(f.Raw)-2]
}

// Serial Number for ack
func (f Frame) SerialNumber() []byte {
	body := f.Body()
	return body[len(body)-4 : len(body)-2]
}

// CRC byte
func (f Frame) CRC() (hi byte, low byte) {
	body := f.Body()
	return body[len(body)-2], body[len(body)-1]
}
