package protocol

import (
	"bytes"
	"encoding/binary"
	"errors"
)

// PacketType represents the type of a TeamSpeak 3 packet.
type PacketType uint8

const (
	PacketTypeVoice        PacketType = 0x00
	PacketTypeVoiceWhisper PacketType = 0x01
	PacketTypeCommand      PacketType = 0x02
	PacketTypeCommandLow   PacketType = 0x03
	PacketTypePing         PacketType = 0x04
	PacketTypePong         PacketType = 0x05
	PacketTypeAck          PacketType = 0x06
	PacketTypeAckLow       PacketType = 0x07
	PacketTypeInit1        PacketType = 0x08
)

// Packet Flags
const (
	PacketFlagUnencrypted = 0x80
	PacketFlagCompressed  = 0x40
	PacketFlagNewProtocol = 0x20
	PacketFlagFragmented  = 0x10
)

const (
	HeaderSizeClientToServer = 13 // 8 MAC + 2 PID + 2 CID + 1 PT
	HeaderSizeServerToClient = 11 // 8 MAC + 2 PID + 1 PT
	MACSize                  = 8
)

// PacketHeader represents the common header fields.
type PacketHeader struct {
	MAC      [8]byte
	PacketID uint16
	ClientID uint16 // Only present in Client -> Server
	Type     uint8  // Contains PacketType and Flags
}

// Packet represents a parsed TeamSpeak 3 packet.
type Packet struct {
	Header PacketHeader
	Data   []byte
}

func (h *PacketHeader) FlagUnencrypted() bool  { return h.Type&PacketFlagUnencrypted != 0 }
func (h *PacketHeader) FlagCompressed() bool   { return h.Type&PacketFlagCompressed != 0 }
func (h *PacketHeader) FlagNewProtocol() bool  { return h.Type&PacketFlagNewProtocol != 0 }
func (h *PacketHeader) FlagFragmented() bool   { return h.Type&PacketFlagFragmented != 0 }
func (h *PacketHeader) PacketType() PacketType { return PacketType(h.Type & 0x0F) }

// SetType sets the packet type and preserves flags
func (h *PacketHeader) SetType(pt PacketType) {
	flags := h.Type & 0xF0
	h.Type = flags | uint8(pt&0x0F)
}

// Encode writes the packet to a byte slice.
func (p *Packet) Encode(isClientToServer bool) ([]byte, error) {
	buf := new(bytes.Buffer)

	// MAC
	buf.Write(p.Header.MAC[:])

	// Packet ID
	if err := binary.Write(buf, binary.BigEndian, p.Header.PacketID); err != nil {
		return nil, err
	}

	// Client ID (only C->S)
	if isClientToServer {
		if err := binary.Write(buf, binary.BigEndian, p.Header.ClientID); err != nil {
			return nil, err
		}
	}

	// Packet Type
	if err := binary.Write(buf, binary.BigEndian, p.Header.Type); err != nil {
		return nil, err
	}

	// Data
	buf.Write(p.Data)

	return buf.Bytes(), nil
}

// Decode parses a packet from a byte slice.
func Decode(data []byte, isClientToServer bool) (*Packet, error) {
	reader := bytes.NewReader(data)
	p := &Packet{}

	// Check minimum size
	minSize := HeaderSizeServerToClient
	if isClientToServer {
		minSize = HeaderSizeClientToServer
	}
	if len(data) < minSize {
		return nil, errors.New("packet too short")
	}

	// MAC
	if _, err := reader.Read(p.Header.MAC[:]); err != nil {
		return nil, err
	}

	// Packet ID
	if err := binary.Read(reader, binary.BigEndian, &p.Header.PacketID); err != nil {
		return nil, err
	}

	// Client ID (only C->S)
	if isClientToServer {
		if err := binary.Read(reader, binary.BigEndian, &p.Header.ClientID); err != nil {
			return nil, err
		}
	}

	// Packet Type
	if err := binary.Read(reader, binary.BigEndian, &p.Header.Type); err != nil {
		return nil, err
	}

	// Data
	p.Data = make([]byte, reader.Len())
	if _, err := reader.Read(p.Data); err != nil {
		return nil, err
	}

	return p, nil
}

func NewPacket(pt PacketType, data []byte) *Packet {
	return &Packet{
		Header: PacketHeader{
			Type: uint8(pt),
			MAC:  [8]byte{}, // Default empty MAC
		},
		Data: data,
	}
}
