#include "block.hpp"

#include <iostream>

static const char *MISSINGPARAM = "Missing parameter '%s'";

extern std::unordered_map<std::string, int> gReferenceIds;

int Block::GetInteger(const std::string &str) const
{
	if (properties.find(str) == properties.end()) {
		PrintErrorMsg(filepos, ERR_LVL_ERROR, MISSINGPARAM, str.c_str());
		return -1;
	}
	const token &asToken = properties.at(str);
	if (asToken.type != TOK_NUMBER || asToken.contents.find(".") != std::string::npos) {
		PrintErrorMsg(filepos, ERR_LVL_ERROR, "Expected %s to be an integer", str.c_str());
		return -1;
	}
	return std::stoi(asToken.contents, nullptr, 0);
}

int Block::GetOptionalInteger(const std::string &str, int def) const
{
	if (properties.find(str) != properties.end())
		return GetInteger(str);
	return def;
}

char Block::GetChar(const std::string &str) const
{
	if (properties.find(str) == properties.end())
	{
		PrintErrorMsg(filepos, ERR_LVL_ERROR, MISSINGPARAM, str.c_str());
		return -1;
	}
	const token &asToken = properties.at(str);
	if (asToken.type != TOK_NAME || asToken.contents.empty())
	{
		PrintErrorMsg(filepos, ERR_LVL_ERROR, "Expected %s to be a character", str.c_str());
		return -1;
	}
	return asToken.contents[0];
}

char Block::GetOptionalChar(const std::string &str, char def) const
{
	if (properties.find(str) != properties.end())
		return GetChar(str);
	return def;
}

float Block::GetFloat(const std::string &str) const
{
	if (properties.find(str) == properties.end()) {
		PrintErrorMsg(filepos, ERR_LVL_ERROR, MISSINGPARAM, str.c_str());
		return 0.0f;
	}
	const token &asToken = properties.at(str);
	if (asToken.type != TOK_NUMBER) {
		PrintErrorMsg(filepos, ERR_LVL_ERROR, "Expected %s to be a decimal number", str.c_str());
		return 0.0f;
	}
	return std::stof(asToken.contents);
}

float Block::GetOptionalFloat(const std::string &str, float def) const
{
	if (properties.find(str) != properties.end())
		return GetFloat(str);
	return def;
}

int Block::GetReference(const std::string &str, RefType type) const
{
	if (properties.find(str) == properties.end()) {
		PrintErrorMsg(filepos, ERR_LVL_ERROR, MISSINGPARAM, str.c_str());
		return -1;
	}
	const token &asToken = properties.at(str);
	if (asToken.type != TOK_REFERENCE) {
		PrintErrorMsg(filepos, ERR_LVL_ERROR, "Expected %s to be a reference", str.c_str());
		return -1;
	}
	return gReferenceIds[asToken.contents];
}

int Block::GetOptionalReference(const std::string &str, RefType type) const
{
	if (properties.find(str) == properties.end())
	{
		if (type == REF_STATE)
			return 0xffff;
		return 0;
	}
	const token &asToken = properties.at(str);
	if (asToken.type != TOK_REFERENCE)
	{
		PrintErrorMsg(filepos, ERR_LVL_ERROR, "Expected %s to be a reference", str.c_str());
		return -1;
	}
	if (gReferenceIds.find(asToken.contents) == gReferenceIds.end())
	{
		if (type == REF_STATE)
			return 0xffff;
		return 0;
	}
	return gReferenceIds[asToken.contents];
}

std::string Block::GetString(const std::string &str) const
{
	if (properties.find(str) == properties.end())
	{
		PrintErrorMsg(filepos, ERR_LVL_ERROR, MISSINGPARAM, str.c_str());
		return "";
	}
	const token &asToken = properties.at(str);
	if (asToken.type != TOK_STRING)
	{
		PrintErrorMsg(filepos, ERR_LVL_ERROR, "Expected %s to be a string", str.c_str());
		return "";
	}
	return asToken.contents;
}

int List::GetInteger(size_t index) const
{
	if (index > items.size())
	{
		PrintErrorMsg(filepos, ERR_LVL_ERROR, "Invalid number of parameters");
		return -1;
	}
	const token &asToken = items.at(index);
	if (asToken.type != TOK_NUMBER || asToken.contents.find(".") != std::string::npos)
	{
		PrintErrorMsg(filepos, ERR_LVL_ERROR, "Expected property #%d to be an integer", index + 1);
		return -1;
	}
	return std::stoi(asToken.contents, nullptr, 0);
}

int List::GetOptionalReference(size_t index, RefType type) const
{
	if (index > items.size())
	{
		PrintErrorMsg(filepos, ERR_LVL_ERROR, "Invalid number of parameters");
		return -1;
	}
	const token &asToken = items.at(index);
	if (asToken.type != TOK_NAME)
	{
		if (asToken.type == TOK_NUMBER && asToken.contents == "0")
		{
			if (type == REF_STATE)
				return 0xffff;
			return 0;
		}
		PrintErrorMsg(filepos, ERR_LVL_ERROR, "Expected property #%d to be a reference", index + 1);
		return -1;
	}
	if (gReferenceIds.find(asToken.contents) == gReferenceIds.end())
	{
		PrintErrorMsg(filepos, ERR_LVL_ERROR, "Unknown reference '%s'", asToken.contents.c_str());
		return 0;
	}
	return gReferenceIds[asToken.contents];
}

std::string List::GetName(size_t index) const
{
	if (index > items.size())
	{
		PrintErrorMsg(filepos, ERR_LVL_ERROR, "Invalid number of parameters");
		return "";
	}
	const token &asToken = items.at(index);
	if (asToken.type != TOK_NAME)
	{
		PrintErrorMsg(filepos, ERR_LVL_ERROR, "Expected property #%d to be a name", index + 1);
		return "";
	}
	return asToken.contents;
}
