#pragma once

#include <ctime>

// для работы с контроллером взят код товарища 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_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_HFS_OPEN_CAT    003		// Открыть оглавление HFS
#define CMD_HFS_READ_CATREC 013		// Прочитать запись оглавления HFS
#define CMD_HFS_MOUNT_UNIT  004		// Монтировать диск
#define CMD_HFS_UMOUNT_UNIT 014		// Размонтировать диск
#define CMD_GET_DIAGCODEEX  020		// Получить расширенный код диагностики

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 в словах

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

struct AZ_UNIT
{
	CFile       fFile;
	CString     strFileName;    // имя файла образа
	CString     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[130];
};

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 = 0;		// версия железа (тип контроллера)
constexpr uint16_t	STM_version = 14;	// версия прошивки STM
constexpr uint16_t	FPGA_version = 14;	// версия прошивки ПЛИС

class CAZBKController
{
		std::unique_ptr<CIni> m_pAZIni;			// ини файл содержимого SD-Карты
		CString             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[8];	// 8 строк всего. а приводов может быть гораздо больше

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

		void                az_start_io();
		void                az_fill_table();
		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);
		bool				az_write_eeprom(uint16_t *buff);

		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();

		int					m_nIoStatus; // 0 - нет операции, 1 - чтение, -1 - запись
		CString				m_strIoFileName;
		int					m_nIoFileSize;
		FILE			   *m_fileIo;
		CString				ParseFilePathName(CString 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();


	public:
		CAZBKController();
		~CAZBKController();
		void				Reset();
		uint16_t			GetFPGAVersion();

		bool				Init(uint8_t* pMemory);

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

