#include "pch.h"
#include "ErrorManager.h"
#include "Globals.h"
#include "Listing.h"
#include "StringUtil.h"
#include <chrono>

#pragma warning(disable:4996)

static const ErrorManager::BKTError_t g_Errors[] =
{
	{ ERRNUM::E_101, L"Псевдокоманда .LA должна быть первой в тексте." },
	{ ERRNUM::E_102, L"Ошибка длины или направления перехода в команде SOB." },
	{ ERRNUM::E_103, L"Недопустимый символ в строке / Синтаксическая ошибка." },
	{ ERRNUM::E_104, L"Ошибка или отсутствие числового аргумента." },
	{ ERRNUM::E_105, L"Неправильная псевдокоманда." },
	{ ERRNUM::E_106, L"Неправильная ассемблерная инструкция." },
	{ ERRNUM::E_107, L"Отсутствует символ после \'." },
	{ ERRNUM::E_108, L"Отсутствуют или недостаточно символов после \"." },
	{ ERRNUM::E_109, L"Ошибка длины перехода по оператору ветвления." },
	{ ERRNUM::E_110, L"Ошибка аргумента MARK." },
	{ ERRNUM::E_111, L"Ошибка в имени регистра." },
	{ ERRNUM::E_112, L"Ошибка в псевдокоманде." },
	{ ERRNUM::E_113, L"Метка уже определена ранее." },
	{ ERRNUM::E_114, L"Аргумент .BLKB слишком велик." },
	{ ERRNUM::E_115, L"Аргумент .BLKW слишком велик." },
	{ ERRNUM::E_116, L"Аргумент .ORG слишком велик." },
	{ ERRNUM::E_117, L"Нечётный адрес команды." },
	{ ERRNUM::E_118, L"Отсутствует .END" },
	{ ERRNUM::E_119, L"Неопределённая метка в непосредственном выражении." },
	{ ERRNUM::E_120, L"Отсутствует переход у команды SOB." },
	{ ERRNUM::E_121, L"Ошибка аргумента TRAP." },
	{ ERRNUM::E_122, L"Ошибка аргумента EMT." },
	{ ERRNUM::E_123, L"Ошибка или неверный метод адресации." },
	{ ERRNUM::E_124, L"Отсутствует второй операнд." },
	{ ERRNUM::E_125, L"Переполнение байтового аргумента." },
	{ ERRNUM::E_126, L"Переполнение словного аргумента." },
	{ ERRNUM::E_127, L"Неожиданный конец строкового аргумента." },
	{ ERRNUM::E_128, L"Ошибка в числе с плавающей точкой." },
	{ ERRNUM::E_129, L"Невозможно открыть файл include или insert." },
	{ ERRNUM::E_130, L"Ошибка в аргументах псевдокоманды." },
	{ ERRNUM::E_131, L"Ошибка в числовом аргументе." },
	{ ERRNUM::E_132, L"Ошибка в имени регистра FPU." },
	{ ERRNUM::E_133, L".ENDS без .SCRIPT." },
	{ ERRNUM::E_134, L"Неожиданный конец строки." },
	{ ERRNUM::E_135, L"Вставляемый элемент слишком велик." },
	{ ERRNUM::E_136, L"Аргумент .ALIGN слишком велик." },
};

int ErrorManager::IsError()
{
	return g_Globals.GetError();
}

/*
вывод сообщения об ошибке.
n - номер ошибки
bOutStr = true - вывести строку, в которой случилась ошибка,
*/
void ErrorManager::OutError(ERRNUM n, bool bOutStr, int nAddr)
{
	FILE *errf = fopen("_errors.txt", "a");

	if (g_Globals.GetError() == 0) // если первый вызов в этой сессии
	{
		const auto t = std::chrono::system_clock::now();
		std::_tstring s1 = std::format(_T("{0:%F} {0:%T}"), t);

		if (errf)
		{
			_ftprintf(errf, _T("\n\n--------------------------- %s -------------------------------\n\n"), s1.c_str());
		}
	}

	/*
	Тут в общем такая ситуация:
	nAddr введён только для того, чтобы правильно отслеживать ошибку длины перехода
	по ветвлению вперёд.
	Во всех случаях nAddr == -1 == значение по умолчанию.
	и только в одном единственном случае возникновения ошибки перехода по ветвлению вперёд
	nAddr - это адрес команды, которая вызвала ошибку 109

	!!! Если возникнет ещё какая нибудь такая ситуация, придётся всё переделывать.

	*/
	auto &error = g_Errors[n - ERRNUM::E_101];
	std::wstring errbuf2 = std::format(L"Error {:3d}: ", static_cast<int>(error.num)) + error.str + L"\n";

	// при линковке листинг не создаётся, и тут получается всё весьма хреново.
	if (g_Listing.empty())
	{
		if (nAddr == -1)
		{
			nAddr = g_Globals.GetPC();
		}

		auto errbuf = std::format(_T("Line none (Addr: {:07o})"), g_Globals.GetRealAddress(nAddr));
		std::_tcout << errbuf << _T(" - ");
		std::_tcout << strUtil::_t_ws2s(errbuf2);

		if (errf)
		{
			_ftprintf(errf, _T("%s :: %s - %ls"), g_pReader->GetFileName()._tstring().c_str(), errbuf.c_str(), errbuf2.c_str());
			fclose(errf);
		}

		g_Globals.SetError(g_Globals.GetError() + 1);
		return;
	}

	auto pll = g_Listing.end() - 1;

	if (nAddr == -1)
	{
		nAddr = g_Globals.GetPC();
	}
	else if (n == E_109)
	{
		// нужно теперь найти, в какое место вставить текст ошибки в листинге
		bool bEx = false;

		for (auto ll = g_Listing.begin(); ll != g_Listing.end(); ++ll)
		{
			for (const auto &lct : ll->vCMD)
			{
				if (lct.nPC == nAddr && lct.LT == ListType::LT_INSTRUCTION)
				{
					pll = ll;
					bEx = true;
					break;
				}
			}

			if (bEx)
			{
				break;
			}
		}
	}

	// и приятный бонус. теперь и тут всё правильно отображается для ошибки 109 по переходу вперёд.
	auto errbuf = std::format(_T("Line {:d} (Addr: {:07o})"), pll->nLineNum, g_Globals.GetRealAddress(nAddr));
	pll->errors.emplace_back(errbuf2); // добавляем в листинг ошибку
	std::_tcout << errbuf << _T(" - ");
	std::_tcout << strUtil::_t_ws2s(errbuf2);

	if (errf)
	{
		_ftprintf(errf, _T("%s :: %s - %ls"), g_pReader->GetFileName()._tstring().c_str(), errbuf.c_str(), errbuf2.c_str());
	}

#if (_DEBUG && DEBUG_LABEL_MANAGER)
	// /-----------------------debug---------------------------
	_ftprintf(dbgF, _T("%s"), errbuf.data());
	// /-----------------------debug---------------------------
#endif

	// если нужно - ещё и строку покажем, где ошибка
	if (bOutStr)
	{
		std::_tcout << strUtil::_t_ws2s(pll->line) << std::endl;

		if (errf)
		{
			_ftprintf(errf, _T("%ls\n"), strUtil::_t_ws2s(pll->line).c_str());
		}

#if (_DEBUG && DEBUG_LABEL_MANAGER)
		// /-----------------------debug---------------------------
		_ftprintf(dbgF, _T("%ls\n"), pll->line.c_str());
		// /-----------------------debug---------------------------
#endif
	}

	if (errf)
	{
		fclose(errf);
	}

	g_Globals.SetError(g_Globals.GetError() + 1);
}

