#pragma once

#include "AZBK_IO.h"
#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;  // Получить размер псевдодиска, большой (32 бита)
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     // обмен данными по сети

constexpr auto 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_READ_FILE     = 047;      // читать файл непосредственно в память AZ
constexpr auto CMD_IO_GET_SDSIZE    = 056;      // получить данные по размеру карты в буфер sizecard
constexpr auto CMD_IO_READ_SDSIZE   = 057;      // чтение буфера sizecard - 2 cлова 16bit


constexpr auto AZ_UNIT_NUMBER = 32;     // максимальное количество дисков (степерь двойки)
constexpr auto AZ_UNIT_MASK = AZ_UNIT_NUMBER - 1; // до 32 дисков
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_IOBUF_SIZE_W = 256;   // размер буфера ввода-вывода AZBK в словах
constexpr auto AZ_EEPROM_SIZE_W = 255;  // размер данных энергонезависимой памяти блока 1 EEPROM AZBK в словах
constexpr auto AZ_IPBUF_SIZE = 12;      // размер буфера для ip адреса


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

class CIni;

constexpr uint16_t  AZ_version = 4;     // версия железа (тип контроллера)
constexpr uint16_t  STM_version = 18;   // версия прошивки STM
constexpr uint16_t  FPGA_version = 18;  // версия прошивки ПЛИС
constexpr auto Product_URL = "https://gid.pdp-11.ru/";

// структура хидера скриншота
#pragma pack(push)
#pragma pack(1)
using screen_header_t = struct screen_header
{
	unsigned short int              tag;                // должен быть равен 0240

	unsigned int                    begin_adress;       // начальный адрес в словах - прямая задача в физических адресах
	unsigned int                    length;             // длина в словах - прямая задача в физических адресах

	unsigned short int              begin_page;         // начальная страница - номер - прямая задача в номерах страниц
	unsigned short int              len_pages;          // количество страниц - прямая задача в номерах страниц

	unsigned short int              R177300;            //
	unsigned short int              R177302;            //
	unsigned short int              R177304;            //
	unsigned short int              R177306;            //
	unsigned short int              R177310;            //
	unsigned short int              R177312;            //
	unsigned short int              R177314;            //
	unsigned short int              R177316;            //
	unsigned short int              R177320;            //
	unsigned short int              R177322;            //
	unsigned short int              R177324;            //
	unsigned short int              R177326;            //
	unsigned short int              R177330;            //
	unsigned short int              R177332;            //
	unsigned short int              R177334;            //
	unsigned short int              R177336;            //
	unsigned short int              R177340;            // - Регистр управления активацией окно - маски окон
	unsigned short int              R177342;            // - Регистр управления r/o на окно
	unsigned short int              R177344;            // - Регистр управления shadow окон - маски окон
	unsigned short int              R177346;            // - Регистр управления маппером
	unsigned short int              R177350;            // - регистр копия по записи 177130 в режиме записи управления памятью в SMK
	unsigned short int              R177352;            // - регистр копия по записи 177716 в режиме записи управления памятью в БК11М

	unsigned short int              R177230;            // - регистр управления
	unsigned short int              R177232;            // - регистр номера страницы начала отображения - верхняя страница (слой 0)
	unsigned short int              R177240;            // - регистр номера страницы начала отображения - средняя страница (слой 1)
	unsigned short int              R177242;            // - регистр номера страницы начала отображения - нижняя страница  (слой 2)
	unsigned short int              R177244;            // - регистр вертикального скролинга слой 2
	unsigned short int              R177246;            // - регистр вертикального скролинга слой 1
	unsigned short int              R177250;            // - регистр вертикального скролинга слой 0
	unsigned short int              R177252;            // - регистр гозизонтального скролинга слой 0
	unsigned short int              R177254;            // - регистр гозизонтального скролинга слой 1
	unsigned short int              R177256;            // - регистр гозизонтального скролинга слой 2

	unsigned short int              paldata[338];   //
};
#pragma pack(pop)

class CPlatform;

class CAZBKController
{
		CPlatform          *m_pPlatform{ nullptr };
		std::unique_ptr<CIni> m_pAZIni;         // ини файл содержимого SD-Карты
		const uint8_t      *m_pAZMainMemory{nullptr};   // указатель на основную память AZ
		//регистры контроллера
		uint16_t            azcs{ 0 };          // 177220
		uint16_t            azdr{ 0 };          // 177222
		uint16_t            azbr1{ 0 };         // 177224
		uint16_t            azbr2{ 0 };         // 177226

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

		uint16_t           *dr_rd_data{nullptr};     // текущий указатель на читаемые данные из буфера
		uint16_t           *dr_wr_data{nullptr};     // текущий указатель на записываемые данные в буфер
		uint16_t           *curr_rd_buf{nullptr};    // указатель на текущий читаемый буфер. В основном это azbuf
		int                 dr_wcnt{ 0 };        // счётчик слов для обмена
		std::array < uint16_t, AZ_IOBUF_SIZE_W + 2 > az_iobuf{}; // буфер данных для обмена - эмуляции приводов дисководов - это основной буфер - IOBUF
		std::array<uint16_t, AZ_IOBUF_SIZE_W> az_cmosmem{}; // буфер данных для работы команд для работы с сд-картой - это энергонезависимый буфер
		std::array<uint16_t, AZ_IPBUF_SIZE> ipaddrblock{}; // буфер блока ип адреса
		std::array<uint16_t, 2> az_sizecard{};      // буфер размера SD карты
		std::array<az_internal_table, AZ_UNIT_NUMBER> az_assets{}; // массив примонтированных приводов

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

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

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

		AZ_UNIT             m_DirHFS;
		bool                az_open_catHFS();
		bool                az_read_itemHFS();

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

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

		int                 m_nIoFileSizeRd{ 0 }, m_nIoFileSizeWr{ 0 };
		AZ_UNIT             m_azFileIORD, m_azFileIOWR;
		int                 m_nIoStatus{ 0 }; // 0 - нет операции, 1 - чтение, -1 - запись
		void                getMACAddressFromIni(uint8_t *buf) const;
		void                setMACAddressToIni();
		void                StrIPtoNumIP(uint8_t *buf, int nOffset, CString &src) const;
		CString             NumIPtoStrIP(const uint8_t *buf, int nOffset) const;
		void                getNetParametersFromIni(uint8_t *buf) const;
		void                setNetParametersToIni();
		bool                isRomKey(const WCHAR ch) const;
		bool                isLogoKey(const WCHAR ch) const;
		bool                isDiskKey(const WCHAR ch) const;
		void                ReadDisksFromIni();
		bool                ProcessDiskFromIni(const std::wstring &strKey, const std::wstring &strValue) const;
		std::wstring        RepackDiskKey(const std::wstring &strKey) const;

		// функции скриншота
		std::wstring        gen_filename_for_screen();
		bool                simple_save_memory_to_file(std::wstring &screen_filename);
		bool                cmd044func();

	public:
		CAZBKController(CPlatform *p);
		~CAZBKController();
		void                Reset();
		uint16_t            GetFPGAVersion() const;

		bool                Init(const 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;
};

