#pragma once
// скриптовый движок

#include "BKToken.h"

struct ScriptLine_t         // элемент строки скрипта
{
	CBKToken Label;         // метка строки, если есть
	CBKToken Cmd;           // команда строки, сделана токеном только потому, что её так сохранять легче
};

// текст скрипта
using ScriptText_t = std::vector<ScriptLine_t>;

struct Script_t             // скрипт
{
	CBKToken name;          // имя скрипта, опционально
	ScriptText_t script;    // текст скрипта
};

extern std::vector<Script_t> g_ScriptDefs;

// когда нихрена не получается с форматным выводом std::format поможет это
template<typename... Args>
std::wstring dyna_wprint(std::wstring_view rt_fmt_str, Args &&... args)
{
	return std::vformat(rt_fmt_str, std::make_wformat_args(args...));
}

template<typename... Args>
std::string dyna_print(std::string_view rt_fmt_str, Args &&... args)
{
	return std::vformat(rt_fmt_str, std::make_format_args(args...));
}


constexpr auto MAX_FILE_SLOTS = 8;
class CScriptAsm
{
		enum
		{
			SC_SRC = 0,
			SC_DST
		};

		enum PSW_FLG : uint16_t
		{
			C = 1,
			V = 2,
			Z = 4,
			N = 8,
		};

		struct Operand
		{
			int addressation{ 0 };  // адресация, если 0, то актуальный адрес==номер регистра (нужен для set_arg)
			int address{ 0 };       // актуальный адрес
			int value{ 0 };         // если адресация 0, то сюда надо сразу помещать значение регистра
		};

		uint16_t            m_PSW{ 0 };
		std::array<uint16_t, 8> m_Vars{ 0, 0, 0, 0, 0, 0, 0, 0 }; //массив переменных R0..R7
		std::array<FILE *, MAX_FILE_SLOTS> m_FileSlots{nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr}; // слоты для файлов
		std::array<Operand, 2> m_OpAddr{};  // параметры операндов: источника и приёмника
		bool                m_bByteOp{ false }; // флаг байтовой операции
		uint32_t            m_ALU{ 0 };     // приёмник
		uint32_t            m_Nbit{ 0 };    // знаковый разряд для операции. 200 для байта, 100000 для слова
		CBKToken            m_scriptName;
		ScriptText_t        m_script;       // скрипт, который выделяется из исходного текста при парсинге.

	public:
		CScriptAsm() noexcept
		{};

		~CScriptAsm()
		{
			m_script.clear();
		};

		void            Init(const std::wstring &strName)
		{
			m_scriptName.setName(strName);
			m_script.clear();
		}

		void            AddScriptLine(const ScriptLine_t &sl)
		{
			m_script.push_back(sl);
		}

		void            PushScript()
		{
			g_ScriptDefs.push_back({ m_scriptName, m_script });
		}

		void            RunScript();

		void            Store(FILE *f) const;
		bool            Load(const uint32_t nLen, FILE *f) const;

	protected:
		bool            SkipWhitespace(wchar_t **pch) const noexcept;
		bool            ReadToken(CBKToken &token, wchar_t **pch, const bool bNoSkip) const;
		bool            ReadOctalNumber(int &num, wchar_t **pch, const bool bNoSkip) const;

		bool            NeedChar(wchar_t ch, wchar_t **pch, const bool bNoSkip) const;
		bool            isLetter(wchar_t ch) const;
		bool            isOctalDigit(const wchar_t ch) const noexcept;
		bool            ParseScripOp(int op, wchar_t **pch);
		bool            CheckRegName(int &num, const CBKToken &reg) const;
		int             CheckRelReg(int &num, wchar_t **pch) const;

		size_t          ProcessScriptLine(const size_t ln, const std::vector<ScriptLine_t> *sc);

		bool            ProcessBranch(const uint16_t opcode);
		void            ProcessOneOps(const uint16_t opcode);
		void            ProcessTwoOps(const uint16_t opcode);
		void            ProcessNoOps(const uint16_t opcode, wchar_t **pch);

		size_t          ProcessFileOps(const size_t ln, const uint16_t opcode, wchar_t **pch);
		bool            CheckComment(wchar_t **pch) noexcept;
		bool            GetName(std::wstring &strName, wchar_t **pch);
		size_t          ProcessTypeOps(const size_t ln, const uint16_t opcode, wchar_t **pch);
		bool            TypeOpArgs(const std::wstring str, std::_tstring &strError);
		void            CloseAll() noexcept;

		uint32_t        get_arg(const int op) const;
		void            set_arg(const int op, const uint32_t v);

		inline bool     GetPSWBit(const PSW_FLG flg) const noexcept
		{
			return !!(m_PSW & flg);
		}
		inline void     SetPSWBit(const PSW_FLG flg, const bool val) noexcept
		{
			if (val)
			{
				m_PSW |= flg;
			}
			else
			{
				m_PSW &= ~flg;
			}
		}

		inline void     SetC(const bool bFlag)
		{
			SetPSWBit(PSW_FLG::C, bFlag);
		}

		inline bool     GetC() const
		{
			return GetPSWBit(PSW_FLG::C);
		}

		inline void     SetV(const bool bFlag)
		{
			SetPSWBit(PSW_FLG::V, bFlag);
		}

		inline bool     GetV() const
		{
			return GetPSWBit(PSW_FLG::V);
		}

		inline void     SetZ(const bool bFlag)
		{
			SetPSWBit(PSW_FLG::Z, bFlag);
		}

		inline bool     GetZ() const
		{
			return GetPSWBit(PSW_FLG::Z);
		}

		inline void     SetN(const bool bFlag)
		{
			SetPSWBit(PSW_FLG::N, bFlag);
		}

		inline bool     GetN() const
		{
			return GetPSWBit(PSW_FLG::N);
		}

		inline void     Set_NZ()
		{
			// установка N
			SetN(!!(m_ALU & m_Nbit));

			// установка Z
			if (m_bByteOp)
			{
				SetZ(!LOBYTE(m_ALU));
			}
			else
			{
				SetZ(!LOWORD(m_ALU));
			}
		}
		inline void     Set_V(const uint32_t old)
		{
			// установка V - это смена значения знакового бита. с 0 на 1 (для сложений)
			SetV(!!(~old & m_ALU & m_Nbit));
		}
		inline void     Set_IV(const uint32_t old)
		{
			// установка V - это смена значения знакового бита. с 1 на 0 (для вычитаний)
			SetV(!!(old & ~m_ALU & m_Nbit));
		}
		inline void     Set_C()
		{
			// установка C
			SetC(!!(m_ALU & (m_bByteOp ? 0xffffff00 : 0xffff0000)));
		}

};

extern CScriptAsm g_ScriptAsm;
