#include "pch.h"
#include "BKMemory.h"
#include "BKToken.h"
#include "Globals.h"
#include "ConvW2k.h" // там константы

GlobalParams g_Globals;

CReader *g_pReader = nullptr; // текущий обрабатываемый файл

std::vector <Registers_t> g_RegNames =
{
	{0, {CBKToken(L"R0"), CBKToken(L"%0")} },
	{1, {CBKToken(L"R1"), CBKToken(L"%1")} },
	{2, {CBKToken(L"R2"), CBKToken(L"%2")} },
	{3, {CBKToken(L"R3"), CBKToken(L"%3")} },
	{4, {CBKToken(L"R4"), CBKToken(L"%4")} },
	{5, {CBKToken(L"R5"), CBKToken(L"%5")} },
	{6, {CBKToken(L"R6"), CBKToken(L"%6"), CBKToken(L"SP")} }, //список должен быть таким, для скриптов берётся только первое имя из списка
	{7, {CBKToken(L"R7"), CBKToken(L"%7"), CBKToken(L"PC")} },
	// это я задумал ввести поддержку присвоения именам регистров пользовательских значений,
	// но что-то пока непонятно, как сделать обращения к регистрам через %n и зачем вообще это надо делать
};

// инициализация всех параметров
void GlobalParams::InitGlobalParameters()
{
	m_nFlags = 0;
	m_nStartAddress = DEFAULT_START_ADDRESS;
	g_inputKOI8 = &g_koi8tbl_RFC1489;	// по умолчанию входной таблицей будет стандартная КОИ8
	g_outputKOI8 = &g_koi8tblBK10;		// по умолчанию выходной будет КОИ8 БК10

	ReInitGlobalParameters();
}
// переинициализация некоторых параметров.
void GlobalParams::ReInitGlobalParameters()
{
	m_nFlags &= ~(BKT_GLBL_INSCRIPT | BKT_GLBL_INSTRING | BKT_GLBL_ENDREACHED | BKT_GLBL_STOPONERROR);
	m_nLinkAddr = BASE_ADDRESS;
	m_nOperandType = OPERAND_TYPE::SRC;
	m_nLinkMode = LINKING_MODE::FINAL;
	m_nArithmeticType = 0;
	m_nPC = BASE_ADDRESS;
	m_nProgramLength = 0;
	m_nErrors = 0;
	m_nWarnings = 0;
	g_Memory.b.fill(0);
}


#define SET_FLAG(b, f) if(b){m_nFlags |= (f);}else{m_nFlags &= ~(f);}
#define GET_FLAG(f) !!(m_nFlags & (f));
// установить флаг выдачи больше информации на экран
void GlobalParams::SetVerbosity(const bool b) noexcept
{
	SET_FLAG(b, BKT_GLBL_VERBOSITY);
}
// флаг выдачи больше информации на экран
bool GlobalParams::isVerbosity() const noexcept
{
	return GET_FLAG(BKT_GLBL_VERBOSITY);
}
// задать, что мы находимся внутри текста скрипта
void GlobalParams::SetInScript(const bool b) noexcept
{
	SET_FLAG(b, BKT_GLBL_INSCRIPT);
}
// узнать, что мы находимся внутри текста скрипта
bool GlobalParams::isInScript() const noexcept
{
	return GET_FLAG(BKT_GLBL_INSCRIPT);
}
// задать, что мы находимся внутри текстовой строки
void GlobalParams::SetInString(const bool b) noexcept
{
	SET_FLAG(b, BKT_GLBL_INSTRING);
}
// узнать, что мы находимся внутри текстовой строки
bool GlobalParams::isInString() const noexcept
{
	return GET_FLAG(BKT_GLBL_INSTRING);
}
// установить флаг, что встретилась команда .END
void GlobalParams::SetENDReached(const bool b) noexcept
{
	SET_FLAG(b, BKT_GLBL_ENDREACHED);
}
// узнать, встретилась ли команда .END
bool GlobalParams::isENDReached() const noexcept
{
	return GET_FLAG(BKT_GLBL_ENDREACHED);
}
// установить флаг остановки компиляции при возникновении ошибки
void GlobalParams::SetStopOnError(const bool b) noexcept
{
	SET_FLAG(b, BKT_GLBL_STOPONERROR);
}
// узнать, останавливать ли компиляцию при возникновении ошибки
bool GlobalParams::isStopOnError() const noexcept
{
	return GET_FLAG(BKT_GLBL_STOPONERROR);
}
// задать адрес линковки
void GlobalParams::SetLinkerAddr(const int n) noexcept
{
	m_nLinkAddr = n;
}
// выдать адрес линковки
int GlobalParams::GetLinkerAddr() const noexcept
{
	return m_nLinkAddr;
}
// задать флаг входа в новую инклуду
void GlobalParams::SetStepInclude(const bool b) noexcept
{
	SET_FLAG(b, BKT_GLBL_STEPINCLUDE);
}
// узнать, что мы вошли в новую инклуду
bool GlobalParams::isStepInclude() const noexcept
{
	return GET_FLAG(BKT_GLBL_STEPINCLUDE);
}
// задать флаг, что мы обрабатываем инклуду
void GlobalParams::SetInInclude(const bool b) noexcept
{
	SET_FLAG(b, BKT_GLBL_ININCLUDE);
}
// узнать, что мы обрабатываем инклуду
bool GlobalParams::isInInclude() const noexcept
{
	return GET_FLAG(BKT_GLBL_ININCLUDE);
}

void GlobalParams::SetEnabl(const bool b) noexcept
{
	SET_FLAG(b, BKT_GLBL_SETENABL);
}

bool GlobalParams::isEnabl() const noexcept
{
	return GET_FLAG(BKT_GLBL_SETENABL);
}

void GlobalParams::GrowLinkObjLength() noexcept
{
	m_nProgramLength += m_nLinkObjLen;
	m_nLinkAddr += m_nLinkObjLen;
	m_nLinkObjLen = 0;
}

int GlobalParams::GetRealAddress(const int addr) const noexcept
{
	return (addr - BASE_ADDRESS + m_nStartAddress) & 0xffff;
}
int GlobalParams::CorrectOffset(const int addr) const noexcept
{
	return (addr - BASE_ADDRESS - m_nStartAddress) & 0xffff;
}

bool GlobalParams::FindReader(const fs::path &strFileName)
{
	// поищем, вдруг уже добавляли эту инклуду
	for (const auto rdr : m_vReader)
	{
		if (rdr && rdr->GetFileName() == strFileName)
		{
			return true; // добавляли
		}
	}

	// теперь поищем среди всех ранее включавшихся
	for (const auto &s : m_vAddIncludes)
	{
		if (s == strFileName)
		{
			return true; // уже включали
		}
	}

	// нет, такое ещё точно не добавляли
	m_vAddIncludes.push_back(strFileName.wstring());
	return false;
}

void GlobalParams::PushReader(CReader *r)
{
	m_vReader.emplace_back(r);

	if (r)
	{
		SET_FLAG(true, BKT_GLBL_STEPINCLUDE | BKT_GLBL_ININCLUDE);
	}
}

CReader *GlobalParams::PopReader() noexcept
{
	CReader *ret = nullptr;

	if (!m_vReader.empty()) // если в векторе что-то есть
	{
		ret = m_vReader.back(); // достаём
		m_vReader.pop_back();

		if (ret)
		{
			SET_FLAG(false, BKT_GLBL_ENDREACHED);
		}
	}

	return ret;
}

// преобразуем строку восьмеричных цифр в число
// имеют значение младшие 16 бит.

int GlobalParams::StrToDec(const std::string &str)
{
	if (!str.empty())
	{
		std::string s = str;
		const auto sz = s.length();

		if (sz > 9) // в инт влазит только 9 цифр
		{
			s = s.substr(sz - 9);   // оставим младшие 9 цифр
		}

		return std::stol(s, nullptr, 10);
	}

	return 0;
}

int GlobalParams::StrToDec(const std::wstring &str)
{
	if (!str.empty())
	{
		std::wstring s = str;
		const auto sz = s.length();

		if (sz > 9) // в инт влазит только 9 цифр
		{
			s = s.substr(sz - 9);   // оставим младшие 9 цифр
		}

		return std::stol(s, nullptr, 10);
	}

	return 0;
}


// преобразуем строку восьмеричных цифр в число
// имеют значение младшие 16 бит.
int GlobalParams::StrToOct(const std::string &str)
{
	if (!str.empty())
	{
		std::string s = str;
		const auto sz = s.length();

		if (sz > 10)    // в инт влазит только 10 цифр
		{
			s = s.substr(sz - 10);  // оставим младшие 10 цифр
		}

		return std::stol(s, nullptr, 8);
	}

	return 0;
}

int GlobalParams::StrToOct(const std::wstring &str)
{
	if (!str.empty())
	{
		std::wstring s = str;
		const auto sz = s.length();

		if (sz > 10)    // в инт влазит только 10 цифр
		{
			s = s.substr(sz - 10);  // оставим младшие 10 цифр
		}

		return std::stol(s, nullptr, 8);
	}

	return 0;
}

