#pragma once
#include "BKToken.h"

#define L_BRACKET L'<'
#define R_BRACKET L'>'

enum
{
	RPNERR_NOERR = 0,
	RPNERR_NONEUNOPR,	// нет операнда унарной операции
	RPNERR_NONERGTOP,	// нет операнда источника в двухоперандной операции
	RPNERR_NONELFTOP,	// нет операнда приёмника в двухоперандной операции
	RPNERR_OPRERR,		// неверная операция
	RPNERR_ARGERR,		// неверный тип операнда
	RPNERR_UNEXPECTEDEND, // неожиданный конец цепочки
	RPNERR_UNEXPECTEDARG, // лишние аргументы в выражении
};

class CRPNParser
{
	enum class A_OP : uint8_t
	{
		NOP,		// Нет операции
		UN_PLUS,	// унарный плюс				+ (по идее, должно игнорироваться)
		UN_MINUS,	// унарный минус			-
		UN_NOT,		// унарное нет				~
		PLUS,		// арифм.плюс				+
		MINUS,		// арифм.минус				-
		MUL,		// арифм.умножение			*
		DIV,		// арифм.деление			/
		DIV_FR,		// арифм.получение остатка	%
		SHL,		// арифм.сдвиг влево		<< (плохо - совпадает со скобками, но других символов нету)
		SHR,		// арифм.сдвиг вправо		>> (плохо - совпадает со скобками, но других символов нету)
		OR,			// бинарное или				|
		XOR,		// бинарное исключающее или	^
		AND			// бинарное и				&

	// в принципе можно сделать ещё и логические !, &&, || и операции сравнения <= >= < > == != 
	// но не знай как решить конфликт скобок.
	// или нужно два парсера. один с треугольными скобками для арифметических выражений
	// и второй с круглыми скобками для логическо-арифметических, чтобы использовать в условных операторах
	// больше им применения пока не находится
	};

	enum class A_TYPE : uint8_t
	{
		NUMBER,			// число
		LABEL,			// метка
		OPERATION		// операция
	};

	// структура узла записи арифметического выражения
	class A_NODE
	{
		public:
		A_TYPE type;	// тип узла
		A_OP op;		// вид операции
		// операнд, может быть число или метка
		uint32_t number;
		CBKToken token;
		public:
		A_NODE()
			: type(A_TYPE::NUMBER)
			, op(A_OP::NOP)
			, number(0)
		{}
		A_NODE(A_TYPE _t, A_OP _o)
			: type(_t)
			, op(_o)
			, number(0)
		{}
		A_NODE(A_TYPE _t, uint32_t _n)
			: type(_t)
			, op(A_OP::NOP)
			, number(_n)
		{}
		A_NODE(A_TYPE _t, CBKToken &_tok)
			: type(_t)
			, op(A_OP::NOP)
			, number(0)
			, token(_tok)
		{}
		void clear()
		{
			type = A_TYPE::NUMBER;
			op = A_OP::NOP;
			number = 0;
			token.clear();
		}

		void Store(FILE *f)
		{
			OBJTags tag = OBJTags::OBJ_Node;
			fwrite(&tag, 1, sizeof(tag), f);
			fwrite(&type, 1, sizeof(type), f);
			switch (type)
			{
				case A_TYPE::NUMBER:
					tag = OBJTags::OBJ_NodeNMR;
					fwrite(&tag, 1, sizeof(tag), f);
					fwrite(&number, 1, sizeof(number), f);
				break;
				case A_TYPE::LABEL:
					tag = OBJTags::OBJ_NodeLBL;
					fwrite(&tag, 1, sizeof(tag), f);
					token.Store(f);
					break;
				case A_TYPE::OPERATION:
					tag = OBJTags::OBJ_NodeOP;
					fwrite(&tag, 1, sizeof(tag), f);
					fwrite(&op, 1, sizeof(op), f);
				break;
			}
		}

		bool Load(FILE *f)
		{
			OBJTags tag;
			fread(&tag, 1, sizeof(tag), f);
			if (tag == OBJTags::OBJ_Node)
			{
				size_t r = fread(&type, 1, sizeof(type), f); //читаем тип операции
				if (r == sizeof(type))
				{
					switch (type)
					{
						case A_TYPE::NUMBER:
							fread(&tag, 1, sizeof(tag), f);
							if (tag == OBJTags::OBJ_NodeNMR)
							{
								op = A_OP::NOP;
								token.clear();
								r = fread(&number, 1, sizeof(number), f);
								if (r == sizeof(number))
								{
									return true;
								}
							}
							break;
						case A_TYPE::LABEL:
							fread(&tag, 1, sizeof(tag), f);
							if (tag == OBJTags::OBJ_NodeOP)
							{
								op = A_OP::NOP;
								number = 0;
								return token.Load(f);
							}
							break;
						case A_TYPE::OPERATION:
							fread(&tag, 1, sizeof(tag), f);
							if (tag == OBJTags::OBJ_NodeOP)
							{
								number = 0;
								token.clear();
								r = fread(&op, 1, sizeof(op), f);
								if (r == sizeof(op))
								{
									return true;
								}
							}
							break;
					}
				}
			}
			return false;
		}
	};

		std::vector<A_NODE> m_vRPNChain;
		int					m_nLastError;

		void PushNode(A_TYPE type, A_OP op);
		void PushNode(A_TYPE type, uint32_t num);
		void PushNode(A_TYPE type, CBKToken &token);

		bool isOROps(wchar_t ch);
		bool isANDOps(wchar_t ch);
		bool isADDOps(wchar_t ch);
		bool isMULOps(wchar_t ch);

		bool Expression(int &cp, wchar_t &ch);
		bool Term_log(int &cp, wchar_t &ch);
		bool Expr_ar(int &cp, wchar_t &ch);
		bool Term(int &cp, wchar_t &ch);
		bool Factor(int &cp, wchar_t &ch);

	public:
		CRPNParser::CRPNParser();
		CRPNParser::~CRPNParser();

		bool FullAriphmParser(int &cp, wchar_t &ch);
		bool CalcRpn(int &result);

		void Store(FILE *f);	// сохранение в объектный файл
		bool Load(FILE *f);		// чтение из объектного файла
};
