#include "pch.h"
#include "Parser2.h"
#include "Parser.h"
#include "Globals.h"
#include "LabelManager.h"
#include "ErrorManager.h"
#pragma warning(disable:4996)
#pragma warning(disable:4146)

// !!! Осталось придумать, как сохранять выражение в объектный файл.


CRPNParser::CRPNParser()
	: m_nLastError(0)
{}

CRPNParser::~CRPNParser()
= default;

// разбор полноценного арифметического выражения
// получение записи в обратной польской нотации
bool CRPNParser::FullAriphmParser(int &cp, wchar_t &ch)
{
	m_vRPNChain.clear(); //очищаем запись
	return Expression(cp, ch);
}

void CRPNParser::PushNode(A_TYPE type, A_OP op)
{
	A_NODE node(type, op);
	m_vRPNChain.push_back(node);
}
void CRPNParser::PushNode(A_TYPE type, uint32_t num)
{
	A_NODE node(type, num);
	m_vRPNChain.push_back(node);
}
void CRPNParser::PushNode(A_TYPE type, CBKToken& token)
{
	A_NODE node(type, token);
	m_vRPNChain.push_back(node);
}

bool CRPNParser::isOROps(wchar_t ch)
{
	return (ch == L'|' || ch == L'^');
}
bool CRPNParser::isANDOps(wchar_t ch)
{
	return (ch == L'&');
}
bool CRPNParser::isADDOps(wchar_t ch)
{
	return (ch == L'+' || ch == L'-');
}
bool CRPNParser::isMULOps(wchar_t ch)
{
	if (ch == L'*' || ch == L'/' || ch == L'%')
	{
		return true;
	}
	if (ch == L'<' && g_pReader->getNextChar() == ch)
	{
		g_pReader->readChar();
		return true;
	}
	if (ch == L'>' && g_pReader->getNextChar() == ch)
	{
		g_pReader->readChar();
		return true;
	}
	return false;
}


// начало.
bool CRPNParser::Expression(int &cp, wchar_t &ch)
{
	if (Term_log(cp, ch))
	{
		g_pReader->SkipWhitespaces(ch);
		while (g_pReader->getCurrCharType() != CReader::CHAR_TYPE::LN && isOROps(ch))
		{
			wchar_t op = ch;
			ch = g_pReader->readChar(true);
			if (!Term_log(cp, ch))
			{
				// какая-то ошибка
				return false;
			}
			switch (op)
			{
				case L'|':
					PushNode(A_TYPE::OPERATION, A_OP::OR);
					break;
				case L'^':
					PushNode(A_TYPE::OPERATION, A_OP::XOR);
					break;
			}
		}
		return true;
	}
	return false;
}

bool CRPNParser::Term_log(int &cp, wchar_t &ch)
{
	if (Expr_ar(cp, ch))
	{
		g_pReader->SkipWhitespaces(ch);
		while (g_pReader->getCurrCharType() != CReader::CHAR_TYPE::LN && isANDOps(ch))
		{
			wchar_t op = ch;
			ch = g_pReader->readChar(true);
			if (!Expr_ar(cp, ch))
			{
				// какая-то ошибка
				return false;
			}
			switch (op)
			{
				case L'&':
					PushNode(A_TYPE::OPERATION, A_OP::AND);
					break;
			}
		}
		return true;
	}
	return false;
}
bool CRPNParser::Expr_ar(int &cp, wchar_t &ch)
{
	if (Term(cp, ch))
	{
		g_pReader->SkipWhitespaces(ch);
		while (g_pReader->getCurrCharType() != CReader::CHAR_TYPE::LN && isADDOps(ch))
		{
			wchar_t op = ch;
			ch = g_pReader->readChar(true);
			if (!Term(cp, ch))
			{
				// какая-то ошибка
				return false;
			}
			switch (op)
			{
				case L'+':
					PushNode(A_TYPE::OPERATION, A_OP::PLUS);
					break;
				case L'-':
					PushNode(A_TYPE::OPERATION, A_OP::MINUS);
					break;
			}
		}
		return true;
	}
	return false;
}
bool CRPNParser::Term(int &cp, wchar_t &ch)
{
	if (Factor(cp, ch))
	{
		g_pReader->SkipWhitespaces(ch);
		while (g_pReader->getCurrCharType() != CReader::CHAR_TYPE::LN && isMULOps(ch))
		{
			wchar_t op = ch;
			ch = g_pReader->readChar(true);
			if (!Factor(cp, ch))
			{
				// какая-то ошибка
				return false;
			}

			switch (op)
			{
				case L'*':
					PushNode(A_TYPE::OPERATION, A_OP::MUL);
					break;
				case L'/':
					PushNode(A_TYPE::OPERATION, A_OP::DIV);
					break;
				case L'%':
					PushNode(A_TYPE::OPERATION, A_OP::DIV_FR);
					break;
				case L'>':
					PushNode(A_TYPE::OPERATION, A_OP::SHR);
					break;
				case L'<':
					PushNode(A_TYPE::OPERATION, A_OP::SHL);
					break;
			}
		}

		return true;
	}

	return false;
}

bool CRPNParser::Factor(int &cp, wchar_t &ch)
{
	if (g_pReader->SkipWhitespaces(ch))
	{
		// конец строки
		// Ошибка - конец строки там, где неположено, пока не придумаю, как определить конец арифметического выражения
		// в многострочной записи, конец строки будет ошибкой
		return false;
	}

	switch (ch)
	{
	case L'+':
	{
		// унарный плюс
		ch = g_pReader->readChar(true); // получим следующий символ
		bool bRes = Factor(cp, ch);
		PushNode(A_TYPE::OPERATION, A_OP::UN_PLUS);
		return bRes;
	}
	case L'-':
	{
		// унарный минус
		ch = g_pReader->readChar(true); // получим следующий символ
		bool bRes = Factor(cp, ch);
		PushNode(A_TYPE::OPERATION, A_OP::UN_MINUS);
		return bRes;
	}
	case L'~':
	{
		// унарный нет - это будет простое инвертирование, а не логическое не
		ch = g_pReader->readChar(true); // получим следующий символ
		bool bRes = Factor(cp, ch);
		PushNode(A_TYPE::OPERATION, A_OP::UN_NOT);
		return bRes;
	}
	case L_BRACKET:
		// открывающая скобка
		ch = g_pReader->readChar(true); // получим следующий символ
		if (Expression(cp, ch))
		{
			// если выражение распарсилось без ошибок
			if (!g_pReader->SkipWhitespaces(ch))
			{
				// если не случился внезапный конец строки
				if (ch == R_BRACKET)
				{
					// если закрывающая скобка на месте
					ch = g_pReader->readChar(true); // прочитаем скобку
					return true; // то только тогда всё в порядке
				}
			}
		}
		//синтаксическая ошибка
		return false;
	}

	bool bMakeInvert = false;
l_bgn:
	if (g_pReader->getCurrCharType() == CReader::CHAR_TYPE::LETTERS)
	{
		// имя метки
		CBKToken label;
		bool bRes = g_pReader->readToken(&label, ch);
		PushNode(A_TYPE::LABEL, label);
		if (bMakeInvert)
		{
			PushNode(A_TYPE::OPERATION, A_OP::UN_NOT);
		}
		return bRes;
	}
	else if (ch == L'\'')
	{
		// получение одного символа
		g_GlobalParameters.bInString = true;
		ch = g_pReader->readChar(true);
		g_GlobalParameters.bInString = false;

		if (g_pReader->getCurrCharType() == CReader::CHAR_TYPE::LN)
		{
			::OutError(ERROR_108); // Ошибка или отсутствие числового аргумента.
			return false; // стоп компиляция.
		}

		int number = UNICODEtoBK_ch(ch);
		if (bMakeInvert)
		{
			number = ~number;
		}
		PushNode(A_TYPE::NUMBER, number);

		ch = g_pReader->readChar(); // нужно прочитать ещё один раз, чтобы подвинуть курсор
		return true;
	}
	else if (ch == L'"')
	{
		// получение двух символов
		g_GlobalParameters.bInString = true;
		ch = g_pReader->readChar();
		g_GlobalParameters.bInString = false;

		if (g_pReader->getCurrCharType() == CReader::CHAR_TYPE::LN)
		{
			::OutError(ERROR_109); // Ошибка или отсутствие числового аргумента.
			return false; // стоп компиляция.
		}

		int number = UNICODEtoBK_ch(ch);

		g_GlobalParameters.bInString = true;
		ch = g_pReader->readChar(true);
		g_GlobalParameters.bInString = false;

		if (g_pReader->getCurrCharType() == CReader::CHAR_TYPE::LN)
		{
			::OutError(ERROR_109); // Ошибка или отсутствие числового аргумента.
			return false; // стоп компиляция.
		}

		number |= uint16_t(UNICODEtoBK_ch(ch)) << 8;
		if (bMakeInvert)
		{
			number = ~number;
		}
		PushNode(A_TYPE::NUMBER, number);

		ch = g_pReader->readChar(); // нужно прочитать ещё один раз, чтобы подвинуть курсор
		return true;
	}
	else if (ch == L'.') // если текущий символ .
	{
		ch = g_pReader->readChar(true); // получаем следующий символ

		/*
		Тут надо сделать, чтобы возвращалось не значение, а в таблицу добавлялась
		особая ссылка на метку
		*/
		CBKToken lbl(L"..PC."); // TODO: привести в соответствие как в других местах
		PushNode(A_TYPE::LABEL, lbl);
		if (bMakeInvert)
		{
			PushNode(A_TYPE::OPERATION, A_OP::UN_NOT);
		}
		return true;
	}
	else if (ch == L'^') // если текущий символ ^
	{
		bool bInvert = false; // флаг команды ^С
		int result = 0;
		if (::Macro11NumberParser(result, ch, bInvert))
		{
			// если была команда ^C, то начинаем сначала
			if (bInvert)
			{
				if (g_pReader->SkipWhitespaces(ch)) // пропустим пустые символы
				{
					::OutError(ERROR_105); // Ошибка или отсутствие числового аргумента.
					return false; // стоп компиляция.
				}
				bMakeInvert = !bMakeInvert;
				goto l_bgn;
			}

			if (bMakeInvert)
			{
				result = ~result;
			}

			PushNode(A_TYPE::NUMBER, result);
			return true;
		}
	}
	else
	{
		int result = 0;
		if (::AdvancedNumberParser(result, ch))
		{
			// !!! в макро11 отсутствие числового или вообще любого аргумента интерпретируется значением по умолчанию.
			// а здесь аргумент должен присутствовать.
			if (bMakeInvert)
			{
				result = ~result;
			}

			PushNode(A_TYPE::NUMBER, result);
			return true;
		}
	}
	// синтаксическая ошибка
	::OutError(ERROR_105); // Ошибка или отсутствие числового аргумента.
	return false; // стоп компиляция.
}

// вычисление, или нет, если есть неопределённые метки.
// выход: true - удалось вычислить цепочку, result - достоверный результат
//		false - или не удалось, или ошибка.
//		если result == 0 - то есть ещё в цепочке неопределённая метка
//		если result == -1 - то ошибка в цепочке, и вычислить её невозможно
bool CRPNParser::CalcRpn(int& result)
{
	std::vector<A_NODE> vStack; // стек элементов

	try
	{
		size_t len = m_vRPNChain.size();

		for (size_t i = 0; i < len; ++i) // для всей записи
		{
			switch (m_vRPNChain.at(i).type)
			{
				case A_TYPE::OPERATION:  // если узел - операция
				{
					//выполним операцию
					switch (m_vRPNChain.at(i).op)
					{
						case A_OP::UN_PLUS:

							if (!vStack.empty())
							{
								A_NODE a = vStack.back();
								vStack.pop_back();
								vStack.push_back(a);
								break;
							}
							m_nLastError = RPNERR_NONEUNOPR;
							throw; // ошибка в цепочке

						case A_OP::UN_MINUS:

							if (!vStack.empty())
							{
								A_NODE a = vStack.back();
								vStack.pop_back();
								a.number = -a.number;
								vStack.push_back(a);
								break;
							}
							m_nLastError = RPNERR_NONEUNOPR;
							throw; // ошибка в цепочке

						case A_OP::UN_NOT:

							if (!vStack.empty())
							{
								A_NODE a = vStack.back();
								vStack.pop_back();
								a.number = ~a.number;
								vStack.push_back(a);
								break;
							}
							m_nLastError = RPNERR_NONEUNOPR;
							throw; // ошибка в цепочке

						case A_OP::PLUS:

							if (!vStack.empty())
							{
								A_NODE a = vStack.back();
								vStack.pop_back();
								if (!vStack.empty())
								{
									A_NODE b = vStack.back();
									vStack.pop_back();
									b.number += a.number;
									vStack.push_back(b);
									break;
								}
								m_nLastError = RPNERR_NONELFTOP;
							}
							else
							{
								m_nLastError = RPNERR_NONERGTOP;
							}

							throw; // ошибка в цепочке

						case A_OP::MINUS:

							if (!vStack.empty())
							{
								A_NODE a = vStack.back();
								vStack.pop_back();
								if (!vStack.empty())
								{
									A_NODE b = vStack.back();
									vStack.pop_back();
									b.number -= a.number;
									vStack.push_back(b);
									break;
								}
								m_nLastError = RPNERR_NONELFTOP;
							}
							else
							{
								m_nLastError = RPNERR_NONERGTOP;
							}

							throw; // ошибка в цепочке

						case A_OP::MUL:

							if (!vStack.empty())
							{
								A_NODE a = vStack.back();
								vStack.pop_back();
								if (!vStack.empty())
								{
									A_NODE b = vStack.back();
									vStack.pop_back();
									b.number *= a.number;
									vStack.push_back(b);
									break;
								}
								m_nLastError = RPNERR_NONELFTOP;
							}
							else
							{
								m_nLastError = RPNERR_NONERGTOP;
							}

							throw; // ошибка в цепочке

						case A_OP::DIV:

							if (!vStack.empty())
							{
								A_NODE a = vStack.back();
								vStack.pop_back();
								if (!vStack.empty())
								{
									A_NODE b = vStack.back();
									vStack.pop_back();
									b.number /= a.number;
									vStack.push_back(b);
									break;
								}
								m_nLastError = RPNERR_NONELFTOP;
							}
							else
							{
								m_nLastError = RPNERR_NONERGTOP;
							}

							throw; // ошибка в цепочке

						case A_OP::DIV_FR:

							if (!vStack.empty())
							{
								A_NODE a = vStack.back();
								vStack.pop_back();
								if (!vStack.empty())
								{
									A_NODE b = vStack.back();
									vStack.pop_back();
									b.number %= a.number;
									vStack.push_back(b);
									break;
								}
								m_nLastError = RPNERR_NONELFTOP;
							}
							else
							{
								m_nLastError = RPNERR_NONERGTOP;
							}

							throw; // ошибка в цепочке

						case A_OP::SHL:

							if (!vStack.empty())
							{
								A_NODE a = vStack.back();
								vStack.pop_back();
								if (!vStack.empty())
								{
									A_NODE b = vStack.back();
									vStack.pop_back();
									b.number <<= a.number;
									vStack.push_back(b);
									break;
								}
								m_nLastError = RPNERR_NONELFTOP;
							}
							else
							{
								m_nLastError = RPNERR_NONERGTOP;
							}

							throw; // ошибка в цепочке

						case A_OP::SHR:

							if (!vStack.empty())
							{
								A_NODE a = vStack.back();
								vStack.pop_back();
								if (!vStack.empty())
								{
									A_NODE b = vStack.back();
									vStack.pop_back();
									b.number >>= a.number;
									vStack.push_back(b);
									break;
								}
								m_nLastError = RPNERR_NONELFTOP;
							}
							else
							{
								m_nLastError = RPNERR_NONERGTOP;
							}

							throw; // ошибка в цепочке

						case A_OP::OR:

							if (!vStack.empty())
							{
								A_NODE a = vStack.back();
								vStack.pop_back();
								if (!vStack.empty())
								{
									A_NODE b = vStack.back();
									vStack.pop_back();
									b.number |= a.number;
									vStack.push_back(b);
									break;
								}
								m_nLastError = RPNERR_NONELFTOP;
							}
							else
							{
								m_nLastError = RPNERR_NONERGTOP;
							}

							throw; // ошибка в цепочке

						case A_OP::XOR:

							if (!vStack.empty())
							{
								A_NODE a = vStack.back();
								vStack.pop_back();
								if (!vStack.empty())
								{
									A_NODE b = vStack.back();
									vStack.pop_back();
									b.number ^= a.number;
									vStack.push_back(b);
									break;
								}
								m_nLastError = RPNERR_NONELFTOP;
							}
							else
							{
								m_nLastError = RPNERR_NONERGTOP;
							}

							throw; // ошибка в цепочке

						case A_OP::AND:

							if (!vStack.empty())
							{
								A_NODE a = vStack.back();
								vStack.pop_back();
								if (!vStack.empty())
								{
									A_NODE b = vStack.back();
									vStack.pop_back();
									b.number &= a.number;
									vStack.push_back(b);
									break;
								}
								m_nLastError = RPNERR_NONELFTOP;
							}
							else
							{
								m_nLastError = RPNERR_NONERGTOP;
							}

							throw; // ошибка в цепочке

						default:
							m_nLastError = RPNERR_OPRERR;
							throw; // ошибка в цепочке
					}
				}
				break;

				case A_TYPE::LABEL:
				{
					// если метка - поищем её в таблице меток
					int p = g_labelGlobalDefs.SearchLabel(&m_vRPNChain.at(i).token);
					if (p >= 0) // если метка найдена
					{
						int value = g_labelGlobalDefs.GetValue(p);
						A_NODE a{ A_TYPE::NUMBER, static_cast<uint32_t>(value) };
						vStack.push_back(a);	// поместим в стек значение метки
					}
					else
					{
						// если не найдена - то дальнейшие вычисления невозможны
						// (хотя, в приципе, если есть скобки или вычисления высшего приоритета,
						// то и там можно повычислять, но что-то сложновато выходит, проще застопориться
						// на первой встреченной неопределённой метке.)
						// всё оставшееся - в стек
						for (size_t j = i; j < len; ++j)
						{
							vStack.push_back(m_vRPNChain.at(j));
						}
						m_vRPNChain.clear();
						m_vRPNChain = vStack; // теперь это наша новая цепочка
						result = 0;
						return false;
					}
				}
				break;

				case A_TYPE::NUMBER:
				{
					// если число - поместим его в стек
					vStack.push_back(m_vRPNChain.at(i));
				}
				break;

				default:
				{
					assert(false);
					m_nLastError = RPNERR_ARGERR;
					throw; // прям вообще ошибка. Других значений быть не должно
				}
			}
		}

		// если попали сюда, то вся цепочка вычислена
		if (!vStack.empty()) // результат - узел в стеке
		{
			A_NODE a = vStack.back();
			vStack.pop_back();
			result = a.number; // достаём
			if (!vStack.empty()) // если в стеке ещё что-то осталось
			{
				m_nLastError = RPNERR_UNEXPECTEDARG;
				throw; // ошибка в цепочке
			}
			return true;	// выходим с корректным результатом
		}

		m_nLastError = RPNERR_UNEXPECTEDEND;
		throw; // ошибка в цепочке
	}
	catch (...)
	{
		m_vRPNChain.clear(); //очищаем запись
		vStack.clear();
		// тут будем обрабатывать ситуацию с ошибкой в цепочке
		result = -1;	// что делать с такой цепочкой, будем решать в другом месте
						// вообще говоря - это будет означать, что алгоритм преобразования выражения
						// в RPN глючит
		return false;
	}
}

void CRPNParser::Store(FILE *f)
{
	auto sz = static_cast<uint32_t>(m_vRPNChain.size());
	OBJTags tag = OBJTags::OBJ_RPNChain;
	fwrite(&tag, 1, sizeof(tag), f);
	fwrite(&sz, 1, sizeof(sz), f);

	for (uint32_t i = 0; i < sz; ++i)
	{
		m_vRPNChain.at(i).Store(f);
	}
}

bool CRPNParser::Load(FILE *f)
{
	OBJTags tag;
	fread(&tag, 1, sizeof(tag), f);
	if (tag == OBJTags::OBJ_RPNChain)
	{
		m_vRPNChain.clear(); //очищаем
		uint32_t sz = 0;
		size_t r = fread(&sz, 1, sizeof(sz), f);
		if (r == sizeof(sz))
		{
			for (uint32_t i = 0; i < sz; ++i)
			{
				A_NODE node;
				bool bRes = node.Load(f);
				if (bRes)
				{
					m_vRPNChain.push_back(node);
				}
				else
				{
					return false;
				}
			}
			// если размер вектора 0 - то это не будем считать ошибкой, так записали.
			return true;
		}
	}

	return false;
}

// нужно:
// 1. сохранять эти векторы в таблице неопределённых меток как объекты-ссылки, требующие вычислений
// 2. сохранять и восстанавливать их в/из бинарного файла.
//  вот это что-то пока непонятнее всего как сделать.
// нужно завести отдельную таблицу, где будут сохраняться такие цепочки.

