#include "JSON.h"
#include "Datei.h"

using namespace Framework;
using namespace JSON;

JSONValue::JSONValue()
	: ReferenceCounter()
{
	this->type = JSONType::NULL_;
}

JSONValue::~JSONValue()
{}

JSONValue::JSONValue(JSONType type)
	: ReferenceCounter()
{
	this->type = type;
}

JSONType JSONValue::getType() const
{
	return type;
}

Text JSONValue::toString() const
{
	return Text("null");
}

JSONBool* JSONValue::asBool() const {
	return (JSONBool*)this;
}

JSONNumber* JSONValue::asNumber() const {
	return (JSONNumber*)this;
}

JSONString* JSONValue::asString() const {
	return (JSONString*)this;
}

JSONArray* JSONValue::asArray() const {
	return (JSONArray*)this;
}

JSONObject* JSONValue::asObject() const {
	return (JSONObject*)this;
}


JSONBool::JSONBool(bool b)
	: JSONValue(JSONType::BOOLEAN)
{
	this->b = b;
}

bool JSONBool::getBool() const
{
	return b;
}

Text JSONBool::toString() const
{
	if (b)
		return Text("true");
	else
		return Text("false");
}


JSONNumber::JSONNumber(double num)
	: JSONValue(JSONType::NUMBER)
{
	number = num;
}

double JSONNumber::getNumber() const
{
	return number;
}

Text JSONNumber::toString() const
{
	return Text(number);
}


JSONString::JSONString(Text string)
	: JSONValue(JSONType::STRING)
{
	this->string = string;
}

Text JSONString::getString() const
{
	return string;
}

Text JSONString::toString() const
{
	return Text(Text("\"") += string.getText()) += "\"";
}


JSONArray::JSONArray()
	: JSONValue(JSONType::ARRAY)
{
	array = new RCArray< JSONValue >();
}

JSONArray::JSONArray(Text string)
	: JSONValue(JSONType::ARRAY)
{
	array = new RCArray< JSONValue >();
	string = Parser::removeWhitespace(string);
	if (string.getText()[0] == '[' && string.getText()[string.getLength() - 1] == ']')
	{
		string.remove(0, 1);
		string.remove(string.getLength() - 1, string.getLength());
		while (string.getLength())
		{
			int end = Parser::findObjectEndInArray(string);
			Text* objStr = string.getTeilText(0, end);
			string.remove(0, end + 1);
			array->add(Parser::getValue(*objStr));
			objStr->release();
		}
	}
}

JSONArray::JSONArray(const JSONArray& arr)
	: JSONValue(JSONType::ARRAY)
{
	array = dynamic_cast<RCArray<JSONValue> *>(arr.array->getThis());
}

JSONArray::~JSONArray()
{
	array->release();
}

JSONArray& JSONArray::operator=(const JSONArray& arr)
{
	array->release();
	array = dynamic_cast<RCArray<JSONValue> *>(arr.array->getThis());
	return *this;
}

void JSONArray::addValue(JSONValue* value)
{
	array->add(value);
}

JSONValue* JSONArray::getValue(int i) const
{
	return array->get(i);
}

int JSONArray::getLength() const
{
	return array->getEintragAnzahl();
}

bool JSONArray::isValueOfType(int i, JSONType type) const
{
	return i >= 0 && i < array->getEintragAnzahl() && array->z(i)->getType() == type;
}

Text JSONArray::toString() const
{
	Text str = "[";
	for (auto i = array->begin(); i; i++)
	{
		str += i->toString();
		if (i.hasNext())
			str += ",";
	}
	str += "]";
	return str;
}


JSONObject::JSONObject()
	: JSONValue(JSONType::OBJECT)
{
	fields = new Array< Text >();
	values = new RCArray< JSONValue >();
}

JSONObject::JSONObject(Text string)
	: JSONValue(JSONType::OBJECT)
{
	fields = new Array< Text >();
	values = new RCArray< JSONValue >();
	string = Parser::removeWhitespace(string);
	if (string.getText()[0] == '{' && string.getText()[string.getLength() - 1] == '}')
	{
		string.remove(0, 1);
		string.remove(string.getLength() - 1, string.getLength());
		while (string.getLength())
		{
			int endField = Parser::findFieldEndInObject(string);
			Text* fieldName = string.getTeilText(0, endField);
			string.remove(0, endField + 1);
			fieldName->remove(0, 1);
			fieldName->remove(fieldName->getLength() - 1, fieldName->getLength());
			int endValue = Parser::findValueEndInObject(string);
			Text* value = string.getTeilText(0, endValue);
			string.remove(0, endValue + 1);
			fields->add(Text(fieldName->getText()));
			values->add(Parser::getValue(*value));
			fieldName->release();
			value->release();
		}
	}
}

JSONObject::JSONObject(const JSONObject& obj)
	: JSONValue(JSONType::OBJECT)
{
	fields = dynamic_cast<Array<Text> *>(obj.fields->getThis());
	values = dynamic_cast<RCArray<JSONValue> *>(obj.values->getThis());
}

JSONObject::~JSONObject()
{
	fields->release();
	values->release();
}


JSONObject& JSONObject::operator=(const JSONObject& obj)
{
	fields->release();
	values->release();
	fields = dynamic_cast<Array<Text> *>(obj.fields->getThis());
	values = dynamic_cast<RCArray<JSONValue> *>(obj.values->getThis());
	return *this;
}


bool JSONObject::addValue(Text field, JSONValue* value)
{
	if (hasValue(field))
		return 0;
	fields->add(field);
	values->add(value);
	return 1;
}

bool JSONObject::removeValue(Text field)
{
	for (int i = 0; i < fields->getEintragAnzahl(); i++)
	{
		if (fields->get(i).istGleich(field))
		{
			fields->remove(i);
			values->remove(i);
			return 1;
		}
	}
	return 0;
}

bool JSONObject::hasValue(Text field)
{
	for (int i = 0; i < fields->getEintragAnzahl(); i++)
	{
		if (fields->get(i).istGleich(field))
			return 1;
	}
	return 0;
}

JSONValue* JSONObject::getValue(Text field)
{
	for (int i = 0; i < fields->getEintragAnzahl(); i++)
	{
		if (fields->get(i).istGleich(field))
			return values->get(i);
	}
	return new JSONValue();
}

Iterator< Text > JSONObject::getFields()
{
	return fields->begin();
}

Iterator< JSONValue* > JSONObject::getValues()
{
	return values->begin();
}

int JSONObject::getFieldCount() const
{
	return fields->getEintragAnzahl();
}

bool JSONObject::isValueOfType(Text field, JSONType type) const
{
	for (int i = 0; i < fields->getEintragAnzahl(); i++)
	{
		if (fields->get(i).istGleich(field))
			return values->z(i)->getType() == type;
	}
	return 0;
}

Text JSONObject::toString() const
{
	Text str = "{";
	Iterator< Text > k = fields->begin();
	for (auto v = values->begin(); k && v; k++, v++)
	{
		str += "\"";
		str += k._.getText();
		str += "\":";
		str += v->toString().getText();
		if (v.hasNext())
			str += ",";
	}
	str += "}";
	return str;
}

JSONValue* JSON::loadJSONFromFile(Text path)
{
	Datei d;
	d.setDatei(path);
	d.open(Datei::Style::lesen);
	int size = (int)d.getSize();
	char* buffer = new char[size + 1];
	buffer[size] = 0;
	d.lese(buffer, size);
	d.close();
	JSONValue* result = Parser::getValue(buffer);
	delete[] buffer;
	return result;
}

int Parser::findObjectEndInArray(const char* str)
{
	return findValueEndInObject(str);
}

Text Parser::removeWhitespace(const char* str)
{
	int wsc = 0;
	int i = 0;
	bool esc = 0;
	bool strO = 0;
	for (; str[i]; i++)
	{
		switch (str[i])
		{
		case '\\':
			if (strO)
				esc = !esc;
			else
				esc = 0;
			break;
		case '"':
			if (!esc)
				strO = !strO;
			esc = 0;
			break;
		case ' ':
		case '\n':
		case '\t':
		case '\r':
			if (!strO)
				wsc++;
			esc = 0;
			break;
		default:
			esc = 0;
			break;
		}
	}
	Text ret;
	ret.fillText(' ', i - wsc);
	i = 0;
	esc = 0;
	strO = 0;
	int index = 0;
	for (; str[i]; i++)
	{
		switch (str[i])
		{
		case '\\':
			if (strO)
				esc = !esc;
			else
				esc = 0;
			ret.getText()[index++] = str[i];
			break;
		case '"':
			if (!esc)
				strO = !strO;
			esc = 0;
			ret.getText()[index++] = str[i];
			break;
		case ' ':
		case '\n':
		case '\t':
		case '\r':
			if (strO)
				ret.getText()[index++] = str[i];
			esc = 0;
			break;
		default:
			ret.getText()[index++] = str[i];
			esc = 0;
			break;
		}
	}
	return ret;
}

JSONValue* Parser::getValue(const char* str)
{
	Text string = Parser::removeWhitespace(str);
	if (string.istGleich("true"))
		return new JSONBool(1);
	if (string.istGleich("false"))
		return new JSONBool(0);
	if (string.getText()[0] == '"')
	{
		string.remove(0, 1);
		string.remove(string.getLength() - 1, string.getLength());
		return new JSONString(string);
	}
	if (string.getText()[0] == '[')
		return new JSONArray(string);
	if (string.getText()[0] == '{')
		return new JSONObject(string);
	if (Text((int)string).istGleich(string.getText()))
		return new JSONNumber(string);
	if (string.anzahlVon('.') == 1)
	{
		bool isNumber = 1;
		for (char* c = (*string.getText() == '-') ? string.getText() + 1 : string.getText(); *c; c++)
			isNumber &= (*c >= '0' && *c <= '9') || *c == '.';
		if (isNumber)
			return new JSONNumber(string);
	}
	return new JSONValue();
}

int Parser::findFieldEndInObject(const char* str)
{
	int i = 0;
	bool esc = 0;
	bool strO = 0;
	int objOc = 0;
	int arrayOc = 0;
	for (; str[i]; i++)
	{
		switch (str[i])
		{
		case '\\':
			if (strO)
				esc = !esc;
			else
				esc = 0;
			break;
		case '"':
			if (!esc)
				strO = !strO;
			esc = 0;
			break;
		case '[':
			if (!strO)
				arrayOc++;
			esc = 0;
			break;
		case ']':
			if (!strO)
				arrayOc--;
			esc = 0;
			break;
		case '{':
			if (!strO)
				objOc++;
			esc = 0;
			break;
		case '}':
			if (!strO)
				objOc--;
			esc = 0;
			break;
		case ':':
			if (!strO && objOc == 0 && arrayOc == 0)
				return i;
			esc = 0;
			break;
		default:
			esc = 0;
			break;
		}
	}
	return i;
}

int Parser::findValueEndInObject(const char* str)
{
	int i = 0;
	bool esc = 0;
	bool strO = 0;
	int objOc = 0;
	int arrayOc = 0;
	for (; str[i]; i++)
	{
		switch (str[i])
		{
		case '\\':
			if (strO)
				esc = !esc;
			else
				esc = 0;
			break;
		case '"':
			if (!esc)
				strO = !strO;
			esc = 0;
			break;
		case '[':
			if (!strO)
				arrayOc++;
			esc = 0;
			break;
		case ']':
			if (!strO)
				arrayOc--;
			esc = 0;
			break;
		case '{':
			if (!strO)
				objOc++;
			esc = 0;
			break;
		case '}':
			if (!strO)
				objOc--;
			esc = 0;
			break;
		case ',':
			if (!strO && objOc == 0 && arrayOc == 0)
				return i;
			esc = 0;
			break;
		default:
			esc = 0;
			break;
		}
	}
	return i;
}