#pragma once

// параметры команды блиттера
struct AZ_BLITTER_t
{
	uint32_t    nSRC{0};        // 24 битный адрес источника в словах
	uint32_t    nDST{0};        // 24 битный адрес приёмника в словах
	uint16_t    nCMD{0};        // команда
	int         nWWidth{0};     // длина спрайта в словах по горизонтали
	int         nHeight{0};     // количество строк
	int         nPitch{ 0 };    // инкрементация адреса назначения после копирования строки спрайта
	uint32_t    nDstYAdd{ 0 };  // 7е слово Y координата для видеорежима 256. слов в строке
	uint8_t     nSRCconst{ 0 }; // константа заполнения или константа прозрачного цвета SRC
	uint8_t     nDSTconst{ 0 }; // константа прозрачного цвета DST

	void clear()
	{
		nSRC = nDST = nCMD = nWWidth = nHeight = nPitch = nDstYAdd = nSRCconst = nDSTconst = 0;
	}

	// чтение из памяти 8 слов команды
	// вход: ptr - указатель в памяти
	void read(uint16_t *ptr)
	{
		uint32_t w = *ptr++;    // первое слово: старший байт - старшая часть адреса SRC
		//               младший байт - старшая часть адреса DST
		nSRC = uint32_t(*ptr++) | (w & 0xff00) << 8; // 2 слово: младшая часть адреса SRC
		nDST = uint32_t(*ptr++) | (w & 0xff) << 16;  // 3 слово: младшая часть адреса DST
		nCMD = *ptr++;          // 4 слово: команда
		w = *ptr++;             // 5 слово:
		nWWidth = (w & 0xff) + 1;   // мл.байт длина спрайта в словах по горизонтали
		nHeight = (w >> 8) & 0xff;  // ст.байт количество строк (было в 7м слове)
		nPitch = ((*ptr++) & 0xff) - 1; // 6 слово (используется младший байт) инкрементация адреса назначения после копирования строки спрайта
		nDstYAdd = (*ptr++) << 8;       // 7 слово: Y координата для видеорежима 256 слов в строке, иначе говоря это часть базового адреса для удобства
		//           addr_mem_dst <= addr_dst + {spr_Yadd, 8'd0};
		w = *ptr++;             // 8 слово:
		nSRCconst = w & 0xff;   // младший байт - константа заполнения или константа прозрачного цвета SRC
		nDSTconst = (w >> 8) & 0xff; // старший байт - константа прозрачного цвета DST
	}
};

constexpr uint16_t AZ_BLT_RUNNING = (1 << 15); // флаг выполнения блиттером команд (RO): 1 - работает, 0 - не работает
constexpr uint16_t AZ_BLT_MANUAL = (1 << 14); // флаг ручного режима (RW): 0 - ручной, 1 - автоматический
constexpr uint16_t AZ_BLT_RUNMAN = (1 << 12); // флаг ручного запуска (WO): 1 - пуск
constexpr uint16_t AZ_BLT_READCMD = (1 << 9); // флаг чтения пачки команд: 1 - означает, что блиттер читает пачку команд в буфер и сейчас ничего с командами делать ничего нельзя. данный бит предназначен для организации конвейерной обработки пакетов команд

constexpr int AZ_BLT_CMDSIZE = 8;       // размер команды блиттера в словах
constexpr int AZ_BLT_MAXCMDNUM = 511;   // максимальное количество команд блиттера

constexpr int AZ_BLTCMDInLineNum = 200; // количество пересылок в словах, которое выполняется за время прорисовки одной строки VGA
constexpr uint32_t AZ_BLTWAITTIME = INFINITE;   // время ожидания события прорисовки строки, в мс.

// команда блиттера
constexpr uint16_t BLT_RDSRC        = (1 << 0); // для команды нужно чтение SRC
constexpr uint16_t BLT_RDDST        = (1 << 1); // для команды нужно чтение DST
constexpr uint16_t BLT_NOP          = (1 << 2); // флаг NOP - те наличие этого флага вызывает переход к следующей команде
constexpr uint16_t BLT_OPMASK       = (7 << 3); // Маска для выделения номера команды
constexpr uint16_t BLT_OP_FILL      = (0 << 3); // заполнение константой
constexpr uint16_t BLT_OP_COPYS2D   = (1 << 3); // копирование SRC=>DST
constexpr uint16_t BLT_OP_OVLOVER   = (2 << 3); // наложение SRC на DST =>DST (при SRC!=прозрачный цвет SRC)
constexpr uint16_t BLT_OP_OVLUNDR   = (3 << 3); // наложение SRC на DST =>DST (при SRC!=прозрачный цвет SRC) и (при DST=прозрачный цвет DST)
constexpr uint16_t BLT_OP_COPYD2S   = (4 << 3); // копирование DST => SRC (сохранение фона)
constexpr uint16_t BLT_OP_CONTURF   = (5 << 3); // заполнение константой по контуру
constexpr uint16_t BLT_SWAPB        = (1 << 6); // перестановка байтов (для зеркалирования)
constexpr uint16_t BLT_MIRRORH      = (1 << 9); // зеркалирование по горизонтали
constexpr uint16_t BLT_MIRRORV      = (1 << 10);// зеркалирование по вертикали

class AZ_Blitter
{
		uint16_t   *m_pMainMemW{ nullptr };      // указатель на основную память AZBK в виде словного массива
		uint16_t   *m_pMainMemWEnd{ nullptr };   // указатель на конец основной памяти AZBK в виде словного массива
		std::array<uint16_t, AZ_BLT_MAXCMDNUM *AZ_BLT_CMDSIZE> m_buffer{}; // внутренний буфер для хранения цепочки команд
		AZ_BLITTER_t m_Cmd{};         // параметры команды

		int         m_nCmd{ 0 };      // количество команд в цепочке, если 0, то в буфере пусто
		int         m_nNumMoves{ 0 };
		int         m_nCurCmd{ 0 };   // номер текущей выполняемой команды
		int         m_nW{ 0 };

		uint16_t    m_nReadCmds{ 0 };    // флаг чтения пачки команд
		uint16_t    m_nRunning{ 0 };     // флаг работы блиттера
		bool        m_bCmdFinished{ true }; // флаг получения следующей команды

		// временные параметры при выполнении команды
		uint16_t   *m_adstw{nullptr};
		uint16_t   *m_asrcw{nullptr};
		uint16_t    m_nFill000{ 0 };
		bool        m_bNeedReadSrc{false};
		bool        m_bNeedReadDst{false};
		bool        m_bSwapBytes{ false };
		int         m_nHorStep{ 0 };
		int         m_nVertStep{ 0 };

	public:
		AZ_Blitter();
		~AZ_Blitter();
		void        AttachMemory(uint16_t *pMainMemW);
		void        DoExec(const int nCmd, const int nAddr);
		void        Reset();

		inline uint16_t isRun() const
		{
			return m_nRunning;
		}

		inline uint16_t isReadCmds() const
		{
			return m_nReadCmds;
		}

		void        exec_chunk_cmd();
};
