#pragma once
#include "BKToken.h"

// определения меток.
#define LABEL_DEFINITE_MASK     0x0000000F  // Маска для выделения типа метки
#define UNKNOWN_LABEL           0x00000000  // значение для ошибочных ситуаций
#define DEFINITE_LABEL          0x00000001  // описание локальной или глобальной метки
#define CONSTANT_LABEL          0x00000002  // описание константы (присваивания)
// определения ссылок на метки
#define LABEL_REFERENCE_MASK    0x000000F0  // Маска для выделения типа метки
#define BRANCH_LABEL            0x00000010  // метка в ветвлении
#define RELATIVE_LABEL          0x00000020  // метка в операнде
#define OFFSET_LABEL            0x00000030  // смещение до адреса
#define CURRENTPC_LABEL         0x00000040  // спец метка, для обработки '.' как текущего PC
// флаги операций
#define HALFLABEL_FLAG          0x00000100  // флаг деления пополам
#define NEGATIVE_FLAG           0x00000200  // флаг минуса
#define INVERSE_FLAG            0x00000400  // флаг инвертирования

class Label
{
	uint32_t    label_type;		// определение метки
	int         label_value;	// вычисленное значение метки
								// для ссылок на метку - адрес, где встретилась ссылка
	CBKToken    label;			// имя метки
public:
	Label() = default;
	Label(uint32_t lt, int lv, CBKToken *pt)
		: label_type(lt)
		, label_value(lv)
		, label(*pt) {}

	inline uint32_t getType() const
	{
		return label_type;
	}
	inline void setType(uint32_t t)
	{
		label_type = t;
	}
	inline int getValue() const
	{
		return label_value;
	}
	inline void setValue(int v)
	{
		label_value = v;
	}

	inline CBKToken &getToken()
	{
		return label;
	}

	inline CBKToken * getPToken()
	{
		return &label;
	}
	void Store(FILE *f)
	{
		OBJTags tag = OBJTags::OBJ_Label;
		fwrite(&tag, 1, sizeof(tag), f);
		label.Store(f);
		fwrite(&label_value, 1, sizeof(label_value), f);
		fwrite(&label_type, 1, sizeof(label_type), f);
	}

	bool Load(FILE *f)
	{
		OBJTags tag;
		fread(&tag, 1, sizeof(tag), f);
		if (tag == OBJTags::OBJ_Label)
		{
			if (label.Load(f))
			{
				fread(&label_value, 1, sizeof(label_value), f);
				fread(&label_type, 1, sizeof(label_type), f);
				return true;
			}
		}
		return false;
	}
};

// Класс для управления таблицей меток.
// необходимые функции - добавление, удаление, очистка таблицы, поиск и выдача результата.
class CLabelTable
{
	protected:
		std::vector <Label> LabelTable;

	public:
		CLabelTable();
		virtual ~CLabelTable();

		void               Clear();    // очистка массива меток
		inline size_t      getSize() const
		{
			return LabelTable.size();
		}

		virtual bool        AddLabel(CBKToken *token, int value, uint32_t lt);
		virtual bool        AddLabel(Label &lbl);
		virtual CBKToken   *GetLabel(size_t n);
		virtual void        DeleteLabel(CBKToken *token);
		virtual void        DeleteLabel(size_t n);
		virtual int         SearchLabel(CBKToken *token);
		virtual int         GetValue(CBKToken *token);
		virtual int         GetValue(size_t n);
		virtual uint32_t    GetType(CBKToken *token);
		virtual uint32_t    GetType(size_t n);
		virtual Label&      GetElement(size_t n)
		{
			return LabelTable.at(n);
		}
};

// класс для таблицы ссылок. главное отличие - в таблице может быть неограниченное количество
// ссылок на одну и ту же метку.
class CLabelTableRefs : public CLabelTable
{
	public:
		CLabelTableRefs();
		virtual ~CLabelTableRefs() override;
		virtual bool        AddLabel(CBKToken *token, int value, uint32_t lt) override;
		virtual bool        AddLabel(Label &lbl) override;
};

