#include "pch.h"
#include "CppUnitTest.h"
#include <Json.h>
#define NO_MAIN
#include <main.h>

using namespace Microsoft::VisualStudio::CppUnitTestFramework;

namespace FrameworkTests
{
	TEST_CLASS(JSONParserTests)
	{
	public:
		TEST_METHOD(NullTest)
		{
			Framework::JSON::JSONValue* value = Framework::JSON::Parser::getValue("null");
			Assert::IsTrue(value != 0, L"Framework::JSON::Parser::getValue('null') should not return 0");
			Assert::IsTrue(value->getType() == Framework::JSON::JSONType::NULL_, L"Framework::JSON::Parser::getValue('null') should return a json null value");
			value->release();
		}

		TEST_METHOD(BooleanTest)
		{
			Framework::JSON::JSONValue* value = Framework::JSON::Parser::getValue("false");
			Assert::IsTrue(value != 0, L"Framework::JSON::Parser::getValue('false') should not return 0");
			Assert::IsTrue(value->getType() == Framework::JSON::JSONType::BOOLEAN, L"Framework::JSON::Parser::getValue('false') should return a boolean");
			Assert::IsTrue(((Framework::JSON::JSONBool*)value)->getBool() == false, L"Framework::JSON::Parser::getValue('false') should return a boolean with value false");
			value->release();

			value = Framework::JSON::Parser::getValue("true");
			Assert::IsTrue(value != 0, L"Framework::JSON::Parser::getValue('true') should not return 0");
			Assert::IsTrue(value->getType() == Framework::JSON::JSONType::BOOLEAN, L"Framework::JSON::Parser::getValue('true') should return a boolean");
			Assert::IsTrue(((Framework::JSON::JSONBool*)value)->getBool() == true, L"Framework::JSON::Parser::getValue('true') should return a boolean with value true");
			value->release();
		}

		TEST_METHOD(StringTest)
		{
			Framework::JSON::JSONValue* value = Framework::JSON::Parser::getValue("\"test\"");
			Assert::IsTrue(value != 0, L"Framework::JSON::Parser::getValue('\"test\"') should not return 0");
			Assert::IsTrue(value->getType() == Framework::JSON::JSONType::STRING, L"Framework::JSON::Parser::getValue('\"test\"') should return a string");
			Assert::IsTrue(((Framework::JSON::JSONString*)value)->getString().istGleich("test"), L"Framework::JSON::Parser::getValue('\"test\"') should return a string with value 'test'");
			value->release();

			value = Framework::JSON::Parser::getValue("\"\"");
			Assert::IsTrue(value != 0, L"Framework::JSON::Parser::getValue('\"\"') should not return 0");
			Assert::IsTrue(value->getType() == Framework::JSON::JSONType::STRING, L"Framework::JSON::Parser::getValue('\"\"') should return a string");
			Assert::IsTrue(((Framework::JSON::JSONString*)value)->getString().istGleich(""), L"Framework::JSON::Parser::getValue('\"\"') should return a string with value ''");
			value->release();
		}

		TEST_METHOD(NumberTest)
		{
			Framework::JSON::JSONValue* value = Framework::JSON::Parser::getValue("0");
			Assert::IsTrue(value != 0, L"Framework::JSON::Parser::getValue('0') should not return 0");
			Assert::IsTrue(value->getType() == Framework::JSON::JSONType::NUMBER, L"Framework::JSON::Parser::getValue('0') should return a number");
			Assert::IsTrue(((Framework::JSON::JSONNumber*)value)->getNumber() == 0.0, L"Framework::JSON::Parser::getValue('0') should return a number with value '0'");
			value->release();

			value = Framework::JSON::Parser::getValue("1.5");
			Assert::IsTrue(value != 0, L"Framework::JSON::Parser::getValue('1.5') should not return 0");
			Assert::IsTrue(value->getType() == Framework::JSON::JSONType::NUMBER, L"Framework::JSON::Parser::getValue('1.5') should return a number");
			Assert::IsTrue(((Framework::JSON::JSONNumber*)value)->getNumber() == 1.5, L"Framework::JSON::Parser::getValue('1.5') should return a number with value '1.5'");
			value->release();

			value = Framework::JSON::Parser::getValue("-1.5");
			Assert::IsTrue(value != 0, L"Framework::JSON::Parser::getValue('-1.5') should not return 0");
			Assert::IsTrue(value->getType() == Framework::JSON::JSONType::NUMBER, L"Framework::JSON::Parser::getValue('-1.5') should return a number");
			Assert::IsTrue(((Framework::JSON::JSONNumber*)value)->getNumber() == -1.5, L"Framework::JSON::Parser::getValue('-1.5') should return a number with value '-1.5'");
			value->release();

			value = Framework::JSON::Parser::getValue("-5.0");
			Assert::IsTrue(value != 0, L"Framework::JSON::Parser::getValue('-5.0') should not return 0");
			Assert::IsTrue(value->getType() == Framework::JSON::JSONType::NUMBER, L"Framework::JSON::Parser::getValue('-5.0') should return a number");
			Assert::IsTrue(((Framework::JSON::JSONNumber*)value)->getNumber() == -5.0, L"Framework::JSON::Parser::getValue('-5.0') should return a number with value '-5.0'");
			value->release();
		}

		TEST_METHOD(ArrayTest)
		{
			Framework::JSON::JSONValue* value = Framework::JSON::Parser::getValue("[]");
			Assert::IsTrue(value != 0, L"Framework::JSON::Parser::getValue('[]') should not return 0");
			Assert::IsTrue(value->getType() == Framework::JSON::JSONType::ARRAY, L"Framework::JSON::Parser::getValue('[]') should return an array");
			Assert::IsTrue(((Framework::JSON::JSONArray*)value)->getLength() == 0, L"Framework::JSON::Parser::getValue('[]') should return an array with length 0");
			value->release();

			value = Framework::JSON::Parser::getValue("  \t[ \r\n\tnull   , \r\n\t  1,true , \"\"  ] ");
			Assert::IsTrue(value != 0, L"Framework::JSON::Parser::getValue('[null, 1, true, \"\"]') should not return 0");
			Assert::IsTrue(value->getType() == Framework::JSON::JSONType::ARRAY, L"Framework::JSON::Parser::getValue('[null, 1, true, \"\"]') should return an array");
			Assert::IsTrue(((Framework::JSON::JSONArray*)value)->getLength() == 4, L"Framework::JSON::Parser::getValue('[null, 1, true, \"\"]') should return an array with length 4");
			Assert::IsTrue(((Framework::JSON::JSONArray*)value)->isValueOfType(0, Framework::JSON::JSONType::NULL_), L"Framework::JSON::Parser::getValue('[null, 1, true, \"\"]') should contain null at index 0");
			Assert::IsTrue(((Framework::JSON::JSONArray*)value)->isValueOfType(1, Framework::JSON::JSONType::NUMBER), L"Framework::JSON::Parser::getValue('[null, 1, true, \"\"]') should contain a number at index 1");
			Assert::IsTrue(((Framework::JSON::JSONArray*)value)->isValueOfType(2, Framework::JSON::JSONType::BOOLEAN), L"Framework::JSON::Parser::getValue('[null, 1, true, \"\"]') should contain a boolean at index 2");
			Assert::IsTrue(((Framework::JSON::JSONArray*)value)->isValueOfType(3, Framework::JSON::JSONType::STRING), L"Framework::JSON::Parser::getValue('[null, 1, true, \"\"]') should contain a boolean at index 3");
			value->release();
		}

		TEST_METHOD(MultipleArrayTest)
		{
			Framework::JSON::JSONValue* value = Framework::JSON::Parser::getValue("[[1],2,[[]]]");
			Assert::IsTrue(value != 0, L"Framework::JSON::Parser::getValue('[[1],2,[[]]]') should not return 0");
			Assert::IsTrue(value->getType() == Framework::JSON::JSONType::ARRAY, L"Framework::JSON::Parser::getValue('[[1],2,[[]]]') should return an array");
			Assert::IsTrue(((Framework::JSON::JSONArray*)value)->getLength() == 3, L"Framework::JSON::Parser::getValue('[[1],2,[[]]]') should return an array with length 3");
			Assert::IsTrue(((Framework::JSON::JSONArray*)value)->isValueOfType(0, Framework::JSON::JSONType::ARRAY), L"Framework::JSON::Parser::getValue('[null, 1, true, \"\"]') should contain an array at index 0");
			Assert::IsTrue(((Framework::JSON::JSONArray*)value)->isValueOfType(1, Framework::JSON::JSONType::NUMBER), L"Framework::JSON::Parser::getValue('[null, 1, true, \"\"]') should contain a number at index 1");
			Assert::IsTrue(((Framework::JSON::JSONArray*)value)->isValueOfType(2, Framework::JSON::JSONType::ARRAY), L"Framework::JSON::Parser::getValue('[null, 1, true, \"\"]') should contain an array at index 2");
			value->release();
		}

		TEST_METHOD(ObjectTest)
		{
			Framework::JSON::JSONValue* value = Framework::JSON::Parser::getValue("{\" \": []}");
			Assert::IsTrue(value != 0, L"Framework::JSON::Parser::getValue('{\"\": []}') should not return 0");
			Assert::IsTrue(value->getType() == Framework::JSON::JSONType::OBJECT, L"Framework::JSON::Parser::getValue('{\" \": []}') should return an object");
			Assert::IsTrue(((Framework::JSON::JSONObject*)value)->getFieldCount() == 1, L"Framework::JSON::Parser::getValue('{\" \": []}') should return an object with one attribute");
			Assert::IsTrue(((Framework::JSON::JSONObject*)value)->isValueOfType(" ", Framework::JSON::JSONType::ARRAY), L"Framework::JSON::Parser::getValue('{\" \": []}') should contain an array at attribute ' '");
			value->release();
		}

		TEST_METHOD(ToStringTest)
		{
			Framework::JSON::JSONValue* value = Framework::JSON::Parser::getValue("{\" \": [1, true, false, 0.0, {}], \"t\": null}");
			Framework::JSON::JSONValue* value2 = Framework::JSON::Parser::getValue(value->toString());
			Assert::IsTrue(isEqual(value, value2), L"Framework::JSON::Parser::getValue(value.toString()) should return a json value eqal to value");
			value->release();
			value2->release();
		}

		static bool isEqual(Framework::JSON::JSONValue* a, Framework::JSON::JSONValue* b)
		{
			if (a->getType() != b->getType()) return 0;
			switch (a->getType())
			{
			case Framework::JSON::JSONType::NUMBER:
				return ((Framework::JSON::JSONNumber*)a)->getNumber() == ((Framework::JSON::JSONNumber*)b)->getNumber();
			case Framework::JSON::JSONType::BOOLEAN:
				return ((Framework::JSON::JSONBool*)a)->getBool() == ((Framework::JSON::JSONBool*)b)->getBool();
			case Framework::JSON::JSONType::STRING:
				return ((Framework::JSON::JSONString*)a)->getString().istGleich(((Framework::JSON::JSONString*)b)->getString());
			case Framework::JSON::JSONType::ARRAY:
			{
				Framework::JSON::JSONArray* arrayA = (Framework::JSON::JSONArray*)a;
				Framework::JSON::JSONArray* arrayB = (Framework::JSON::JSONArray*)b;
				if (arrayA->getLength() != arrayB->getLength()) return 0;
				for (int i = 0; i < arrayA->getLength(); i++)
				{
					Framework::JSON::JSONValue* entryA = arrayA->getValue(i);
					Framework::JSON::JSONValue* entryB = arrayB->getValue(i);
					bool eq = isEqual(entryA, entryB);
					entryA->release();
					entryB->release();
					if (!eq) return 0;
				}
				return 1;
			}
			case Framework::JSON::JSONType::OBJECT:
			{
				Framework::JSON::JSONObject* objA = (Framework::JSON::JSONObject*)a;
				Framework::JSON::JSONObject* objB = (Framework::JSON::JSONObject*)b;
				if (objA->getFieldCount() != objB->getFieldCount()) return 0;
				auto oaf = objA->getFields();
				while (oaf)
				{
					if (!objB->hasValue(oaf)) return 0;
					Framework::JSON::JSONValue* entryA = objA->getValue(oaf);
					Framework::JSON::JSONValue* entryB = objB->getValue(oaf);
					bool eq = isEqual(entryA, entryB);
					entryA->release();
					entryB->release();
					if (!eq) return 0;
					oaf++;
				}
				return 1;
			}
			}
			return 1;
		}

		TEST_METHOD(ToArrayTest)
		{
			Framework::JSON::JSONArray* jArray = Framework::JSON::Parser::getValue("[1,2,3,4,5,6,7,8,9,10]")->asArray();
			Framework::Array<int>* numberArray = jArray->toArray<int>([](Framework::JSON::JSONValue& v)
				{
					return (int)v.asNumber()->getNumber();
				});
			Assert::IsTrue(numberArray->getEintragAnzahl() == 10, L"Array hat die falsche Anzahl an elementen");
			Assert::IsTrue(numberArray->get(2) == 3, L"Array hat mindestens ein falsches element");
			Assert::IsTrue(numberArray->get(7) == 8, L"Array hat mindestens ein falsches element");
			numberArray->release();
			numberArray = jArray->toArray<int>([](Framework::JSON::JSONValue& v)
				{
					return (int)v.asNumber()->getNumber() % 2 == 0;
				}, [](Framework::JSON::JSONValue& v)
				{
					return (int)v.asNumber()->getNumber();
				});
			Assert::IsTrue(numberArray->get(0) == 2, L"Array hat mindestens ein falsches element");
			Assert::IsTrue(numberArray->get(3) == 8, L"Array hat mindestens ein falsches element");
			jArray->release();
		}

		TEST_METHOD(ToRCArrayTest)
		{
			Framework::JSON::JSONArray* jArray = Framework::JSON::Parser::getValue("[\"1\",\"2\",\"3\",\"4\",\"5\"]")->asArray();
			Framework::RCArray<Framework::Text>* numberArray = jArray->toRCArray<Framework::Text>([](Framework::JSON::JSONValue& v)
				{
					return new Framework::Text(v.asString()->getString());
				});
			Assert::IsTrue(numberArray->getEintragAnzahl() == 5, L"Array hat die falsche Anzahl an elementen");
			Assert::IsTrue(numberArray->z(1)->istGleich("2"), L"Array hat mindestens ein falsches element");
			Assert::IsTrue(numberArray->z(4)->istGleich("5"), L"Array hat mindestens ein falsches element");
			numberArray->release();
			numberArray = jArray->toRCArray<Framework::Text>([](Framework::JSON::JSONValue& v)
				{
					return (int)v.asString()->getString() % 2 == 0;
				}, [](Framework::JSON::JSONValue& v)
				{
					return new Framework::Text(v.asString()->getString());
				});
			Assert::IsTrue(numberArray->z(0)->istGleich("2"), L"Array hat mindestens ein falsches element");
			Assert::IsTrue(numberArray->z(1)->istGleich("4"), L"Array hat mindestens ein falsches element");
			jArray->release();
		}

		class TestObject
		{
		public:
			Framework::Text name;
			Framework::Text value;
		};

		TEST_METHOD(ParseObjectTest)
		{
			Framework::JSON::JSONObject* jObj = Framework::JSON::Parser::getValue("{\"name\": \"test\", \"value\": \"1234\"}")->asObject();
			TestObject* obj = jObj->parseTo<TestObject>(new TestObject(), [](TestObject* obj, Framework::Text attrName, Framework::JSON::JSONValue& v)
				{
					if (attrName.istGleich("name"))
					{
						obj->name = v.asString()->getString();
					}
					else
					{
						obj->value = v.asString()->getString();
					}
				});
			Assert::IsTrue(obj->name.istGleich("test"), L"Feld hat falschen wert");
			Assert::IsTrue(obj->value.istGleich("1234"), L"Feld hat falschen wert");
			delete obj;
			jObj->release();
		}

		TEST_METHOD(FromArrayTest)
		{
			Framework::Array<int> arr;
			arr.add(1);
			arr.add(2);
			arr.add(3);
			arr.add(4);

			Framework::JSON::JSONArray* jArray = Framework::JSON::JSONArray::fromArray<int>(arr, [](int v)
				{
					return new Framework::JSON::JSONNumber(v);
				});
			Assert::IsTrue(jArray->getLength() == 4, L"Array hat falsche länge");
			Framework::JSON::JSONNumber* n = jArray->getValue(1)->asNumber();
			Assert::IsTrue(n->getNumber() == 2, L"Array hat mindestens einen falschen Wert");
			n->release();
			jArray->release();

			Framework::RCArray<Framework::Text> rcArr;
			rcArr.add(new Framework::Text("1"));
			rcArr.add(new Framework::Text("2"));
			rcArr.add(new Framework::Text("3"));
			rcArr.add(new Framework::Text("4"));

			jArray = Framework::JSON::JSONArray::fromRCArray<Framework::Text>(rcArr, [](Framework::Text& v)
				{
					return new Framework::JSON::JSONString(v);
				});
			Assert::IsTrue(jArray->getLength() == 4, L"Array hat falsche länge");
			Framework::JSON::JSONString* s = jArray->getValue(2)->asString();
			Assert::IsTrue(s->getString().istGleich("3"), L"Array hat mindestens einen falschen Wert");
			s->release();
			jArray->release();
		}
	};

	TEST_CLASS(JSONValidatorTests)
	{
	private:
		static OutputDebugStringBuf<char, std::char_traits<char>> charDebugOutput;
		static std::streambuf* buf;

	public:
		TEST_CLASS_INITIALIZE(Init)
		{
			buf = std::cout.rdbuf();
			std::cout.rdbuf(&charDebugOutput);
		}
		TEST_METHOD(ValidTest)
		{
			Framework::JSON::Validator::JSONValidator* validator = Framework::JSON::Validator::JSONValidator::buildForArray()->
				addAcceptedObjectInArray()->
				withRequiredNumber("x")->
				whichIsLessThen(5)->
				finishNumber()->
				withRequiredBool("bla")->
				whichIsOptional()->
				withDefault(true)->
				finishBool()->
				finishObject()->finishArray();
			Framework::JSON::JSONArray* jArray = Framework::JSON::Parser::getValue("[{\"x\": 4, \"bla\": false}]")->asArray();
			Assert::IsTrue(validator->isValid(jArray), L"A valid json Array was marked as invalid by the validator");
			validator->release();
		}

		TEST_METHOD(ComplexTest)
		{
			Framework::JSON::Validator::JSONValidator* validator = Framework::JSON::Validator::JSONValidator::buildForArray()->typeSpecifiedByAttribute("type")->addAcceptedObjectInArray()->withRequiredString("type")->withExactMatch("shaped")->finishString()->withRequiredString("group")->finishString()->withRequiredNumber("width")->whichIsGreaterThen(0)->finishNumber()->withRequiredNumber("height")->whichIsGreaterThen(0)->finishNumber()->withRequiredArray("inputs")->addAcceptedObjectInArray()->withRequiredNumber("x")->whichIsGreaterOrEqual(0)->finishNumber()->withRequiredNumber("y")->whichIsGreaterOrEqual(0)->finishNumber()->withRequiredObject("filter")->withRequiredString("itemType")->finishString()->finishObject()->finishObject()->finishArray()->withRequiredObject("output")->withRequiredString("itemType")->finishString()->finishObject()->withRequiredNumber("outputCount")->whichIsGreaterThen(0)->finishNumber()->finishObject()->addAcceptedObjectInArray()->withRequiredString("type")->withExactMatch("unordered")->finishString()->withRequiredString("group")->finishString()->withRequiredArray("inputs")->addAcceptedObjectInArray()->withRequiredNumber("count")->whichIsGreaterThen(0)->finishNumber()->withRequiredObject("filter")->withRequiredString("itemType")->finishString()->finishObject()->finishObject()->finishArray()->withRequiredArray("output")->addAcceptedObjectInArray()->withRequiredObject("filter")->withRequiredString("itemType")->finishString()->finishObject()->withRequiredNumber("count")->whichIsGreaterThen(0)->finishNumber()->finishObject()->finishArray()->finishObject()->finishArray();
			std::cout << validator->zConstraints()->toString().getText() << "\n";
			Framework::JSON::JSONValue* value = Framework::JSON::Parser::getValue("[{\"type\": \"shaped\",\"group\": \"inventory\",\"width\": 1,\"height\": 2,\"inputs\": [{\"x\": 0,\"y\": 0,\"filter\": {\"itemType\": \"Cobble\"}},{\"x\": 0,\"y\": -1,\"filter\": {\"itemType\": \"Cobble\"}}],\"output\": {\"itemType\": \"StoneTool\"},\"outputCount\": 1},{\"type\": \"shaped\",\"group\": \"inventory\",\"width\": 1,\"height\": 2,\"inputs\": [{\"x\": 0,\"y\": 0,\"filter\": {\"itemType\": \"Cobble\"}},{\"x\": 0,\"y\": 1,\"filter\": {\"itemType\": \"Cobble\"}}],\"output\": {\"itemType\": \"StoneTool\"},\"outputCount\": 1}]");
			std::cout << value->toString().getText() << "\n";
			Framework::JSON::Validator::JSONValidationResult* result = validator->validate(value);
			result->printInvalidInfo();
			Assert::IsTrue(!result->isValid(), L"Invalid Json was marked as valid");
			Framework::JSON::JSONValue* validValue = result->getValidPart();
			result->release();
			Assert::IsTrue(validValue == 0, L"getValidPart of invalid validation result without removeInvalidEntries or default values used in validation should return 0");
			value->release();
			validator->release();
		}

		TEST_METHOD(ComplexRemoveInvalidTest)
		{
			Framework::JSON::Validator::JSONValidator* validator = Framework::JSON::Validator::JSONValidator::buildForArray()->removeInvalidEntries()->typeSpecifiedByAttribute("type")->addAcceptedObjectInArray()->withRequiredString("type")->withExactMatch("shaped")->finishString()->withRequiredString("group")->finishString()->withRequiredNumber("width")->whichIsGreaterThen(0)->finishNumber()->withRequiredNumber("height")->whichIsGreaterThen(0)->finishNumber()->withRequiredArray("inputs")->addAcceptedObjectInArray()->withRequiredNumber("x")->whichIsGreaterOrEqual(0)->finishNumber()->withRequiredNumber("y")->whichIsGreaterOrEqual(0)->finishNumber()->withRequiredObject("filter")->withRequiredString("itemType")->finishString()->finishObject()->finishObject()->finishArray()->withRequiredObject("output")->withRequiredString("itemType")->finishString()->finishObject()->withRequiredNumber("outputCount")->whichIsGreaterThen(0)->finishNumber()->finishObject()->addAcceptedObjectInArray()->withRequiredString("type")->withExactMatch("unordered")->finishString()->withRequiredString("group")->finishString()->withRequiredArray("inputs")->addAcceptedObjectInArray()->withRequiredNumber("count")->whichIsGreaterThen(0)->finishNumber()->withRequiredObject("filter")->withRequiredString("itemType")->finishString()->finishObject()->finishObject()->finishArray()->withRequiredArray("output")->addAcceptedObjectInArray()->withRequiredObject("filter")->withRequiredString("itemType")->finishString()->finishObject()->withRequiredNumber("count")->whichIsGreaterThen(0)->finishNumber()->finishObject()->finishArray()->finishObject()->finishArray();
			std::cout << validator->zConstraints()->toString().getText() << "\n";
			Framework::JSON::JSONValue* value = Framework::JSON::Parser::getValue("[{\"type\": \"shaped\",\"group\": \"inventory\",\"width\": 1,\"height\": 2,\"inputs\": [{\"x\": 0,\"y\": 0,\"filter\": {\"itemType\": \"Cobble\"}},{\"x\": 0,\"y\": -1,\"filter\": {\"itemType\": \"Cobble\"}}],\"output\": {\"itemType\": \"StoneTool\"},\"outputCount\": 1},{\"type\": \"shaped\",\"group\": \"inventory\",\"width\": 1,\"height\": 2,\"inputs\": [{\"x\": 0,\"y\": 0,\"filter\": {\"itemType\": \"Cobble\"}},{\"x\": 0,\"y\": 1,\"filter\": {\"itemType\": \"Cobble\"}}],\"output\": {\"itemType\": \"StoneTool\"},\"outputCount\": 1}]");
			std::cout << value->toString().getText() << "\n";
			Framework::JSON::Validator::JSONValidationResult* result = validator->validate(value);
			result->printInvalidInfo();
			Assert::IsTrue(!result->isValid(), L"Invalid Json was marked as valid");
			Framework::JSON::JSONValue* validValue = result->getValidPart();
			result->release();
			Framework::JSON::JSONValue* expected = Framework::JSON::Parser::getValue("[{\"type\": \"shaped\",\"group\": \"inventory\",\"width\": 1,\"height\": 2,\"inputs\": [{\"x\": 0,\"y\": 0,\"filter\": {\"itemType\": \"Cobble\"}},{\"x\": 0,\"y\": 1,\"filter\": {\"itemType\": \"Cobble\"}}],\"output\": {\"itemType\": \"StoneTool\"},\"outputCount\": 1}]");
			Assert::IsTrue(JSONParserTests::isEqual(validValue, expected), L"getValidPart of invalid validation result does not match the expected valid part");
			result = validator->validate(validValue);
			Assert::IsTrue(result->isValid(), L"Re validation of a value returned by getValidPart on a validation result should never return an invalid validation result");
			value->release();
			value = result->getValidPart();
			Assert::IsTrue(JSONParserTests::isEqual(validValue, value), L"getValidPart of a valid validation result should return the validated value");
			value->release();
			validValue->release();
			expected->release();
			validator->release();
		}

		TEST_METHOD(DefaultValuesTest)
		{
			Framework::JSON::Validator::JSONValidator* validator = Framework::JSON::Validator::JSONValidator::buildForArray()
				->typeSpecifiedByAttribute("type")
				->removeInvalidEntries()
				->addAcceptedTypeInArray(
					Framework::JSON::Validator::JSONValidator::buildForObject()
					->withRequiredString("type")->withExactMatch("shaped")->finishString()
					->withRequiredString("group")->withDefault("test")->finishString()
					->withRequiredNumber("width")->whichIsGreaterThen(0)->finishNumber()
					->withRequiredNumber("height")->whichIsGreaterThen(0)->finishNumber()
					->withRequiredAttribute("inputs",
						Framework::JSON::Validator::JSONValidator::buildForArray()
						->withDefault(new Framework::JSON::JSONArray())
						->addAcceptedTypeInArray(Framework::JSON::Validator::JSONValidator::buildForObject()
							->withRequiredNumber("x")->whichIsGreaterOrEqual(0)->finishNumber()
							->withRequiredNumber("y")->whichIsGreaterOrEqual(0)->finishNumber()
							->withRequiredObject("filter")->withRequiredString("itemType")->finishString()->finishObject()
							->finishObject())
						->finishArray())
					->withRequiredObject("output")->withRequiredString("itemType")->finishString()->finishObject()
					->withRequiredNumber("outputCount")->withDefault(1)->whichIsGreaterThen(0)->finishNumber()
					->finishObject())
				->addAcceptedTypeInArray(
					Framework::JSON::Validator::JSONValidator::buildForObject()
					->withRequiredString("type")->withExactMatch("unordered")->finishString()
					->withRequiredString("group")->finishString()
					->withRequiredAttribute("inputs",
						Framework::JSON::Validator::JSONValidator::buildForArray()
						->withDefault(new Framework::JSON::JSONArray())
						->addAcceptedTypeInArray(
							Framework::JSON::Validator::JSONValidator::buildForObject()
							->withRequiredNumber("count")->withDefault(1)->whichIsGreaterThen(0)->finishNumber()
							->withRequiredObject("filter")->withRequiredString("itemType")->finishString()->finishObject()
							->finishObject())
						->finishArray())
					->withRequiredAttribute("output",
						Framework::JSON::Validator::JSONValidator::buildForArray()
						->addAcceptedTypeInArray(Framework::JSON::Validator::JSONValidator::buildForObject()
							->withRequiredObject("filter")->withRequiredString("itemType")->finishString()->finishObject()
							->withRequiredNumber("count")->withDefault(1)->whichIsGreaterThen(0)->finishNumber()
							->finishObject())
						->finishArray())
					->finishObject())
				->finishArray();
			std::cout << validator->zConstraints()->toString().getText() << "\n";
			Framework::JSON::JSONValue* value = Framework::JSON::Parser::getValue("[{\"type\": \"shaped\",\"width\": 1,\"height\": 2,\"inputs\": [{\"x\": 0,\"y\": 0,\"filter\": {\"itemType\": \"Cobble\"}},{\"x\": 0,\"y\": 1,\"filter\": {\"itemType\": \"Cobble\"}}],\"output\": {\"itemType\": \"StoneTool\"}},{\"type\": \"shaped\",\"width\": 1,\"height\": 2,\"inputs\": [{\"x\": 0,\"y\": 0,\"filter\": {\"itemType\": \"Cobble\"}},{\"x\": 0,\"y\": -1,\"filter\": {\"itemType\": \"Cobble\"}}],\"output\": {\"itemType\": \"StoneTool\"}},{\"type\": \"unordered\",\"group\": \"bla\", \"inputs\": [{\"filter\": {\"itemType\": \"Cobble\"}},{\"filter\": {\"itemType\": \"Cobble\"}}],\"output\": [{\"filter\": {\"itemType\": \"StoneTool\"}}]}]");
			std::cout << value->toString().getText() << "\n";
			Framework::JSON::Validator::JSONValidationResult* result = validator->validate(value);
			result->printInvalidInfo();
			Assert::IsTrue(!result->isValid(), L"Invalid Json was marked as valid");
			Framework::JSON::JSONValue* validValue = result->getValidPart();
			result->release();
			Framework::JSON::JSONValue* expected = Framework::JSON::Parser::getValue("[{\"type\": \"shaped\", \"group\": \"test\",\"width\": 1,\"height\": 2,\"inputs\": [{\"x\": 0,\"y\": 0,\"filter\": {\"itemType\": \"Cobble\"}},{\"x\": 0,\"y\": 1,\"filter\": {\"itemType\": \"Cobble\"}}],\"output\": {\"itemType\": \"StoneTool\"}, \"outputCount\": 1},{\"type\": \"unordered\",\"group\": \"bla\", \"inputs\": [{\"count\": 1, \"filter\": {\"itemType\": \"Cobble\"}},{\"count\": 1, \"filter\": {\"itemType\": \"Cobble\"}}],\"output\": [{\"count\": 1, \"filter\": {\"itemType\": \"StoneTool\"}}]}]");
			Assert::IsTrue(JSONParserTests::isEqual(validValue, expected), L"getValidPart of invalid validation result does not match the expected valid part");
			result = validator->validate(validValue);
			Assert::IsTrue(result->isValid(), L"Re validation of a value returned by getValidPart on a validation result should never return an invalid validation result");
			value->release();
			value = result->getValidPart();
			Assert::IsTrue(JSONParserTests::isEqual(validValue, value), L"getValidPart of a valid validation result should return the validated value");
			value->release();
			validValue->release();
			expected->release();
			validator->release();
		}

		TEST_CLASS_CLEANUP(Cleanup)
		{
			std::cout.rdbuf(buf);
		}
	};

	OutputDebugStringBuf<char, std::char_traits<char>> JSONValidatorTests::charDebugOutput;
	std::streambuf* JSONValidatorTests::buf;
}
