#pragma once
#include "AZBK\AZBK_Blitter.h"
#include "AZBK\AZBK_const.h"
#include "AZBK\AZBK_ctrl.h"

#include "BKMPIDevice.h"
#include "BKSound.h"
#include "LockVarType.h"

#include <mutex>
#include <random>
#include <thread>

constexpr auto AZBK_MEMSIZE = 32 * 1024 * 1024;
constexpr auto AZ_BANKS_NUM = 16;   // количество банков памяти в карте памяти. размер банка - 4кб
constexpr auto AZBK_PORTSIO_AREA = 0177000; // адрес отсечки, выше которого нет ОЗУ в банке 15

template<typename T>
constexpr auto AZ_BANK_TO_OFFSET(T bnk)
{
	return (bnk << 12);
}

template<typename T>
constexpr auto AZ_ADDRESS_TO_BANK(T a)
{
	return ((a >> 12) & 0xf);
}

/////////////////////////////////////////////////////////////////////////////////
// данные для эмуляции палитр

constexpr auto AZ_PALETTE_SIZE = 338;   // размер массива палитр
constexpr auto AZ_256_COLOR_IDX = 0;    // начало блока палитр из 256 цветов в массиве
constexpr auto AZ_16_COLOR_IDX = 320;   // начало блока палитр из 16 цветов в массиве
constexpr auto AZ_4_COLOR_IDX = 256;    // начало блока палитр из 4 цветов в массиве (стандартная БКшная 16х4)
constexpr auto AZ_2_COLOR_IDX = 336;    // начало блока палитр из 2 цветов в массиве (монохром)

class CAZBK : public CBKMPIDevice
{
		DECLARE_DYNAMIC(CAZBK)

#pragma pack(push)
#pragma pack(1)
		struct sAZCustom_t // структура для сохранения кастомных данных
		{
			AltProCtrlData_t    sAltpro{ 0, 0, 0 };
			BKMEMBank_t         sIOSegment;         // тут будет задаваться статус участка памяти 177000-177777
			// перехват некоторых данных
			int                 nLegacyPalette{ 0 };     // номер палитры
			int                 nLegacyScrBuf{0};      // номер отображаемого экрана

			BOOL                bSEL1{false};              // триггер, для подмены адреса запуска в 177716 на этапе старта/рестарта

			int                 nVideo_Mode{0};        // номер видеорежима
			int                 nBpp{0};               // бит на точку
			int                 nLineLen{0};           // длина строки в словах
			int                 nXStretch{0};          // число повторов точки по ширине
			int                 nYStretch{0};          // число повторов строки по высоте
			int                 nScrlSize{0};          // длина рулона в словах
			int                 nScrlLines{0};         // размер рулона в строках.
			// копия данных на время работы фрейма
			int                 nFrameVideo_Mode{0};   // номер видеорежима
			int                 nFrameLineLen{ 0 };      // длина строки в словах
			int                 nFrameXStretch{ 0 };     // число повторов точки по ширине
			int                 nFrameYStretch{ 0 };     // число повторов строки по высоте
			int                 nFrameScrlLines{ 0 };    // размер рулона в строках.
			// копия регистров скроллинга
			uint16_t            nFrameVSCRL177244{0};
			uint16_t            nFrameVSCRL177246{0};
			uint16_t            nFrameVSCRL177250{0};
			uint8_t             nFrameHSCRL177252{0};
			uint8_t             nFrameHSCRL177254{0};
			uint8_t             nFrameHSCRL177256{0};
			// копия регистров страниц
			uint16_t            nFrameSCRPageNum_L0{0};
			uint16_t            nFrameSCRPageNum_L1{0};
			uint16_t            nFrameSCRPageNum_L2{0};

			int                 nCurScrLineNum{0};     // номер линии VGA 0..805
			int                 nCurScrLinesCount{0};  // счётчик строк, зависит от видео режима
			int                 nCurScrYStretch{0};    // счётчик повторов строки
			std::array<uint32_t, 1024> LineBuffer{};   // буфер строки
			uint32_t           *pCurTextureBits{ nullptr };  // указатель на формируемую линию в текстуре
			uint16_t           *pDataBase_L0{nullptr};       // начало данных верхнего слоя
			uint16_t           *pDataBase_L1{nullptr};       // начало данных среднего слоя
			uint16_t           *pDataBase_L2{nullptr};       // начало данных нижнего слоя

			std::array<uint8_t, 8> pre_b11m_reg_az_mapper{ 0, 0, 0, 0, 0, 0, 0, 0 };
			std::array<uint8_t, 8> pre_smk_reg_az_mapper{ 0, 0, 0, 0, 0, 0, 0, 0 };   // массив регистров мапирования // сохраняем младшие 13бит // регистры только старшие
			uint16_t            pre_b11m_reg_az_win_ctrl{0};
			uint16_t            pre_b11m_reg_az_win_shdw{0};
			uint16_t            pre_smk_reg_az_win_ctrl{0};    // управление окнами - битовая маска активации окон, верхняя часть
			uint16_t            pre_smk_reg_az_win_ro{0};      // управление окнами - битовая маска r/o           окон, верхняя часть
			bool                bAltProMM{false};          // тип последнего использованного менеджера памяти true - altpro, false - БК11
			bool                bExtended{false};          // режим расширенной памяти (малого экрана)
			bool                bMode_Sync{false};         // режим синхронизации
			sAZCustom_t() = default;
		};

		struct sAZRegisters_t
		{
			// регистры DMA
			mutable uint16_t    nDMACSR{0};         // управление
			uint16_t            nDMAStartPage{0};       // начальная страница
			uint32_t            nDMAFileSize{0};        // размер файла в словах
			mutable uint16_t    nDMACurrentPage{0}; // текущая страница востпроизведения

			// регистры AY
			uint8_t             nAY1Addr{0}, nAY1Data{0}, nAY2Addr{0}, nAY2Data{0};
			// регистры COVOX
			uint16_t            nCVXLeft{0};
			uint16_t            nCVXRight{0};
			uint16_t            nCVXMono{0};
			uint16_t            nCVXCSR{0};

			// регистры Экрана
			uint16_t            nSCRCSR{012100};
			uint16_t            nSCRPageNum_L0{040000 / 010000};
			uint16_t            nSCRPageNum_L1{040000 / 010000};
			uint16_t            nSCRPageNum_L2{040000 / 010000};

			// регистры Палитры
			uint16_t            nPalCell{ 0 };

			// регистры скроллинга
			uint16_t            nVSCRL177244{0};
			uint16_t            nVSCRL177246{0};
			uint16_t            nVSCRL177250{0};
			uint8_t             nHSCRL177252{0};
			uint8_t             nHSCRL177254{0};
			uint8_t             nHSCRL177256{0};

			// регистры блиттера
			uint16_t            nBLTC177270{0};
			uint16_t            nBLTP177272{0};

			// регистры UART
			uint16_t            nCOM177560{0};
			uint16_t            nCOM177562{0};
			uint16_t            nCOM177564{0};
			uint16_t            nCOM177566{0};

			//регистры маппера памяти
			uint16_t            nAZ_win_ctrl{ 0 };       // 0177340 карта подключенных страниц.
			uint16_t            nAZ_win_ro{ 0 };         // 0177342 режим доступа
			uint16_t            nAZ_win_shdw{ 0 };       // 0177344 регистр управления shadow режимом
			uint16_t            nAZ_mapper_ctrl{ 0 };    // 0177346 регистр управления контроллером памяти
			uint16_t            nMMUAZ130{0};          // 0177350 R/O - регистр копия по записи 177130 в режиме записи управления памятью в SMK
			uint16_t            nMMUAZ716{0};          // 0177352 R/O - регистр копия по записи 177716 в режиме записи управления памятью в БК11М
			sAZRegisters_t() = default;
		};

#pragma pack(pop)

		std::unique_ptr<CAZBKController> m_pAZCtrl;
		std::unique_ptr<AZ_Blitter>     m_pAZBlt;

		std::unique_ptr<CBkSound> m_pAZBKSound;
		void                SetDMAWork();
		void                StopDMAThread();
		bool                StartDMAThread();
		void                DMAThreadFunc() const;  // собственно функция
		std::thread         m_DMAThread;
		mutable LockVarType m_lockDMAThr;   // мутекс потока. залочен пока поток живой
		mutable LockVarType m_lockDMA;      // мутекс для синхронизации

		mutable std::mutex  m_mutScrParam;  // блокировка изменения параметров экрана

		bool                m_bAZ50HzEnabled{ false };
		bool                Clk50НzFunc() const;  // собственно функция


		static const int   m_vAZBPP[];
		static const int   m_vAZLLEN[];
		static const int   m_vAZXS[];
		static const int   m_vAZYS[];
		static const int   m_vAZLSCRL[];

		static std::array<uint32_t, AZ_PALETTE_SIZE> m_azPalette;

		mutable uint32_t   *m_pTexture{nullptr};     // указатель на текстуру экрана

		sAZRegisters_t      m_AZRegs{};
		sAZCustom_t         m_AZData{};
		std::mt19937        m_rndEngine{};        // minstd_rand, mt19937, ranlux, knuth_b
		uint32_t            m_nRND{ 0x35355353 };
		uint16_t            m_nAltproMode{ 0 };   // временный код режима работы контроллера Альтпро
		uint16_t            m_nAltProMemBank{ 0 };// временный номер банка памяти контроллера Альтпро
		bool                m_bA16M_Trigger{ false };

		void                SetSynchroMode();

	protected:
		virtual bool        InitMemory(const uint16_t wHighLim) override;
		virtual void        MemoryManager() override;
		void                DropLegacyShadowMap(const uint16_t addr, const uint16_t value, UINT dwFlags = 0);
		void                MapToWindow(const int nWnd, const int nPage, const bool bRW); // подключение страницы в окно

		uint32_t            R5G5B5toRGB(const uint16_t c) const;
		uint16_t            RGBtoR5G5B5(const uint32_t c) const;
		void                SetScreenParam(const uint16_t v);

		// функции маппера
		uint16_t            AZ_716_Out() const;
		bool                AZ_716_In(const uint16_t w);
		uint16_t            Get_AZ_Mapper(const uint32_t n);
		void                Set_AZ_Mapper(const uint32_t n, const uint16_t v);

		void                AZ_130_In(const uint16_t w);

		void                AZ_MemoryManager();
		void                AltPro_MemoryManager();
		virtual void        SetMemoryMapViewData(const int seg) override;

		void                AutoSetAY();
		void                AutoSetCovox();

		virtual bool        CheckReadSystemRegister(const uint16_t addr) override;
		virtual bool        CheckWriteSystemRegister(const uint16_t addr) override;

	public:
		void                MakeScreenLine();
		void                DrawDebugScreen();
		void                CatchAr2Lat();

		CAZBK();
		virtual ~CAZBK() override;
		virtual bool        InitialSetDevice() override;

		virtual BK_DEV_MPI  GetBoardModel() const override
		{
			return BK_DEV_MPI::AZBK;
		}
		virtual std::wstring  GetBoardName() const override
		{
			return _T("AZBK");
		}

		virtual bool        InitDevice(const uint16_t addrStart) override;

		// Virtual method called after reset command
		virtual void        OnReset() override;
		virtual void        ResetCold();
		void                SetSel(bool b);

		// Методы, эмулирующие поведение регистров
		virtual bool        GetSystemRegister(const uint16_t addr, void *pDst, UINT dwFlags = 0) override;
		virtual bool        SetSystemRegister(const uint16_t addr, uint16_t value, UINT dwFlags = 0) override;
		// Methods for Set/Get byte/word
		virtual bool        GetByteT(const uint16_t addr, uint8_t *pValue, int &nTC) override;
		virtual bool        GetWordT(const uint16_t addr, uint16_t *pValue, int &nTC) override;
		virtual bool        SetByteT(const uint16_t addr, uint8_t value, int &nTC) override;
		virtual bool        SetWordT(const uint16_t addr, uint16_t value, int &nTC) override;
		virtual bool        GetByteIndirect(const uint16_t addr, uint8_t *pValue) override;
		virtual bool        GetWordIndirect(const uint16_t addr, uint16_t *pValue) override;
		virtual bool        CheckReadWord(const uint16_t addr) override;
		virtual bool        CheckWriteWord(const uint16_t addr) override;

		// Методы для загрузки и сохранения состояния эмулятора
		virtual bool        RestoreRegisters(const DWORD id, CMSFManager *pMsf) override; // это для сохранения/восстановления регистров
		virtual bool        RestoreMemory(const DWORD id, CMSFManager *pMsf) override;
		virtual bool        RestoreMemoryMap(const DWORD id, CMSFManager *pMsf) override;
		virtual bool        RestoreCustomData(const DWORD id, CMSFManager *pMsf) override; // это для сохранения/восстановления каких-нибудь своих данных
};



/*

/////////////////////////////////////////////////////////////////////////////////
распределение памяти.
0-128 - теневое ОЗУ БК
128-256 - System. это полноценная R/W память с которой всегда идет RPLY, но эта память служебная и используется для работы AZ контроллера
256-512 - RO память для эмуляции ПЗУ - безусловное RO, значения регистра 177342 не применяются.
512-1024 - память для эмуляции СМК
1024-... - память AZBK

177220 - регистр команд и состояния (CSR)
  по записи
|15|14|13|12|11|10| 9| 8| 7| 6| 5| 4| 3| 2| 1| 0|
                             |  +------+-------+
                             |         +---------- код команды
                             +-------------------- разрешение прерывания
  по чтению
|15|14|13|12|11|10| 9| 8| 7| 6| 5| 4| 3| 2| 1| 0|
  |  |                    |  |
  |  |                    |  +-------------------- разрешение прерывания
  |  |                    +----------------------- признак готовности
  +  +-------------------------------------------- признак данных не врезающих в 16 бит
  +----------------------------------------------- признак ошибки


177300-177336 – 16 регистров маппера. На каждое окно по 4 Кб
177300 - окно  0: адр 000000...007777
177302 - окно  1: адр 010000...017777
177304 - окно  2: адр 020000...027777
177306 - окно  3: адр 030000...037777
177310 - окно  4: адр 040000...047777
177312 - окно  5: адр 050000...057777
177314 - окно  6: адр 060000...067777
177316 - окно  7: адр 070000...077777
177320 - окно  8: адр 100000...107777
177322 - окно  9: адр 110000...117777
177324 - окно 10: адр 120000...127777
177326 - окно 11: адр 130000...137777
177330 - окно 12: адр 140000...147777
177332 - окно 13: адр 150000...157777
177334 - окно 14: адр 160000...167777
177336 - окно 15: адр 170000...177777
формат данных регистров маппера
|15|14|13|12|11|10| 9| 8| 7| 6| 5| 4| 3| 2| 1| 0|
|  |  |  |    номер сегмента памяти             |
биты 15-13 - зарезервированы

177340 - регистр маски окон маппера (вкл. / выкл.).
|15|14|13|12|11|10| 9| 8| 7| 6| 5| 4| 3| 2| 1| 0|
  |                                            +-- маска окна 0
  |                                                ......
  +----------------------------------------------- маска окна 15
1 в бите - в окно подключено заданное ОЗУ
0 - не подключено

177342 - регистр-маска режима доступа к окну (1-ro / 0-rw).
|15|14|13|12|11|10| 9| 8| 7| 6| 5| 4| 3| 2| 1| 0|
  |                                            +-- режим окна 0
  |                                                ......
  +----------------------------------------------- режим окна 15

177344 - регистр с битовой маской запрета подключения окон (1 аппаратный запрет на подключение окна)/
|15|14|13|12|11|10| 9| 8| 7| 6| 5| 4| 3| 2| 1| 0|
  |                                            +-- режим окна 0
  |                                                ......
  +----------------------------------------------- режим окна 15

Но поскольку в первых 32 кб ОЗУ БК всегда и не отключается, то использовать
окна 0..7 нельзя. т.е. если использовать, то будет наложение на ОЗУ БК.

/////////////////////////////////////////////////////////////////////////////////
Экран
177230 - регистр управления
|15|14|13|12|11|10| 9| 8| 7| 6| 5| 4| 3| 2| 1| 0|
 +----+----+    +-+-+    +-+-+    +-+-+    +-+-+
      |           |        |        |        +---- количество бит на цвет:
      |           |        |        |               0 - 1 бит на цвет - 2 цвета
      |           |        |        |               1 - 2 бита на цвет - 4 цвета
      |           |        |        |               2 - 4 бита на цвет - 16 цветов
      |           |        |        |               3 - 8 бит на цвет - 256 цветов
      |           |        |        +------------- длина строки в словах
      |           |        |                        0 - 32 слова
      |           |        |                        1 - 64
      |           |        |                        2 - 128
      |           |        |                        3 - 256
      |           |        +---------------------- растяжение по горизонтали - т.е. количество повторов у точки
      |           |                                 0 - х1 - те 1024 точки в строке
      |           |                                 1 - х2 - 512
      |           |                                 2 - х4 - 256
      |           |                                 3 - х8 - 128
      |           +------------------------------- количество повторов строк
      |                                             0 - х1 - 768 строк
      |                                             1 - х2 - 384
      |                                             2 - х3 - 256
      |                                             3 - х4 - 192
      +------------------------------------------- длина рулона в словах
                                                    0 - 6144        256 x 24
                                                    1 - 8192        256 x 32
                                                    2 - 12288       256 x 48
                                                    3 - 16384       256 x 64
                                                    4 - 24576       256 x 96
                                                    5 - 32768       256 x 128
                                                    6 - 49152       256 x 192
                                                    7 - 65536       256 x 256
                                                    8 - 98304       256 x 384
                                                    9 - 131072      256 x 512
                                                    10 - 196608     256 x 768
                                                    11 - 262144     256 x 1024

177232 - регистр номера страницы начала отображения
формат данных регистра
|15|14|13|12|11|10| 9| 8| 7| 6| 5| 4| 3| 2| 1| 0|
|  |  |  |    номер сегмента памяти             |
биты 15-13 - зарезервированы

Палитры
177234 - адрес ячейки палитры [9бит]
177236 - значение палитры [15бит]


/////////////////////////////////////////////////////////////////////////////////
COVOX
177200 - 16bit левый канал
177202 - 16bit правый канал
177204 - 16bit моно ковокс
177206 - 8bit стерео/mono ковокс
            битность определяется режимом обращения. словный - стерео, байтовый - моно
177212 - регистр управления режимами ковокса
формат данных регистра
|15|14|13|12|11|10| 9| 8| 7| 6| 5| 4| 3| 2| 1| 0|
                                         |  |  +-- режим легаси перехвата 177714:
                                         |  |       0 - моно, 1 - стерео
                                         |  +----- легаси перехват 177714:
                                         |          0 - разрешён, 1 - запрещён
                                         +-------- перехват звука из 177716:
                                                    0 - разрешён, 1 - запрещён

/////////////////////////////////////////////////////////////////////////////////
DMA звук
177160 - регистр управления
|15|14|13|12|11|10| 9| 8| 7| 6| 5| 4| 3| 2| 1| 0|
             +---+--+  +--+--+     |  |  |  |  +-- старт:
                 |        |        |  |  |  |       1 - старт
                 |        |        |  |  |  +----- однократный запуск:
                 |        |        |  |  |          1 - однократный запуск, 0 - циклический
                 |        |        |  |  |          после окончания однократного запуска устанавливается
                 |        |        |  |  |          в 1 бит 3, для циклического запуска бит 3 тоже должен
                 |        |        |  |  |          быть обнулён.
                 |        |        |  |  +-------- принудительная остановка до завершения цикла
                 |        |        |  +----------- по чтению: 1 - конец однократного запуска, 0 - ещё нет
                 |        |        |               по записи: 0 - разрешён циклический запуск
                 |        |        +-------------- =1 - потоковое воспроизведение с подкачкой данных
                 |        +----------------------- скорость выдачи наружу:
                 |                                  0 - 44100
                 |                                  больше пока нет
                 +-------------------------------- режим интерпретации:
                                                    000 - PCM 16 mono
                                                    100 - IMA ADPCM DECODER mono
                                                    101 - IMA ADPCM DECODER stereo
177162 - регистр адреса страницы, с которой будем читать с дискретностью 10000 (4кБ)
формат данных регистра
|15|14|13|12|11|10| 9| 8| 7| 6| 5| 4| 3| 2| 1| 0|
|  |  |  |    номер сегмента памяти             |
биты 15-13 - зарезервированы
177164 - старшая часть длины - 8бит
177166 - младшая часть длины - 16бит
177170 - номер текущей страницы воспроизведения
формат данных регистра
|15|14|13|12|11|10| 9| 8| 7| 6| 5| 4| 3| 2| 1| 0|
|  |  |  |    номер сегмента памяти             |
биты 15-13 - зарезервированы







*/
