package pomeloPacket

import (
	"bytes"

	cerr "github.com/mhaya/error"
)

type (
	Type = byte // packet type
)

const (
	None         Type = 0x00 // None
	Handshake    Type = 0x01 // Handshake represents a handshake: request(client) <====> handshake response(server)
	HandshakeAck Type = 0x02 // HandshakeAck represents a handshake ack from client to server
	Heartbeat    Type = 0x03 // Heartbeat represents a heartbeat
	Data         Type = 0x04 // settings represents a common data packet
	Kick         Type = 0x05 // Kick represents a kick off packet
)

var (
	HeadLength    = 4       // 4 bytes
	MaxPacketSize = 1 << 24 // 16mb

	packetTypes = map[Type]string{
		None:         "None",
		Handshake:    "Handshake",
		HandshakeAck: "HandshakeAck",
		Heartbeat:    "Heartbeat",
		Data:         "Data",
		Kick:         "Kick",
	}
)

func TypeName(t Type) string {
	return packetTypes[t]
}

func InvalidType(t Type) bool {
	return t < Handshake || t > Kick
}

// ParseHeader parses a packet header and returns its dataLen and packetType or an error
func ParseHeader(header []byte) (int, error) {
	if len(header) != HeadLength {
		return 0, cerr.PacketInvalidHeader
	}

	typ := header[0]
	if InvalidType(typ) {
		return 0, cerr.PacketWrongType
	}

	size := BytesToInt(header[1:])

	if size > MaxPacketSize {
		return 0, cerr.PacketSizeExceed
	}

	return size, nil
}

// BytesToInt Decode packet data length byte to int(Big end)
func BytesToInt(b []byte) int {
	result := 0
	for _, v := range b {
		result = result<<8 + int(v)
	}
	return result
}

// IntToBytes Encode packet data length to bytes(Big end)
func IntToBytes(n int) []byte {
	buf := make([]byte, 3)
	buf[0] = byte((n >> 16) & 0xFF)
	buf[1] = byte((n >> 8) & 0xFF)
	buf[2] = byte(n & 0xFF)
	return buf
}

func forward(buf *bytes.Buffer) (int, Type, error) {
	header := buf.Next(HeadLength)

	typ := header[0]
	if InvalidType(typ) {
		return 0, None, cerr.PacketSizeExceed
	}

	// get 2,3,4 byte
	size := BytesToInt(header[1:])

	// packet length limitation
	if size > MaxPacketSize {
		return 0, None, cerr.PacketSizeExceed
	}

	return size, typ, nil
}
