#pragma once
#include <thread>
#include <mutex>
#include <condition_variable>

// параметры команды блиттера
struct AZ_BLITTER_t
{
	uint32_t    nSRC;       // 24 битный адрес источника в словах
	uint32_t    nDST;       // 24 битный адрес приёмника в словах
	uint16_t    nCMD;       // команда
	int         nWWidth;    // длина спрайта в словах по горизонтали
	int         nHeight;    // количество строк
	int         nPitch;     // инкрементация адреса назначения после копирования строки спрайта
	uint32_t    nDstYAdd;   // 7е слово Y координата для видеорежима 256. слов в строке
	uint8_t     nSRCconst;  // константа заполнения или константа прозрачного цвета SRC
	uint8_t     nDSTconst;  // константа прозрачного цвета 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;      // указатель на основную память AZBK в виде словного массива
		uint16_t *m_pMainMemWEnd;   // указатель на конец основной памяти AZBK в виде словного массива
		uint16_t    m_buffer[AZ_BLT_MAXCMDNUM * AZ_BLT_CMDSIZE]; // внутренний буфер для хранения цепочки команд
		AZ_BLITTER_t m_Cmd;         // параметры команды

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

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

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

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