#pragma once

#include <stdint.h>

namespace Kernel::NVMe
{

	struct CAP
	{
		uint64_t mqes : 16;
		uint64_t cqr : 1;
		uint64_t ams : 2;
		uint64_t __reserved0 : 5;
		uint64_t to : 8;
		uint64_t dstrd : 4;
		uint64_t nssrs : 1;
		uint64_t css : 8;
		uint64_t bps : 1;
		uint64_t cps : 2;
		uint64_t mpsmin : 4;
		uint64_t mpsmax : 4;
		uint64_t pmrs : 1;
		uint64_t cmpbs : 1;
		uint64_t nsss : 1;
		uint64_t crms : 2;
		uint64_t __reserved1 : 3;
	};
	static_assert(sizeof(CAP) == sizeof(uint64_t));

	enum CAP_CSS
	{
		CAP_CSS_NVME  = 1 << 0,
		CAP_CSS_IO    = 1 << 6,
		CAP_CSS_ADMIN = 1 << 7,
	};

	struct VS
	{
		uint32_t tertiary : 8;
		uint32_t minor : 8;
		uint32_t major : 16;
	};
	static_assert(sizeof(VS) == sizeof(uint32_t));

	struct CC
	{
		uint32_t en : 1;
		uint32_t __reserved0 : 3;
		uint32_t css : 3;
		uint32_t mps : 4;
		uint32_t ams : 3;
		uint32_t shn : 2;
		uint32_t iosqes : 4;
		uint32_t iocqes : 4;
		uint32_t crime : 1;
		uint32_t __reserved1 : 7;
	};
	static_assert(sizeof(CC) == sizeof(uint32_t));

	struct CSTS
	{
		uint32_t rdy : 1;
		uint32_t cfs : 1;
		uint32_t shts : 2;
		uint32_t nssro : 1;
		uint32_t pp : 1;
		uint32_t st : 1;
		uint32_t __reserved : 25;
	};
	static_assert(sizeof(CSTS) == sizeof(uint32_t));

	struct AQA
	{
		uint32_t asqs : 12;
		uint32_t __reserved0 : 4;
		uint32_t acqs : 12;
		uint32_t __reserved1 : 4;
	};
	static_assert(sizeof(AQA) == sizeof(uint32_t));

	// BAR0
	struct ControllerRegisters
	{
		CAP cap;
		VS vs;
		uint32_t intms;
		uint32_t intmc;
		CC cc;
		uint8_t __reserved0[4];
		CSTS csts;
		uint32_t nssr;
		AQA aqa;
		uint64_t asq;
		uint64_t acq;

		static constexpr uint32_t SQ0TDBL = 0x1000;
	};
	static_assert(sizeof(ControllerRegisters) == 0x38);

	struct DoorbellRegisters
	{
		uint32_t sq_tail;
		uint32_t cq_head;
	} __attribute__((packed));

	struct CompletionQueueEntry
	{
		uint32_t dontcare[3];
		uint16_t cid;
		uint16_t sts;
	} __attribute__((packed));
	static_assert(sizeof(CompletionQueueEntry) == 16);

	struct DataPtr
	{
		union
		{
			struct
			{
				uint64_t prp1;
				uint64_t prp2;
			};
			uint8_t sgl1[16];
		};
	};

	struct CommandGeneric
	{
		uint32_t nsid;
		uint32_t cdw2;
		uint32_t cdw3;
		uint64_t mptr;
		DataPtr dptr;
		uint32_t cdw10;
		uint32_t cdw11;
		uint32_t cdw12;
		uint32_t cdw13;
		uint32_t cdw14;
		uint32_t cdw15;
	} __attribute__((packed));
	static_assert(sizeof(CommandGeneric) == 15 * sizeof(uint32_t));

	struct CommandIdentify
	{
		uint32_t nsid;
		uint64_t __reserved0[2];
		DataPtr dptr;
		// dword 10
		uint8_t cns;
		uint8_t __reserved1;
		uint16_t cntid;
		// dword 11
		uint16_t cnsid;
		uint8_t __reserved2;
		uint8_t csi;
		// dword 12-15
		uint32_t __reserved3[4];
	} __attribute__((packed));
	static_assert(sizeof(CommandIdentify) == 15 * sizeof(uint32_t));

	struct CommandCreateCQ
	{
		uint32_t __reserved0;
		uint64_t __reserved1[2];
		DataPtr dptr;
		// dword 10
		uint16_t qid;
		uint16_t qsize;
		// dword 11
		uint16_t pc : 1;
		uint16_t ien : 1;
		uint16_t __reserved2 : 14;
		uint16_t iv;
		// dword 12-15
		uint32_t __reserved4[4];
	} __attribute__((packed));
	static_assert(sizeof(CommandCreateCQ) == 15 * sizeof(uint32_t));

	struct CommandCreateSQ
	{
		uint32_t __reserved0;
		uint64_t __reserved1[2];
		DataPtr dptr;
		// dword 10
		uint16_t qid;
		uint16_t qsize;
		// dword 11
		uint16_t pc : 1;
		uint16_t qprio : 2;
		uint16_t __reserved2 : 13;
		uint16_t cqid;
		// dword 12
		uint16_t nvmsetid;
		uint16_t __reserved4;
		// dword 13-15
		uint32_t __reserved5[3];
	} __attribute__((packed));
	static_assert(sizeof(CommandCreateSQ) == 15 * sizeof(uint32_t));


	struct CommandRead
	{
		uint32_t nsid;
		uint64_t __reserved0;
		uint64_t mptr;
		DataPtr dptr;
		// dword 10-11
		uint64_t slba;
		// dword 12
		uint16_t nlb;
		uint16_t __reserved1;
		// dword 13-15
		uint32_t __reserved2[3];
	} __attribute__((packed));
	static_assert(sizeof(CommandRead) == 15 * sizeof(uint32_t));

	struct SubmissionQueueEntry
	{
		uint8_t opc;
		uint8_t fuse : 2;
		uint8_t __reserved : 4;
		uint8_t psdt : 2;
		uint16_t cid;
		union
		{
			CommandGeneric generic;
			CommandIdentify identify;
			CommandCreateCQ create_cq;
			CommandCreateSQ create_sq;
			CommandRead read;
		};
	} __attribute__((packed));
	static_assert(sizeof(SubmissionQueueEntry) == 64);

	enum OPC : uint8_t
	{
		OPC_ADMIN_CREATE_SQ = 0x01,
		OPC_ADMIN_CREATE_CQ = 0x05,
		OPC_ADMIN_IDENTIFY = 0x06,
		OPC_IO_WRITE = 0x01,
		OPC_IO_READ = 0x02,
	};

	enum CNS : uint8_t
	{
		CNS_INDENTIFY_NAMESPACE = 0x00,
		CNS_INDENTIFY_CONTROLLER = 0x01,
		CNS_INDENTIFY_ACTIVE_NAMESPACES = 0x02,
	};

	struct NamespaceIdentify
	{
		uint64_t nsze;
		uint64_t ncap;
		uint64_t nuse;
		uint8_t nsfeat;
		uint8_t nlbaf;
		uint8_t flbas;
		uint8_t mc;
		uint8_t dpc;
		uint8_t dps;
		uint8_t nmic;
		uint8_t rescap;
		uint8_t fpi;
		uint8_t dlfeat;
		uint16_t nawun;
		uint16_t nawupf;
		uint16_t nacwu;
		uint16_t nabsn;
		uint16_t nabo;
		uint16_t nabspf;
		uint16_t noiob;
		uint64_t nvmcap[2];
		uint16_t npwg;
		uint16_t npwa;
		uint16_t npdg;
		uint16_t npda;
		uint16_t nows;
		uint16_t mssrl;
		uint32_t mcl;
		uint8_t msrc;
		uint8_t __reserved0[11];
		uint32_t adagrpid;
		uint8_t __reserved1[3];
		uint8_t nsattr;
		uint16_t nvmsetid;
		uint16_t endgid;
		uint64_t nguid[2];
		uint64_t eui64;
		uint32_t lbafN[64];
		uint8_t vendor_specific[3712];
	} __attribute__((packed));
	static_assert(sizeof(NamespaceIdentify) == 0x1000);

}
