// EQ2Emulator: Everquest II Server Emulator
// Copyright (C) 2007 EQ2EMulator Development Team
// Licensed under GPL v3 - see <http://www.gnu.org/licenses/>
#ifndef _EQPACKET_H
#define _EQPACKET_H

#include "types.h"
#include <stdio.h>
#include <string.h>
#include <memory>

#ifdef WIN32
	#include <time.h>
	#include <WinSock2.h>
#else
	#include <sys/time.h>
	#include <netinet/in.h>
#endif

#include "emu_opcodes.h"
#include "op_codes.h"
#include "packet_dump.h"

// Forward declarations
class OpcodeManager;
class EQStream;

/**
 * Base class for all EverQuest packet types.
 * Provides common functionality for packet management, opcode handling,
 * and network information tracking.
 */
class EQPacket {
	friend class EQStream;

public:
	// Packet data
	unsigned char* pBuffer;		// Raw packet data buffer
	uint32 size;				// Size of packet data
	
	// Network information
	uint32 src_ip, dst_ip;		// Source and destination IP addresses
	uint16 src_port, dst_port;	// Source and destination ports
	uint32 priority;			// Packet priority for processing
	timeval timestamp;			// Packet timestamp
	int16 version;				// Protocol version
	
	~EQPacket();
	
	// Debug and display methods
	void DumpRawHeader(uint16 seq = 0xffff, FILE* to = stdout) const;
	void DumpRawHeaderNoTime(uint16 seq = 0xffff, FILE* to = stdout) const;
	void DumpRaw(FILE* to = stdout) const;
	const char* GetOpcodeName();
	
	// Packet information setters
	void setVersion(int16 new_version) { version = new_version; }
	void setSrcInfo(uint32 sip, uint16 sport) { src_ip = sip; src_port = sport; }
	void setDstInfo(uint32 dip, uint16 dport) { dst_ip = dip; dst_port = dport; }
	void setTimeInfo(uint32 ts_sec, uint32 ts_usec) { 
		timestamp.tv_sec = ts_sec; 
		timestamp.tv_usec = ts_usec; 
	}
	
	// Copy network and timing info from another packet
	void copyInfo(const EQPacket* p) { 
		src_ip = p->src_ip;
		src_port = p->src_port;
		dst_ip = p->dst_ip;
		dst_port = p->dst_port;
		timestamp.tv_sec = p->timestamp.tv_sec;
		timestamp.tv_usec = p->timestamp.tv_usec;
	}
	
	// Get total packet size including opcode
	uint32 Size() const { return size + 2; }
	
	// Get raw opcode value
	uint16 GetRawOpcode() const { return opcode; }
	
	// Comparison operator for timestamp-based sorting
	bool operator<(const EQPacket& rhs) const {
		return (timestamp.tv_sec < rhs.timestamp.tv_sec || 
				(timestamp.tv_sec == rhs.timestamp.tv_sec && 
				 timestamp.tv_usec < rhs.timestamp.tv_usec));
	}
	
	// Set the protocol opcode
	void SetProtocolOpcode(int16 new_opcode) {
		opcode = new_opcode;
	}
	
protected:
	uint16 opcode; // Packet opcode
	
	// Constructors (protected to enforce proper inheritance)
	EQPacket(uint16 op, const unsigned char* buf, uint32 len);
	EQPacket(const EQPacket& p) { version = 0; }
	EQPacket() { 
		opcode = 0;
		pBuffer = nullptr;
		size = 0;
		version = 0;
		setTimeInfo(0, 0);
	}
};

// Forward declaration
class EQApplicationPacket;

/**
 * Protocol-level packet class for EverQuest network communication.
 * Handles low-level protocol features like compression, encryption,
 * sequencing, and packet combining.
 */
class EQProtocolPacket : public EQPacket {
public:
	// Constructor with opcode and buffer
	EQProtocolPacket(uint16 op, const unsigned char* buf, uint32 len) 
		: EQPacket(op, buf, len) { 
		eq2_compressed = false; 
		packet_prepared = false;
		packet_encrypted = false;
		sequence = 0;
		sent_time = 0;
		attempt_count = 0;
		acked = false;
	}
	
	// Constructor from raw buffer with optional opcode override
	EQProtocolPacket(const unsigned char* buf, uint32 len, int in_opcode = -1);
	
	// Packet manipulation methods
	bool combine(const EQProtocolPacket* rhs);
	uint32 serialize(unsigned char* dest, int8 offset = 0) const;
	
	// Static utility methods for packet processing
	static bool ValidateCRC(const unsigned char* buffer, int length, uint32 Key);
	static uint32 Decompress(const unsigned char* buffer, uint32 length, 
							 unsigned char* newbuf, uint32 newbufsize);
	static uint32 Compress(const unsigned char* buffer, uint32 length, 
						   unsigned char* newbuf, uint32 newbufsize);
	static void ChatDecode(unsigned char* buffer, int size, int DecodeKey);
	static void ChatEncode(unsigned char* buffer, int size, int EncodeKey);
	static bool IsProtocolPacket(const unsigned char* in_buff, uint32_t len, bool bTrimCRC);
	
	// Create a copy of this packet
	EQProtocolPacket* Copy() { 
		auto new_packet = new EQProtocolPacket(opcode, pBuffer, size);
		new_packet->eq2_compressed = this->eq2_compressed;
		new_packet->packet_prepared = this->packet_prepared;
		new_packet->packet_encrypted = this->packet_encrypted;
		return new_packet; 
	}
	
	// Convert to application-level packet
	EQApplicationPacket* MakeApplicationPacket(uint8 opcode_size = 0) const;
	
	// Protocol packet state flags
	bool eq2_compressed;	// Packet is compressed
	bool packet_prepared;	// Packet has been prepared for sending
	bool packet_encrypted;	// Packet is encrypted
	bool acked;				// Packet has been acknowledged
	
	// Reliability and sequencing
	int32 sent_time;	// Timestamp when packet was sent
	int8 attempt_count;	// Number of send attempts
	int32 sequence;		// Sequence number for ordering

private:
	// Prevent copy construction
	EQProtocolPacket(const EQProtocolPacket& p) = delete;
	EQProtocolPacket& operator=(const EQProtocolPacket& p) = delete;
};

/**
 * EverQuest 2 specific packet class.
 * Handles EQ2-specific packet operations like application combining
 * and login opcode management.
 */
class EQ2Packet : public EQProtocolPacket {
public:
	// Constructor with EQ2 login opcode
	EQ2Packet(EmuOpcode in_login_op, const unsigned char* buf, uint32 len) 
		: EQProtocolPacket(OP_Packet, buf, len) { 
		login_op = in_login_op;
		eq2_compressed = false;
		packet_prepared = false;
		packet_encrypted = false;
	}
	
	// EQ2-specific packet operations
	bool AppCombine(EQ2Packet* rhs);
	int8 PreparePacket(int16 MaxLen);
	const char* GetOpcodeName();
	
	// Create a copy of this EQ2 packet
	EQ2Packet* Copy() { 
		auto new_packet = new EQ2Packet(login_op, pBuffer, size); 
		new_packet->eq2_compressed = this->eq2_compressed;
		new_packet->packet_prepared = this->packet_prepared;
		new_packet->packet_encrypted = this->packet_encrypted;
		return new_packet;
	}
	
	EmuOpcode login_op; // EQ2 login/application opcode
};

/**
 * Application-level packet class for EverQuest.
 * Handles high-level game opcodes and application data.
 * This is the main packet type used by game logic.
 */
class EQApplicationPacket : public EQPacket {
	friend class EQProtocolPacket;
	friend class EQStream;

public:
	// Default constructor
	EQApplicationPacket() : EQPacket(0, nullptr, 0) { 
		emu_opcode = OP_Unknown; 
		app_opcode_size = default_opcode_size; 
	}
	
	// Constructor with opcode only
	EQApplicationPacket(EmuOpcode op) : EQPacket(0, nullptr, 0) { 
		SetOpcode(op); 
		app_opcode_size = default_opcode_size; 
	}
	
	// Constructor with opcode and size
	EQApplicationPacket(EmuOpcode op, uint32 len) : EQPacket(0, nullptr, len) { 
		SetOpcode(op); 
		app_opcode_size = default_opcode_size; 
	}
	
	// Constructor with opcode, buffer, and size
	EQApplicationPacket(EmuOpcode op, const unsigned char* buf, uint32 len) 
		: EQPacket(0, buf, len) { 
		SetOpcode(op); 
		app_opcode_size = default_opcode_size; 
	}
	
	// Packet operations
	bool combine(const EQApplicationPacket* rhs);
	uint32 serialize(unsigned char* dest) const;
	uint32 Size() const { return size + app_opcode_size; }
	
	// Create a deep copy of this packet
	EQApplicationPacket* Copy() const {
		auto it = std::make_unique<EQApplicationPacket>();
		try {
			if (size > 0) {
				it->pBuffer = new unsigned char[size];
				memcpy(it->pBuffer, pBuffer, size);
			}
			it->size = size;
			it->opcode = opcode;
			it->emu_opcode = emu_opcode;
			it->version = version;
			return it.release();
		}
		catch (const std::bad_alloc& ba) {
			// Memory allocation failed - return nullptr
			return nullptr;
		}
	}
	
	// Opcode management
	void SetOpcodeSize(uint8 s) { app_opcode_size = s; }
	void SetOpcode(EmuOpcode op);
	const EmuOpcode GetOpcodeConst() const;
	
	// Get opcode (const version)
	const EmuOpcode GetOpcode() const { return GetOpcodeConst(); }
	
	// Get opcode (caching version for performance)
	EmuOpcode GetOpcode() { 
		EmuOpcode r = GetOpcodeConst(); 
		emu_opcode = r; 
		return r; 
	}
	
	// Default opcode size for all application packets
	static uint8 default_opcode_size;

protected:
	// Cached emulator opcode to avoid repeated lookups
	EmuOpcode emu_opcode;

private:
	// Constructor used by EQProtocolPacket - assumes first bytes are opcode
	EQApplicationPacket(const unsigned char* buf, uint32 len, uint8 opcode_size = 0);
	
	// Prevent copy construction
	EQApplicationPacket(const EQApplicationPacket& p) = delete;
	EQApplicationPacket& operator=(const EQApplicationPacket& p) = delete;
	
	// Size of the opcode in bytes (1 or 2)
	uint8 app_opcode_size;
};

// Packet debugging and display functions
void DumpPacketHex(const EQApplicationPacket* app);
void DumpPacket(const EQProtocolPacket* app);
void DumpPacketAscii(const EQApplicationPacket* app);
void DumpPacket(const EQApplicationPacket* app, bool iShowInfo = false);
void DumpPacketBin(const EQApplicationPacket* app);

#endif
