#pragma once

#include "Types.h"
#include "zip/ZipArchiveWriter.h"
#include "zip/ZipArchiveReader.h"
#include "../gs/GSHandler.h"
#include "../Profiler.h"

class CDMAC;

class CGIF
{
public:
	enum REGISTER
	{
		GIF_MODE = 0x10003010,
		GIF_STAT = 0x10003020
	};

	enum
	{
		GIF_STAT_M3P = 0x002,
		GIF_STAT_OPH = 0x200,
		GIF_STAT_APATH3 = 0xC00,
	};

	enum
	{
		REGS_START = 0x10003000,
		REGS_END = 0x100030B0,

		GIF_FIFO_START = 0x10006000,
		GIF_FIFO_END = 0x10006FFF,
	};

	struct TAG
	{
		unsigned int loops : 15;
		unsigned int eop : 1;
		unsigned int reserved0 : 16;
		unsigned int reserved1 : 14;
		unsigned int pre : 1;
		unsigned int prim : 11;
		unsigned int cmd : 2;
		unsigned int nreg : 4;
		uint64 regs;
	};
	static_assert(sizeof(TAG) == 0x10, "Size of TAG must be 16 bytes.");

	CGIF(CGSHandler*&, CDMAC&, uint8*, uint8*);
	virtual ~CGIF() = default;

	void Reset();
	uint32 ReceiveDMA(uint32, uint32, uint32, bool);

	uint32 ProcessSinglePacket(const uint8*, uint32, uint32, uint32, const CGsPacketMetadata&);
	uint32 ProcessMultiplePackets(const uint8*, uint32, uint32, uint32, const CGsPacketMetadata&);

	void CountTicks(uint32);

	uint32 GetRegister(uint32);
	void SetRegister(uint32, uint32);

	CGSHandler* GetGsHandler();

	uint32 GetActivePath() const;
	void SetPath3Masked(bool);

	void LoadState(Framework::CZipArchiveReader&);
	void SaveState(Framework::CZipArchiveWriter&);

private:
	enum
	{
		FIFO_QWC = 0x10,
		FIFO_SIZE = FIFO_QWC * 0x10,
	};

	enum SIGNAL_STATE
	{
		SIGNAL_STATE_NONE,
		SIGNAL_STATE_ENCOUNTERED,
		SIGNAL_STATE_PENDING,
	};

	enum MASKED_PATH3_XFER_STATE
	{
		MASKED_PATH3_XFER_NONE,
		MASKED_PATH3_XFER_PROCESSING,
		MASKED_PATH3_XFER_DONE,
	};

	uint32 ProcessPacked(const uint8*, uint32, uint32);
	uint32 ProcessRegList(const uint8*, uint32, uint32);
	uint32 ProcessImage(const uint8*, uint32, uint32, uint32);

	void ProcessFifoWrite(uint32, uint32);
	void DrainFifo();

	void DisassembleGet(uint32);
	void DisassembleSet(uint32, uint32);

	bool m_path3Masked = false;
	uint32 m_activePath = 0;
	uint32 m_MODE = 0;

	uint16 m_loops = 0;
	uint8 m_cmd = 0;
	uint8 m_regs = 0;
	uint8 m_regsTemp = 0;
	uint64 m_regList = 0;
	bool m_eop = false;
	uint32 m_qtemp;
	SIGNAL_STATE m_signalState = SIGNAL_STATE_NONE;
	MASKED_PATH3_XFER_STATE m_maskedPath3XferState = MASKED_PATH3_XFER_NONE;
	int32 m_path3XferActiveTicks = 0;
	uint8 m_fifoBuffer[FIFO_SIZE];
	uint32 m_fifoIndex = 0;
	uint8* m_ram;
	uint8* m_spr;
	CGSHandler*& m_gs;
	CDMAC& m_dmac;

	CProfiler::ZoneHandle m_gifProfilerZone = 0;
};
