/*
    Ousía
    Copyright (C) 2014, 2015  Benjamin Paaßen, Andreas Stöckel
    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.
    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.
    You should have received a copy of the GNU General Public License
    along with this program.  If not, see .
*/
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
namespace ousia {
namespace RttiTypes {
extern const Rtti Document;
extern const Rtti Domain;
extern const Rtti Typesystem;
}
struct XmlStandaloneEnvironment : public StandaloneEnvironment {
	XmlParser xmlParser;
	FileLocator fileLocator;
	XmlStandaloneEnvironment(ConcreteLogger &logger)
	    : StandaloneEnvironment(logger)
	{
		fileLocator.addDefaultSearchPaths();
		fileLocator.addUnittestSearchPath("xmlparser");
		registry.registerDefaultExtensions();
		registry.registerParser({"text/vnd.ousia.oxm", "text/vnd.ousia.oxd"},
		                        {&RttiTypes::Node}, &xmlParser);
		registry.registerResourceLocator(&fileLocator);
	}
};
static TerminalLogger logger(std::cerr, true);
TEST(XmlParser, mismatchedTag)
{
	XmlStandaloneEnvironment env(logger);
	env.parse("mismatchedTag.oxm", "", "", RttiSet{&RttiTypes::Document});
	ASSERT_TRUE(logger.hasError());
}
TEST(XmlParser, generic)
{
	XmlStandaloneEnvironment env(logger);
	env.parse("generic.oxm", "", "", RttiSet{&RttiTypes::Node});
#ifdef MANAGER_GRAPHVIZ_EXPORT
	env.manager.exportGraphviz("xmlDocument.dot");
#endif
}
static void checkStructuredClass(
    Handle n, const std::string &name, Handle domain,
    Variant cardinality = AnyCardinality,
    Handle attributesDescriptor = nullptr,
    Handle superclass = nullptr, bool transparent = false,
    bool root = false)
{
	ASSERT_FALSE(n == nullptr);
	Handle sc = n.cast();
	ASSERT_FALSE(sc == nullptr);
	ASSERT_EQ(name, sc->getName());
	ASSERT_EQ(domain, sc->getParent());
	ASSERT_EQ(cardinality, sc->getCardinality());
	ASSERT_EQ(transparent, sc->isTransparent());
	ASSERT_EQ(root, sc->hasRootPermission());
}
static Rooted checkStructuredClass(
    const std::string &resolve, const std::string &name, Handle domain,
    Variant cardinality = AnyCardinality,
    Handle attributesDescriptor = nullptr,
    Handle superclass = nullptr, bool transparent = false,
    bool root = false)
{
	auto res = domain->resolve(RttiTypes::StructuredClass, resolve);
	if (res.size() != 1) {
		throw OusiaException("resolution error!");
	}
	Handle sc = res[0].node.cast();
	checkStructuredClass(sc, name, domain, cardinality, attributesDescriptor,
	                     superclass, transparent, root);
	return sc;
}
static void checkFieldDescriptor(
    Handle n, const std::string &name, Handle parent,
    NodeVector children,
    FieldDescriptor::FieldType type = FieldDescriptor::FieldType::TREE,
    Handle primitiveType = nullptr, bool optional = false)
{
	ASSERT_FALSE(n.isNull());
	Handle field = n.cast();
	ASSERT_FALSE(field.isNull());
	ASSERT_EQ(name, field->getName());
	ASSERT_EQ(parent, field->getParent());
	ASSERT_EQ(type, field->getFieldType());
	ASSERT_EQ(primitiveType, field->getPrimitiveType());
	ASSERT_EQ(optional, field->isOptional());
	// check the children.
	ASSERT_EQ(children.size(), field->getChildren().size());
	for (unsigned int c = 0; c < children.size(); c++) {
		ASSERT_EQ(children[c], field->getChildren()[c]);
	}
}
static void checkFieldDescriptor(
    Handle desc, NodeVector children,
    const std::string &name = "",
    FieldDescriptor::FieldType type = FieldDescriptor::FieldType::TREE,
    Handle primitiveType = nullptr, bool optional = false)
{
	auto res = desc->resolve(RttiTypes::FieldDescriptor, name);
	ASSERT_EQ(1, res.size());
	checkFieldDescriptor(res[0].node, name, desc, children, type, primitiveType,
	                     optional);
}
TEST(XmlParser, domainParsing)
{
	XmlStandaloneEnvironment env(logger);
	Rooted n =
	    env.parse("book_domain.oxm", "", "", RttiSet{&RttiTypes::Domain});
	ASSERT_FALSE(n == nullptr);
	ASSERT_FALSE(logger.hasError());
	// check the domain node.
	Rooted domain = n.cast();
	ASSERT_EQ("book", domain->getName());
	// get the book struct node.
	Cardinality single;
	single.merge({1});
	Rooted book = checkStructuredClass(
	    "book", "book", domain, single, nullptr, nullptr, false, true);
	// get the chapter struct node.
	Rooted chapter =
	    checkStructuredClass("chapter", "chapter", domain);
	Rooted section =
	    checkStructuredClass("section", "section", domain);
	Rooted subsection =
	    checkStructuredClass("subsection", "subsection", domain);
	Rooted paragraph =
	    checkStructuredClass("paragraph", "paragraph", domain, AnyCardinality,
	                         nullptr, nullptr, true, false);
	Rooted text = checkStructuredClass(
	    "text", "text", domain, AnyCardinality, nullptr, nullptr, true, false);
	// check the FieldDescriptors.
	checkFieldDescriptor(book, {chapter, paragraph});
	checkFieldDescriptor(chapter, {section, paragraph});
	checkFieldDescriptor(section, {subsection, paragraph});
	checkFieldDescriptor(subsection, {paragraph});
	checkFieldDescriptor(paragraph, {text});
	checkFieldDescriptor(
	    text, {}, "content", FieldDescriptor::FieldType::PRIMITIVE,
	    env.project->getSystemTypesystem()->getStringType(), false);
}
}