#include "pch.h"
#include "LabelDefs.h"
#include "LabelTableRef.h"
#include "Parser2.h"
#if defined _DEBUG
#include "StringUtil.h"
#endif
#pragma warning(disable:4996)

CRefsTable  g_RefsTable;        // таблица ссылок на метки, локальные и глобальные.

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


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

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

bool CRefsTable::AddRefs(const CBKToken *token, const int addr, uint32_t lt)
{
	// если токена нет, заменим его значением по умолчанию
	CBKToken tok = ((lt & ARL_DEFINITE_MASK) != ARL_DEFINE) ? CBKToken{} : (token ? *token : CBKToken{});

	if (g_RPNParser.GetLocNamesNum())
	{
		lt |= ARL_LOCAL;
	}

//	if (g_RPNParser.GetDotsNum())
//	{
//		lt |= ARL_DOTS;
//	}
	LabelRef lbl(lt, addr, &tok, g_RPNParser.GetRPN());
	RefsTable.push_back(lbl);
#if defined _DEBUG
	Dump();
#endif
	return true;
}

bool CRefsTable::AddRefs(const LabelRef &lbl)
{
	RefsTable.push_back(lbl);
#if defined _DEBUG
	Dump();
#endif
	return true;
}

uint32_t CRefsTable::GetType(const size_t n)
{
	if (n < getSize())
	{
		return RefsTable.at(n).getType();
	}

	return LBL_UNKNOWN;
}

void CRefsTable::DeleteElement(const size_t n)
{
	if (n < getSize()) // если номер в допустимых пределах
	{
		RefsTable.erase(RefsTable.begin() + n); // стираем
	}
}

// при добавлении глобальной метки, нужно пробежаться по таблице ссылок, и отметить
// все ссылки на локальные метки не в ветвлениях недействительными.
void CRefsTable::LockUndefinedLocalLabels()
{
	// просмотрим таблицу
	for (auto &ref : RefsTable)  // для всех ссылок на метки
	{
		const uint32_t lti = ref.getType();

		if ((lti & ARL_LOCAL) && ((lti & REFERENCE_TYPE_MASK) != ARL_BRANCH_LABEL))
		{
			RPNChain_t &rpn = ref.getRPN();

			for (auto &n : rpn)
			{
				if (n.type == A_TYPE::LOC_LABEL && n.number == uint32_t(-1)) // если значение метки было не определено
				{
					n.number = -2;  // то метка будет недействительна и это ошибка - ссылка не неопределённую метку.
				}
			}
		}
	}

#if defined _DEBUG
	Dump();
#endif
}

void CRefsTable::FixLocalLabels(const CBKToken *token, const int value)
{
	// просмотрим таблицу
	for (auto &ref : RefsTable) // для всех ссылок на метки
	{
		const uint32_t lti = ref.getType();

		if ((lti & ARL_LOCAL) && ((lti & REFERENCE_TYPE_MASK) != ARL_BRANCH_LABEL))
		{
			RPNChain_t &rpn = ref.getRPN();

			for (auto &n : rpn)
			{
				if (n.type == A_TYPE::LOC_LABEL && n.number == uint32_t(-1)) // если значение метки было не определено
				{
					if (n.token.getHash() == token->getHash())
					{
						n.number = value; // то метка теперь будет иметь такое значение
					}
				}
			}
		}
	}

#if defined _DEBUG
	Dump();
#endif
}

#if defined _DEBUG

void CRefsTable::SetDumpName(const fs::path &strDumpName)
{
	m_strDumpName = strDumpName;
}

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

	if (sz)
	{
		fprintf(fd, "Table size : %zd.\n", sz);
		fprintf(fd, "Type      Flags       Addr    Define.\n");

		for (auto &ref : RefsTable)
		{
			const uint32_t t = ref.getType();

			switch (t & ARL_DEFINITE_MASK)
			{
				case ARL_DEFINE:
					fprintf(fd, "Define    ");
					break;

				case ARL_CMDARG:
					fprintf(fd, "Argument  ");
					break;

				default:
					fprintf(fd, "ERROR!    ");
			}

			if (t & ARL_ABS)
			{
				fprintf(fd, "=");
			}
			else
			{
				fprintf(fd, " ");
			}

//			if (t & ARL_DOTS)
//			{
//				fprintf(fd, ".");
//			}
//			else
//			{
//				fprintf(fd, " ");
//			}

			if (t & ARL_LOCAL)
			{
				fprintf(fd, "L");
			}
			else
			{
				fprintf(fd, " ");
			}

			if (t & ARL_BYTEL)
			{
				fprintf(fd, "B");
			}
			else
			{
				fprintf(fd, " ");
			}

			switch (t & REFERENCE_TYPE_MASK)
			{
				case ARL_BRANCH_LABEL:
					fprintf(fd, " Branch   ");
					break;

				case ARL_OFFSET_LABEL:
					fprintf(fd, " Offset   ");
					break;

				case ARL_RELATIVE_LABEL:
					fprintf(fd, " Relative ");
					break;

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

			const int v = ref.getAddress();
			auto &tok = ref.getDefine();
			_ftprintf(fd, _T("%07o   %s\n"), (v & 0xffff), strUtil::_t_ws2s(tok.getName()).c_str());
			// Chain
			RPNChain_t chain = ref.getRPN();
			const auto sz1 = chain.size();

			if (sz1)
			{
				fprintf(fd, "%zd:\t", sz1);

				for (size_t n = 0; n < sz1; ++n)
				{
					const A_TYPE t = chain.at(n).type;

					switch (t)
					{
						case A_TYPE::NUMBER:
							fprintf(fd, "NUM:%07o ", chain.at(n).number & 0xffff);
							break;

						case A_TYPE::LABEL:
							_ftprintf(fd, _T("GL:%s "), strUtil::_t_ws2s(chain.at(n).token.getName()).c_str());
							break;

						case A_TYPE::LOC_LABEL:
							_ftprintf(fd, _T("LL:%s "), strUtil::_t_ws2s(chain.at(n).token.getName()).c_str());
							break;

						case A_TYPE::DOT_PC:
							fprintf(fd, "DOT:%07o ", chain.at(n).number & 0xffff);
							break;

						case A_TYPE::OPERATION:
							fprintf(fd, "OP:");

							switch (chain.at(n).op)
							{
								case A_OP::NOP:
									fprintf(fd, "nop ");
									break;

								case A_OP::UN_PLUS:
									fprintf(fd, "+u ");
									break;

								case A_OP::UN_MINUS:
									fprintf(fd, "-u ");
									break;

								case A_OP::UN_NOT:
									fprintf(fd, "~u ");
									break;

								case A_OP::PLUS:
									fprintf(fd, "+ ");
									break;

								case A_OP::MINUS:
									fprintf(fd, "- ");
									break;

								case A_OP::MUL:
									fprintf(fd, "* ");
									break;

								case A_OP::DIV:
									fprintf(fd, "/ ");
									break;

								case A_OP::DIV_FR:
									fprintf(fd, "%% ");
									break;

								case A_OP::SHL:
									fprintf(fd, "<< ");
									break;

								case A_OP::SHR:
									fprintf(fd, ">> ");
									break;

								case A_OP::OR:
									fprintf(fd, "| ");
									break;

								case A_OP::XOR:
									fprintf(fd, "^ ");
									break;

								case A_OP::AND:
									fprintf(fd, "& ");
									break;

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

							break;

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

				fprintf(fd, "\n");
			}
			else
			{
				fprintf(fd, "\tEmpty chain!\n");
			}
		}
	}
	else
	{
		fprintf(fd, "Empty.\n");
	}

	fclose(fd);
}

#endif
