package types

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

// Packet represents a basic EQ2 packet interface
type Packet interface {
	Serialize(dest []byte) uint32
	Size() uint32
}

// PacketWriter helps with writing packet data
type PacketWriter struct {
	buffer *bytes.Buffer
	offset uint32
}

// NewPacketWriter creates a new packet writer
func NewPacketWriter() *PacketWriter {
	return &PacketWriter{
		buffer: new(bytes.Buffer),
		offset: 0,
	}
}

// WriteUint8 writes a uint8 value
func (pw *PacketWriter) WriteUint8(v uint8) error {
	return binary.Write(pw.buffer, binary.LittleEndian, v)
}

// WriteUint16 writes a uint16 value
func (pw *PacketWriter) WriteUint16(v uint16) error {
	return binary.Write(pw.buffer, binary.LittleEndian, v)
}

// WriteUint32 writes a uint32 value
func (pw *PacketWriter) WriteUint32(v uint32) error {
	return binary.Write(pw.buffer, binary.LittleEndian, v)
}

// WriteUint64 writes a uint64 value
func (pw *PacketWriter) WriteUint64(v uint64) error {
	return binary.Write(pw.buffer, binary.LittleEndian, v)
}

// WriteString8 writes a string with 8-bit length prefix
func (pw *PacketWriter) WriteString8(s string) error {
	str := EQ2String8(s)
	return str.Serialize(pw.buffer)
}

// WriteString16 writes a string with 16-bit length prefix
func (pw *PacketWriter) WriteString16(s string) error {
	str := EQ2String16(s)
	return str.Serialize(pw.buffer)
}

// WriteString32 writes a string with 32-bit length prefix
func (pw *PacketWriter) WriteString32(s string) error {
	str := EQ2String32(s)
	return str.Serialize(pw.buffer)
}

// WriteColor writes a color value
func (pw *PacketWriter) WriteColor(c Color) error {
	return c.Serialize(pw.buffer)
}

// WriteFloat32 writes a float32 value
func (pw *PacketWriter) WriteFloat32(v float32) error {
	f := Float32(v)
	return f.Serialize(pw.buffer)
}

// WriteFloat64 writes a float64 value
func (pw *PacketWriter) WriteFloat64(v float64) error {
	f := Float64(v)
	return f.Serialize(pw.buffer)
}

// Bytes returns the written bytes
func (pw *PacketWriter) Bytes() []byte {
	return pw.buffer.Bytes()
}

// Size returns the current size of written data
func (pw *PacketWriter) Size() uint32 {
	return uint32(pw.buffer.Len())
}

// PacketReader helps with reading packet data
type PacketReader struct {
	reader io.Reader
	offset uint32
}

// NewPacketReader creates a new packet reader
func NewPacketReader(data []byte) *PacketReader {
	return &PacketReader{
		reader: bytes.NewReader(data),
		offset: 0,
	}
}

// NewPacketReaderFromReader creates a packet reader from an io.Reader
func NewPacketReaderFromReader(r io.Reader) *PacketReader {
	return &PacketReader{
		reader: r,
		offset: 0,
	}
}

// ReadUint8 reads a uint8 value
func (pr *PacketReader) ReadUint8() (uint8, error) {
	var v uint8
	err := binary.Read(pr.reader, binary.LittleEndian, &v)
	if err == nil {
		pr.offset++
	}
	return v, err
}

// ReadUint16 reads a uint16 value
func (pr *PacketReader) ReadUint16() (uint16, error) {
	var v uint16
	err := binary.Read(pr.reader, binary.LittleEndian, &v)
	if err == nil {
		pr.offset += 2
	}
	return v, err
}

// ReadUint32 reads a uint32 value
func (pr *PacketReader) ReadUint32() (uint32, error) {
	var v uint32
	err := binary.Read(pr.reader, binary.LittleEndian, &v)
	if err == nil {
		pr.offset += 4
	}
	return v, err
}

// ReadUint64 reads a uint64 value
func (pr *PacketReader) ReadUint64() (uint64, error) {
	var v uint64
	err := binary.Read(pr.reader, binary.LittleEndian, &v)
	if err == nil {
		pr.offset += 8
	}
	return v, err
}

// ReadString8 reads a string with 8-bit length prefix
func (pr *PacketReader) ReadString8() (string, error) {
	var s EQ2String8
	err := s.Deserialize(pr.reader)
	if err == nil {
		pr.offset += s.Size()
	}
	return string(s), err
}

// ReadString16 reads a string with 16-bit length prefix
func (pr *PacketReader) ReadString16() (string, error) {
	var s EQ2String16
	err := s.Deserialize(pr.reader)
	if err == nil {
		pr.offset += s.Size()
	}
	return string(s), err
}

// ReadString32 reads a string with 32-bit length prefix
func (pr *PacketReader) ReadString32() (string, error) {
	var s EQ2String32
	err := s.Deserialize(pr.reader)
	if err == nil {
		pr.offset += s.Size()
	}
	return string(s), err
}

// ReadColor reads a color value
func (pr *PacketReader) ReadColor() (Color, error) {
	var c Color
	err := c.Deserialize(pr.reader)
	if err == nil {
		pr.offset += 4
	}
	return c, err
}

// ReadFloat32 reads a float32 value
func (pr *PacketReader) ReadFloat32() (float32, error) {
	var f Float32
	err := f.Deserialize(pr.reader)
	if err == nil {
		pr.offset += 4
	}
	return float32(f), err
}

// ReadFloat64 reads a float64 value
func (pr *PacketReader) ReadFloat64() (float64, error) {
	var f Float64
	err := f.Deserialize(pr.reader)
	if err == nil {
		pr.offset += 8
	}
	return float64(f), err
}

// Offset returns the current read offset
func (pr *PacketReader) Offset() uint32 {
	return pr.offset
}