#include "pch.h"
#include "LabelDefs.h"
#include "LabelTable.h"
#if defined _DEBUG
#include "StringUtil.h"
#endif

#pragma warning(disable:4996)

CLabelTable g_labelGlobalDefs;  // таблица глобальных меток
CLabelTable g_labelLocalDefs;   // таблица локальных меток, должна очищаться каждый раз при добавлении метки в таблицу глобальных меток


CLabelTable::CLabelTable() noexcept
{
#if defined _DEBUG
	m_strDumpName = _T("_labeltable.txt");
#endif
}


CLabelTable::~CLabelTable()
{
	Clear();
}

void CLabelTable::Clear()
{
	LabelTable.clear();
#if defined _DEBUG
	Dump();
#endif
}

// выход: true - метка добавлена.
//      false - метка не добавлена, потому что такая уже есть
bool CLabelTable::AddLabel(CBKToken *token, const int value, const uint32_t lt)
{
	if (SearchLabel(token) == -1) // если такая метка не найдена
	{
		// добавляем
		Label lbl{ lt, value, token };
		LabelTable.push_back(lbl);
#if defined _DEBUG
		Dump();
#endif
		return true;
	}

	return false; // если метка уже есть - сообщим об этом
}

bool CLabelTable::AddLabel(Label &lbl)
{
	if (SearchLabel(lbl.getPToken()) == -1) // если такая метка не найдена
	{
		// добавляем
		LabelTable.push_back(lbl);
#if defined _DEBUG
		Dump();
#endif
		return true;
	}

	return false; // если метка уже есть - сообщим об этом
}

/*
Выдача имени метки по номеру
*/
CBKToken *CLabelTable::GetLabel(const size_t n)
{
	if (n < getSize()) // если номер в допустимых пределах
	{
		return LabelTable.at(n).getPToken(); // выдаём
	}

	return nullptr; // иначе null
}

/*
Удаление метки из таблицы по номеру
*/
void CLabelTable::DeleteLabel(const size_t n)
{
	if (n < getSize()) // если номер в допустимых пределах
	{
		LabelTable.erase(LabelTable.begin() + n); // стираем
#if defined _DEBUG
		Dump();
#endif
	}
}

/*
Удаление метки из таблицы по имени
*/
void CLabelTable::DeleteLabel(const CBKToken *token)
{
	const int n = SearchLabel(token); // ищем метку

	if (n >= 0) // если нашли
	{
		DeleteLabel(n); // удаляем по номеру
	}
}

/*
поиск метки в таблице определений, если не найдено, возвращает -1
иначе - номер позиции.
*/
int CLabelTable::SearchLabel(const CBKToken *token)
{
	const size_t sz = getSize();

	for (size_t i = 0; i < sz; ++i)
	{
		const CBKToken &lt = LabelTable.at(i).getToken();

		if (lt.getHash() == token->getHash())
		{
#if defined _DEBUG

			if (lt.getName() != token->getName())
			{
				std::wstring strOut = std::format(L"HASH Error: {0:}:{1:#x} ~~ {2:}:{3:#x}\n",
				                                  lt.getName(), lt.getHash(), token->getName(), token->getHash());
				std::_tcout << strUtil::_t_ws2s(strOut);
				assert(false);
			}

#endif
			return static_cast<int>(i);
		}
	}

	return -1; // ничего не найдено
}

/*
Выдача значения метки по имени
*/
int CLabelTable::GetValue(const CBKToken *token)
{
	const int n = SearchLabel(token); // ищем метку

	if (n >= 0) // если нашли
	{
		return LabelTable.at(n).getValue(); // возвращаем её значение
	}

	return -1; // если не нашли
}

/*
Выдача значения метки по номеру
*/
int CLabelTable::GetValue(const size_t n)
{
	if (n < getSize()) // если номер в допустимых пределах
	{
		return LabelTable.at(n).getValue(); // возвращаем её значение
	}

	return -1; // если не нашли
}

/*
Выдача типа метки по имени
*/
uint32_t CLabelTable::GetType(const CBKToken *token)
{
	const int n = SearchLabel(token);

	if (n >= 0)
	{
		return LabelTable.at(n).getType();
	}

	return LBL_UNKNOWN;
}

/*
Выдача типа метки по номеру
*/
uint32_t CLabelTable::GetType(const size_t n)
{
	if (n < getSize())
	{
		return LabelTable.at(n).getType();
	}

	return LBL_UNKNOWN;
}

#if defined _DEBUG
void CLabelTable::SetDumpName(const fs::path &strDumpName)
{
	m_strDumpName = strDumpName;
}

void CLabelTable::Dump()
{
	FILE *fd = _tfopen(m_strDumpName._tstring().c_str(), _T("wt"));
	const auto sz = LabelTable.size();

	if (sz)
	{
		fprintf(fd, "Table size : %zd.\n", sz);
		fprintf(fd, "Type      Value     Name.\n");

		for (auto &lbl : LabelTable)
		{
			const uint32_t t = lbl.getType();

			switch (t & LBL_DEFINITE_MASK)
			{
				case LBL_UNKNOWN:
					fprintf(fd, "UNKNOWN ");
					break;

				case LBL_GLOBAL:
					fprintf(fd, "Global  ");
					break;

				case LBL_LOCAL:
					fprintf(fd, "Local   ");
					break;

				case LBL_DEFINE:
					fprintf(fd, "Define  ");
					break;

				case LBL_WEAKDEFINE:
					fprintf(fd, "*Define ");
					break;
			}

			switch (t & LBL_FLAGS_MASK)
			{
				case LBL_ABSVALUE:
					fprintf(fd, "A ");
					break;

				default:
					fprintf(fd, "  ");
					break;
			}

			const int v = lbl.getValue();
			auto &tok = lbl.getToken();
			_ftprintf(fd, _T("%07o   %s\n"), (v & 0xffff), strUtil::_t_ws2s(tok.getName()).c_str());
		}
	}
	else
	{
		fprintf(fd, "Empty.\n");
	}

	fclose(fd);
}
#endif
