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

#pragma warning(disable:4996)

const std::map<ERRNUM, ErrorManager::BKTError_t> g_Errors =
{
	{ ERRNUM::E_OK,  {ErrRet::Ok,       ErrorManager::ErrT::W, L"Нет ошибок." }},
	{ ERRNUM::E_101, {ErrRet::Continue, ErrorManager::ErrT::W, L"Псевдокоманда .LA должна быть первой в тексте." }},        // предупреждение, игнорить и продолжать
	{ ERRNUM::E_102, {ErrRet::Continue, ErrorManager::ErrT::E, L"Ошибка длины или направления перехода в команде SOB." }}, // ошибка, не фатальная, можно продолжать
	{ ERRNUM::E_103, {ErrRet::Fatal,    ErrorManager::ErrT::E, L"Недопустимый символ в строке / Синтаксическая ошибка." }}, // ошибка, фатально, продолжать нельзя
	{ ERRNUM::E_104, {ErrRet::Fatal,    ErrorManager::ErrT::E, L"Ошибка или отсутствие числового аргумента." }},           // ошибка, фатально, продолжать нельзя
	{ ERRNUM::E_105, {ErrRet::Fatal,    ErrorManager::ErrT::E, L"Неправильная псевдокоманда." }},                          // ошибка, фатально, продолжать нельзя
	{ ERRNUM::E_106, {ErrRet::Fatal,    ErrorManager::ErrT::E, L"Неправильная ассемблерная инструкция." }},                   // ошибка, фатально, продолжать нельзя
	{ ERRNUM::E_107, {ErrRet::Continue, ErrorManager::ErrT::E, L"" }},  // не используется
	{ ERRNUM::E_108, {ErrRet::Continue, ErrorManager::ErrT::E, L"" }},  // не используется
	{ ERRNUM::E_109, {ErrRet::Continue, ErrorManager::ErrT::E, L"Ошибка длины перехода по оператору ветвления." }},          // ошибка, не фатальная, можно продолжать
	{ ERRNUM::E_110, {ErrRet::Fatal,    ErrorManager::ErrT::E, L"Нет закрывающей '>' в числовом аргументе псевдокоманды .ASCII/.ASCIIZ." }},   // ошибка, фатально, продолжать нельзя
	{ ERRNUM::E_111, {ErrRet::Fatal,    ErrorManager::ErrT::E, L"Ошибка в имени регистра." }},                              // ошибка, фатально, продолжать нельзя
	{ ERRNUM::E_112, {ErrRet::Fatal,    ErrorManager::ErrT::E, L"Ошибка в псевдокоманде." }},                               // ошибка, фатально, продолжать нельзя
	{ ERRNUM::E_113, {ErrRet::Continue, ErrorManager::ErrT::W, L"Метка уже определена ранее." }},                            // предупреждение, игнорить и продолжать
	{ ERRNUM::E_114, {ErrRet::Fatal,    ErrorManager::ErrT::E, L"Аргумент .BLKB слишком велик." }},                         // ошибка, фатально, продолжать нельзя
	{ ERRNUM::E_115, {ErrRet::Fatal,    ErrorManager::ErrT::E, L"Аргумент .BLKW слишком велик." }},                         // ошибка, фатально, продолжать нельзя
	{ ERRNUM::E_116, {ErrRet::Fatal,    ErrorManager::ErrT::E, L"Аргумент .ORG слишком велик." }},                          // ошибка, фатально, продолжать нельзя
	{ ERRNUM::E_117, {ErrRet::Continue, ErrorManager::ErrT::E, L"Нечётный адрес команды." }},                               // ошибка, не фатально, можно продолжать (выравниваем адрес 0)
	{ ERRNUM::E_118, {ErrRet::Fatal,    ErrorManager::ErrT::E, L"Отсутствует .END" }},                                       // ошибка, фатально, продолжать нельзя
	{ ERRNUM::E_119, {ErrRet::Continue, ErrorManager::ErrT::E, L"Неопределённая метка в непосредственном выражении." }},   // ошибка, не фатальная, можно продолжать (подставляем 0)
	{ ERRNUM::E_120, {ErrRet::Continue, ErrorManager::ErrT::E, L"Отсутствует переход у команды SOB." }},                  // ошибка, не фатальная, можно продолжать (подставляем 0)
	{ ERRNUM::E_121, {ErrRet::Continue, ErrorManager::ErrT::E, L"" }},  // не используется
	{ ERRNUM::E_122, {ErrRet::Continue, ErrorManager::ErrT::E, L"" }},  // не используется
	{ ERRNUM::E_123, {ErrRet::Fatal,    ErrorManager::ErrT::E, L"Ошибка или неверный метод адресации." }},                   // ошибка, фатально, продолжать нельзя
	{ ERRNUM::E_124, {ErrRet::Fatal,    ErrorManager::ErrT::E, L"Отсутствует второй операнд." }},                           // ошибка, фатально, продолжать нельзя
	{ ERRNUM::E_125, {ErrRet::Continue, ErrorManager::ErrT::W, L"Переполнение байтового аргумента." }},                       // предупреждение, игнорить и продолжать
	{ ERRNUM::E_126, {ErrRet::Continue, ErrorManager::ErrT::W, L"Переполнение словного аргумента." }},                     // предупреждение, игнорить и продолжать
	{ ERRNUM::E_127, {ErrRet::Continue, ErrorManager::ErrT::E, L"Неожиданный конец строкового аргумента." }},                // ошибка, не фатальная, можно продолжать
	{ ERRNUM::E_128, {ErrRet::Fatal,    ErrorManager::ErrT::E, L"Ошибка в числе с плавающей точкой." }},                    // ошибка, фатально, продолжать нельзя
	{ ERRNUM::E_129, {ErrRet::Continue, ErrorManager::ErrT::E, L"Невозможно открыть файл include." }},                     // ошибка, не фатальная, можно продолжать
	{ ERRNUM::E_130, {ErrRet::Continue, ErrorManager::ErrT::E, L"Невозможно открыть файл insert." }},                      // ошибка, не фатальная, можно продолжать
	{ ERRNUM::E_131, {ErrRet::Continue, ErrorManager::ErrT::E, L"Ошибка в числовом аргументе." }},                          // ошибка, не фатальная, можно продолжать (подставляем 0)
	{ ERRNUM::E_132, {ErrRet::Fatal,    ErrorManager::ErrT::E, L"Ошибка в имени регистра FPU." }},                          // ошибка, фатально, продолжать нельзя
	{ ERRNUM::E_133, {ErrRet::Continue, ErrorManager::ErrT::W, L".ENDS без .SCRIPT." }},                                 // предупреждение, игнорить и продолжать
	{ ERRNUM::E_134, {ErrRet::Continue, ErrorManager::ErrT::E, L"Неожиданный конец строки." }},                               // ошибка, не фатальная, можно продолжать
	{ ERRNUM::E_135, {ErrRet::Fatal,    ErrorManager::ErrT::E, L"Вставляемый элемент слишком велик." }},                  // ошибка, фатально, продолжать нельзя
	{ ERRNUM::E_136, {ErrRet::Fatal,    ErrorManager::ErrT::E, L"Аргумент .ALIGN слишком велик." }},                        // ошибка, фатально, продолжать нельзя
	{ ERRNUM::E_137, {ErrRet::Fatal,    ErrorManager::ErrT::E, L"Отсутствует закрывающая скобка." }},                       // ошибка, фатально, продолжать нельзя
	{ ERRNUM::E_138, {ErrRet::Fatal,    ErrorManager::ErrT::E, L"В выражении нельзя использовать скобки." }},             // ошибка, фатально, продолжать нельзя
	{ ERRNUM::E_139, {ErrRet::Continue, ErrorManager::ErrT::W, L"Ошибка разбора арифметического выражения." }},                // предупреждение, игнорить и продолжать
};

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

/*
вывод сообщения об ошибке.
Вход: n - номер ошибки
      bOutStr = true - вывести строку, в которой случилась ошибка
      nAddr - адрес для команд ветвления
Выход: true - можно продолжать, false - стоп
*/
bool ErrorManager::OutError(const ERRNUM n, const 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

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

	*/
	const auto &error = g_Errors.at(n);
	std::wstring errbuf2 = (error.et == ErrT::W) ? L"Warning" : L"Error";
	errbuf2 += std::format(L" {:3d}: ", static_cast<int>(n)) + 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);
		}

		if (error.et == ErrT::W)
		{
			g_Globals.SetWarning(g_Globals.GetWarning() + 1);
		}
		else
		{
			g_Globals.SetError(g_Globals.GetError() + 1);
		}

		return (error.ret == ErrRet::Fatal) ? false : true;
	}

	auto pll = g_Listing.end() - 1;

	if (nAddr == -1)
	{
		nAddr = g_Globals.GetPC();
	}
	else if (n == ERRNUM::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)
		{
#if defined (_UNICODE)
			_ftprintf(errf, _T("%ls\n"), strUtil::_t_ws2s(pll->line).c_str());
#else
			_ftprintf(errf, _T("%s\n"), strUtil::_t_ws2s(pll->line).c_str());
#endif
		}

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

	if (errf)
	{
		fclose(errf);
	}

	if (error.et == ErrT::W)
	{
		g_Globals.SetWarning(g_Globals.GetWarning() + 1);
	}
	else
	{
		g_Globals.SetError(g_Globals.GetError() + 1);
	}

	return (error.ret == ErrRet::Fatal) ? false : true;
}

