#pragma once
#include "BKFloppyImage_Prototype.h"
#include <deque>

#pragma pack(push)
#pragma pack(1)
struct CsidosFileRecord
{
	struct FileName
	{
		std::array<uint8_t, 8> name{};
		std::array<uint8_t, 3> ext{};
	};

	uint8_t     type{ 0 };          // тип записи, номер директории для записи-директории
	uint8_t     protection{ 0 };    // если <0 - защита от удаления
	union
	{
		FileName file;
		std::array<uint8_t, 11> filename{ };
	};                              // имя файла с расширением, но без точки
	uint8_t     status{ 0 };        // статус файла.
	uint16_t    start_block{ 0 };   // начальный блок
	uint16_t    address{ 0 };       // адрес загрузки
	uint16_t    length{ 0 };        // длина файла, в байтах или блоках, зависит от статуса

	CsidosFileRecord() = default;

	CsidosFileRecord &operator = (const CsidosFileRecord &src)
	{
		this->type = src.type;
		this->protection = src.protection;
		this->filename = src.filename;
		this->status = src.status;
		this->start_block = src.start_block;
		this->address = src.address;
		this->length = src.length;
		return *this;
	}
	CsidosFileRecord &operator = (const CsidosFileRecord *src)
	{
		this->type = src->type;
		this->protection = src->protection;
		this->filename = src->filename;
		this->status = src->status;
		this->start_block = src->start_block;
		this->address = src->address;
		this->length = src->length;
		return *this;
	}

	bool operator==(const CsidosFileRecord &other) const
	{
		return type == other.type && protection == other.protection && filename == other.filename && status == other.status && start_block == other.start_block && address == other.address && length == other.length;
	}

	void clean()
	{
		type = 0;
		protection = 0;
		filename.fill(0);
		status = 0;
		start_block = 0;
		address = 0;
		length = 0;
	}
};

struct CsidosCatHeader
{
	uint16_t block_number{ 0 }; // физический номер блока
	uint16_t block_counts{ 0 }; // общее число блоков на диске
	uint16_t marker1{ 0 };      // 0123123 для csidos - 3
	uint16_t marker2{ 0 };      // 0123123 для csidos - 3
	uint16_t marker3{ 0 };      // 0123123 принадлежность диска системе csidos
	uint16_t reserved{ 0 };     // не используется == 0

	CsidosCatHeader &operator = (const CsidosCatHeader &src)
	{
		memcpy(this, &src, sizeof(CsidosCatHeader));
		return *this;
	}
	CsidosCatHeader &operator = (const CsidosCatHeader *src)
	{
		memcpy(this, src, sizeof(CsidosCatHeader));
		return *this;
	}

	bool operator==(const CsidosCatHeader &other) const
	{
		return block_number == other.block_number && block_counts == other.block_counts && marker1 == other.marker1 && marker2 == other.marker2 && marker3 == other.marker3 && reserved == other.reserved;
	}

	CsidosCatHeader(const uint16_t &block_number, const uint16_t &block_counts, const uint16_t &marker1, const uint16_t &marker2, const uint16_t &marker3, const uint16_t &reserved)
		: block_number(block_number), block_counts(block_counts), marker1(marker1), marker2(marker2), marker3(marker3), reserved(reserved)
	{
	}

	CsidosCatHeader() = default;
};
#pragma pack(pop)


class CBKFloppyImage_Csidos3 :
	public CBKFloppyImage_Prototype
{
		CsidosCatHeader m_FirstBlockHeader;
		std::deque<CsidosFileRecord> m_CSICatalog; // здесь будем хранить каталог

		/* поиск заданной записи в каталоге.
		делается поиск на полное бинарное соответствие
		выход: -1 если не найдено,
		номер записи в каталоге - если найдено.
		*/
		int     FindRecord(CsidosFileRecord *pRec);
		/* поиск заданной записи в каталоге.
		если bFull==false делается поиск по имени
		если bFull==true делается поиск по имени и другим параметрам
		выход: -1 если не найдено,
		номер записи в каталоге - если найдено.
		*/
		int     FindRecord2(CsidosFileRecord *pRec, bool bFull = true);

		bool    ReadCSICatalog();
		bool    WriteCSICatalog();
		bool    Squeeze();
		int     CompareSize(CsidosFileRecord *pRec, uint32_t fileLen);
		uint32_t GetLength(CsidosFileRecord *pRec);

		void    SetLength(CsidosFileRecord *pRec, uint32_t fileLen);
#ifdef _DEBUG
		void DebugOutCatalog();
#endif

	protected:
		virtual void ConvertAbstractToRealRecord(BKDirDataItem *pFR, bool bRenameOnly = false) override;
		virtual void ConvertRealToAbstractRecord(BKDirDataItem *pFR) override;
		virtual void OnReopenFloppyImage() override;
		virtual void GetSpecificData(BKDirDataItem *fr) const override;

	public:
		CBKFloppyImage_Csidos3(const PARSE_RESULT &image);
		virtual ~CBKFloppyImage_Csidos3() override;

		// виртуальные функции
		virtual uint32_t HasSpecificData() const override
		{
			return IDS_LC_SPECIFIC_CSIDOS_HDR;
		}

		/* прочитать каталог образа.
		 на выходе: заполненная структура m_sDiskCat */
		virtual bool ReadCurrentDir() override;
		/* записать каталог образа */
		virtual bool WriteCurrentDir() override;
		/* прочитать файл из образа
		* Параметры.
		* pFR - указатель на абстрактную запись о файле, внутри которой сохранена
		*       и реальная запись о файле, где сохранены все необходимые данные,
		*       даже с дублированием.
		* pBuffer - указатель на заранее выделенный массив памяти, куда будем сохранять файл.
		*   что мы будем делать потом с данными буфера, функцию не волнует. */
		virtual bool ReadFile(BKDirDataItem *pFR, uint8_t *pBuffer) override;
		virtual void OnExtract(BKDirDataItem *pFR, std::wstring &strName) override;
		/* записать файл в образ
		* Параметры.
		* pFR - указатель на абстрактную запись о файле, внутри которой сформирована
		*       реальная запись о файле, где по возможности сохранены все необходимые данные для записи,
		*       даже с дублированием.
		* pBuffer - указатель на заранее выделенный массив памяти, где заранее подготовлен массив сохраняемого файла. */
		virtual bool WriteFile(BKDirDataItem *pFR, uint8_t *pBuffer, bool &bNeedSqueeze) override;
		/* удалить файл в образе
		* Параметры.
		* pFR - указатель на абстрактную запись о файле, внутри которой сохранена
		*       и реальная запись о файле, где сохранены все необходимые данные,
		*       даже с дублированием. */
		virtual bool DeleteFile(BKDirDataItem *pFR, bool bForce = false) override;
		/* создать директорию в образе */
		virtual bool CreateDir(BKDirDataItem *pFR) override;
		/* проверить, существует ли такая директория в образе */
		virtual bool VerifyDir(BKDirDataItem *pFR) override;
		/* удалить директорию в образе */
		virtual bool DeleteDir(BKDirDataItem *pFR) override;

		virtual bool GetNextFileName(BKDirDataItem *pFR) override;

		virtual bool RenameRecord(BKDirDataItem *pFR) override;
};


/*
Расположение информации на диске

00 блок - загрузчик системы
02-011 блок - каталог
012-... блоки под файлы

Расположение записей в каталоге

первый блок
смещение длина значение
------------------------------
0       2       физический номер блока
2       2       общее число блоков на диске
4       2       0123123 для csidos-3
6       2       0123123 для csidos-3
010     2       0123123 принадлежность диска системе csidos
012     2       не используется == 0
014     0764    записи о файлах

остальные блоки
смещение длина значение
------------------------------
0       2       физический номер блока
2       012     не используется == 0
014     0764    записи о файлах


Формат записи о файле (024 байта)

смещение длина значение
------------------------------
0       1       тип:
                1-0310 номер директории, к которой принадлежит запись. 1 - корень.
                0311 плохое место - сбойный блок
                0312 запись не содержит информации (это когда удалили директорию)
                0376 дырка
                0377 удалённый файл, который можно восстановить
                0 конец каталога
                номер директории 0307 используется для сокрытия файлов.
1       1       <0 - защита от удаления
2       013     имя файла с расширением (без точки)
                8 символов на имя файла и 3 символа на расширение
                Оказывается, директории имеют только 8 символов имени,
                и опознаются по нулями в поле расширения
                Неочевидная фича - имя файла должно состоять только из строчных символов.
015     1       статус файла:
                7 бит - признак длины в блоках
                3 бит - признак файла для бк0010: 1 == БК11 0 == БК10
                остальные биты - копия регистра 0177717 (страницы)
                либо номер директории для директории
016     2       начальный блок файла, 0 для директории
020     2       адрес загрузки, 0 для директории
022     2       длина файла, 0 для директории

оставшееся свободное место в конце диска помечается дыркой.

*/