#pragma once

#include <ctime>
#include <io.h>

// для работы с контроллером взят код товарища form

/////////////////////////////////////////////////////////////////////////////////
// данные для эмуляции контроллера AZBK

constexpr uint16_t CS_CMD_MASK = 0000077;
constexpr uint16_t CS_IE = 0000100;
constexpr uint16_t CS_DONE = 0000200;
constexpr uint16_t CS_BIG = 0040000;
constexpr uint16_t CS_ERR = 0100000;
constexpr uint16_t CS_VECTOR = 0000174; // адрес вектора прерываний контроллера

constexpr uint16_t CS_RD_MASK = (CS_ERR | CS_BIG | CS_DONE | CS_IE);
constexpr uint16_t CS_WR_MASK = (CS_IE | CS_CMD_MASK);


template<typename T>
constexpr T AZ_CMD(T v)     // выделяем код команды
{
	return ((v) & CS_CMD_MASK);
};

// команды контроллера

constexpr auto CMD_RESET            = 000;  // Сброс контроллера
constexpr auto CMD_SETUNI           = 001;  // Выбор устройства
constexpr auto CMD_SETBLK           = 002;  // Установка номера блока, младшие биты
constexpr auto CMD_SETBLKX          = 012;  // Установка номера блока, старшие биты
constexpr auto CMD_READ             = 005;  // Чтение блока в буфер
constexpr auto CMD_READ_BUF         = 015;  // Начать передачу считанного блока
constexpr auto CMD_WRITE            = 006;  // Записать блок из буфера на диск
constexpr auto CMD_WRITE_BUF        = 016;  // Принять блок данных в буфер
constexpr auto CMD_GET_SIZE         = 007;  // Получить размер псевдодиска, младшие биты
constexpr auto CMD_GET_SIZEX        = 017;  // Получить размер псевдодиска, старшие биты
constexpr auto CMD_NET              = 010;  // Разрешить работу сети
constexpr auto CMD_NOP              = 030;  // Нет операции
constexpr auto CMD_GET_FEAT         = 027;  // Считывание версии
constexpr auto CMD_HFS_GET_AZUNITS  = 011;  // Получить таблицу назначений приводов AZn
constexpr auto CMD_HFS_OPEN_CAT     = 003;  // Открыть оглавление HFS
constexpr auto CMD_HFS_READ_CATREC  = 013;  // Прочитать запись оглавления HFS
constexpr auto CMD_HFS_MOUNT_UNIT   = 004;  // Монтировать диск
constexpr auto CMD_HFS_UMOUNT_UNIT  = 014;  // Размонтировать диск

constexpr auto CMD_GET_EEPROM       = 021;  // считать блок 1 энергонезависимой памяти в буфер
constexpr auto CMD_GET_IOBLOCK      = 022;  // отдать на шину считанный блок энергонезависимой памяти из буфера
constexpr auto CMD_PUT_IOBLOCK      = 023;  // принять с шины в буфер данные для последующей записи в буфер
constexpr auto CMD_PUT_EEPROM       = 024;  // записать из буфера в блок 1 энергонезависимой памяти

constexpr auto CMD_RTC_GET_TIME_HW  = 031;  // получить время из часов МК в буфер timestamp
constexpr auto CMD_RTC_READ_TIME    = 032;  // получить время из буфера timestamp в МПИ
constexpr auto CMD_RTC_WRITE_TIME   = 033;  // запись времени-даты в буфер (ручная установка часов) формат SimpleIN ( 7 слов)
constexpr auto CMD_RTC_SET_TIME_BUF = 034;  // установка автономных часов на основании данных из буфера
constexpr auto CMD_RTC_GET_TIME_NTP = 035;  // стимуляция запроса времени с NTP сервера, установка на основании ответа
constexpr auto CMD_RTC_SET_TIME_NET = 036;  // установка автономных часов STM32 на основании часов в TCP/IP стеке
constexpr auto CMD_RTC_GET_TIME_NET = 042;  // получить время из часов TCP/IP стека в буфер timestamp

constexpr auto CMD_RESET_AZBK       = 037;  // перезапуск AZБК и всей БК

// пока не реализованные
#define CMD_GET_DIAGCODEEX  020     // Получить расширенный код диагностики
#define CMD_INIT_HOF        025     // инициализация работы по сети
#define CMD_IO_HOF          026     // обмен данными по сети
#define CMD_SCREENSHOT      044     // Сделать скриншот

constexpr auto CMD_NET_GET_IP       = 040;      // получить ip адреса в буфер
constexpr auto CMD_NET_READ_IP      = 041;      // чтение буфера ip адреса - 8 слов 16bit
constexpr auto CMD_NET_GET_MAC      = 043;      // чтение MAC-адреса в ip буфер

constexpr auto CMD_IO_SET_RDFILE    = 050;      // установить имя файла который будем читать
constexpr auto CMD_IO_GET_FILESTAT  = 051;      // получить размер файла на чтение(или его статус) на МПИ
constexpr auto CMD_IO_READ          = 052;      // читать блок установленного файла в буфер
constexpr auto CMD_IO_SET_WRFILE    = 053;      // установить имя файла который будем писать
constexpr auto CMD_IO_SET_FILESIZE  = 054;      // установка длины файла
constexpr auto CMD_IO_WRITE         = 055;      // записать в файл данные из буфера

constexpr auto CMD_IO_GET_SDSIZE    = 056;      // получить данные по размеру карты в буфер sizecard
constexpr auto CMD_IO_READ_SDSIZE   = 057;      // чтение буфера sizecard - 2 cлова 16bit


constexpr auto AZ_UNIT_MASK = 0000377;
constexpr auto AZ_LSB_MASK  = 0177777;

#define UF_CFG          0x01
#define UF_BOT          0x02
#define UF_MTD          0x04
#define UF_ERR          0x80

constexpr auto AZ_BUF_SIZE    = 256;    // размер буфера ввода-вывода AZBK в словах
constexpr auto AZ_EEPROM_SIZE = 255;    // размер данных энергонезависимой памяти блока 1 EEPROM AZBK в словах

constexpr auto AZ_CNTDSK = 32;			// количество дисков
constexpr auto AZ_LENFLN = 386;			// длина буфера под имя файла

/////////////////////////////////////////////////////////////////////////////////

struct AZ_UNIT
{
	CFile       fFile;
	fs::path    strFileName;    // имя файла образа
	fs::path    strOrigPathName; // путь к файлу из ини файла
	bool        bRO;            // Флаг защиты от записи
	uint32_t    nSize;          // размер образа в байтах
	uint32_t    nBlkSize;       // размер образа в блоках
	uint32_t    nAttr;          // атрибуты файла
	uint16_t    nDate, nTime;   // дата/время в формате МС-ДОС
	AZ_UNIT() : bRO(true), nSize(0), nBlkSize(0), nAttr(0), nDate(0), nTime(0) {}
};

// элемент внутренней таблицы назначений.
#pragma pack(push)
#pragma pack(1)
struct az_internal_table
{
	uint32_t    size;
	uint16_t    date;
	uint16_t    time;
	uint8_t     flag;
	uint8_t     attr;
	char        name[AZ_LENFLN];
};

struct az_HFSitem
{
	uint32_t    fsize;
	uint16_t    fdate;
	uint16_t    ftime;
	uint8_t     fattrib;
	char        fname[13];
};

struct az_timestamp
{
	uint16_t rt11date;
	uint16_t rt11time50h;
	uint16_t rt11time50l;
	uint16_t rt11time60h;
	uint16_t rt11time60l;
	uint16_t fatdate;
	uint16_t fattime;
	uint16_t year;
	uint16_t month;
	uint16_t day;
	uint16_t wday;
	uint16_t hour;
	uint16_t min;
	uint16_t sec;
};
#pragma pack(pop)



class CIni;

constexpr uint16_t  AZ_version = 4;     // версия железа (тип контроллера)
constexpr uint16_t  STM_version = 17;   // версия прошивки STM
constexpr uint16_t  FPGA_version = 17;  // версия прошивки ПЛИС


class CAZBKController
{
		std::unique_ptr<CIni> m_pAZIni;         // ини файл содержимого SD-Карты
		fs::path            m_strConfImgPath;   // путь к содержимому SD-Карты, там хранится всё нужное

		//регистры контроллера
		uint16_t            azcs;   // 177220
		uint16_t            azdr;   // 177222
		uint16_t            azbr1;  // 177224
		uint16_t            azbr2;  // 177226

		int                 azunit;         // номер выбранного привода для работы
		uint32_t            azblkn;         // номер блока для чтения/записи
		int                 azcmd;          // текущая команда контроллера
		uint32_t            az_init_wcnt;   // инициализирующее количество слов для обмена

		uint16_t           *dr_rd_data;     // текущий указатель на читаемые данные из буфера
		uint16_t           *dr_wr_data;     // текущий указатель на записываемые данные в буфер
		uint16_t           *curr_rd_buf;    // указатель на текущий читаемый буфер. В основном это azbuf
		int                 dr_wcnt;        // счётчик слов для обмена
		uint16_t            azbuf[AZ_BUF_SIZE]; // буфер данных для обмена - эмуляции приводов дисководов
		uint16_t            iobuf[AZ_BUF_SIZE]; // буфер данных для работы команд для работы с сд-картой
		uint16_t            ipaddrblock[12];    // буфер блока ип адреса
		az_internal_table   az_assets[AZ_CNTDSK]; // массив примонтированных приводов

		// Контроллер поддерживает до восьми псевдодисков
		// мы же сделаем поддержку неограниченного количества, т.е. ограниченного типом int
		std::unique_ptr<AZ_UNIT[]> m_pUnits;// таблица псевдодисков из ини-файле
		int                 m_nMaxUnit;     // максимальный номер псевдодиска
		int                 m_nBootUnit;    // номер загрузочного псевдодиска

		void                az_start_io();
		void                az_fill_table();
		bool				az_mount_unit();
		bool				az_umount_unit();
		void                az_set_done();
		uint16_t            az_read_data(uint16_t *reg, uint16_t **buff, int *wcnt);
		void                az_write_data(uint16_t *reg, uint16_t data, uint16_t **buff, int *wcnt);

		bool                az_read_eeprom(uint16_t *buff) const;
		bool                az_write_eeprom(uint16_t *buff) const;

		struct _wfinddata_t	m_file;
		intptr_t			m_hFile;
		fs::path			m_findPath;
		bool				az_open_catHFS();
		bool				az_read_itemHFS();

		time_t              m_timeOffset;   // разница между локальным временем хоста и эмулируемого AZBK
		az_timestamp        m_timestamp;    // буфер timestamp
		uint16_t            rtc_fat_date(std::tm *now);
		uint16_t            rtc_fat_time(std::tm *now);
		uint16_t            rtc_rt11date(std::tm *now);
		uint32_t            rtc_rt11time50(std::tm *now);
		uint32_t            rtc_rt11time60(std::tm *now);

		void                az_fill_timestamp(az_timestamp *t);
		void                az_set_time();

		fs::path            m_strIoFileName;
		FILE               *m_fileIo;
		int                 m_nIoFileSize;
		int                 m_nIoStatus; // 0 - нет операции, 1 - чтение, -1 - запись
		fs::path            ParseFilePathName(const fs::path &str);
		void                getMACAddressFromIni(uint8_t *buf);
		void                setMACAddressToIni();
		void                StrIPtoNumIP(uint8_t *buf, int nOffset, CString &src);
		CString             NumIPtoStrIP(uint8_t *buf, int nOffset);
		void                getNetParametersFromIni(uint8_t *buf);
		void                setNetParametersToIni();
		void				ReadDisksFromIni();
		bool				ProcessDiskFromIni(const CString &strKey, const CString &strValue);
		CString				RepackDiskKey(const CString &strKey);
	public:
		CAZBKController();
		~CAZBKController();
		void                Reset();
		uint16_t            GetFPGAVersion();

		bool                Init(uint8_t *pMemory);

		uint16_t            ReadCSR() const;
		bool                WriteCSR(uint16_t v);
		uint16_t            ReadDR_int() const;
		bool                ReadDR(uint16_t *v);
		bool                WriteDR(uint16_t v);
		bool                ReadBR1(uint16_t *v) const;
		bool                WriteBR1(uint16_t v) const;
		bool                ReadBR2(uint16_t *v) const;
		bool                WriteBR2(uint16_t v) const;
};

