// This file is part of the Coral project.
// Please read the copyright notice in the LICENSE file.

#include "uElement.h"

#include <coral/core/CorePrimitives.h>

NAMESPACE_BEGIN(coral);
NAMESPACE_BEGIN(util);

coral::autounref<coral::OrderedSet>
Element__getMyselfAndParents__(const Element* e, bool includemyself) {
	autounref<coral::OrderedSet> res(new OrderedSet());
	Element* t = const_cast<Element*>(e);
	while (t) {
		if (!includemyself && t == e) goto nxt;
		res->insert(t);
	nxt:
		t = t->__parent__();
	}
	return res;
}

autounref<coral::OrderedSet>
Element__getParentsAndMyself__(const Element* e, bool includemyself) {
	autounref<coral::OrderedSet> res(new OrderedSet());
	Element* t = const_cast<Element*>(e);
	while (t) {
		if (!includemyself && t == e) goto nxt;
		res->insert(t, 0);
	nxt:
		t = t->__parent__();
	}
	return res;
}

NAMESPACE_BEGIN();

double add(double x_or_y, double param) {
	return x_or_y + param;
}

double sub(double x_or_y, double param) {
	return x_or_y - param;
}

std::pair<double, double> fix(const Element* e, double x, double y,
			      double (func)(double, double)) {
	while (e) {
		/*
		 * Add position.x and position.y
		 */
		const Slot& sposition = e->__getSlotRO__("position");
		const Atom* aposition = sposition.__getitem__(0);
		if (aposition) {
			const Element* pos =
				dynamic_cast<const Element*>(aposition);
			coral_assert(pos);
			const Slot& sx = pos->__getSlotRO__("x");
			const Slot& sy = pos->__getSlotRO__("y");
			const ElementDouble* epx
				= dynamic_cast<const ElementDouble*>(sx.__getitem__(0));
			const ElementDouble* epy
				= dynamic_cast<const ElementDouble*>(sy.__getitem__(0));
			double nx = 0;
			double ny = 0;
			if (epx) nx = epx->__getDValue__();
			if (epy) ny = epy->__getDValue__();
			x = func(x, nx);
			y = func(y, ny);
		}
		/*
		 * go up
		 */
		const Slot& scontainer = e->__getSlotRO__("container");
		const Atom* acontainer = scontainer.__getitem__(0);
		if (!acontainer) break;
		e = dynamic_cast<const Element*>(acontainer);
	}
	return std::pair<double, double>(x, y);
}

NAMESPACE_END();

std::pair<double, double> XMIDI_LocalToGlobalPosition(const Element* e, double x, double y) {
	return fix(e, x, y, add);
}

std::pair<double, double> XMIDI_GlobalToLocalPosition(const Element* e,
						      double x,
						      double y) {
	return fix(e, x, y, sub);
}

bool XMIDI_HasReferentialRole(const Element* de) {
	/*
	 * parent --- dparent
	 *   |           |
	 * child  ---   de
	 */
	const Slot& s1 = de->__getSlotRO__("semanticModel");
	const Element* de_sm_first =
		dynamic_cast<const Element*>(s1.__getitem__(0));
	coral_assert(de_sm_first);
	const Slot& s2 = de_sm_first->__getSlotRO__("element");
	const Element* child =
		dynamic_cast<const Element*>(s2.__getitem__(0));

	if (!child) return true;

	const Element* parent = child->__parent__();
	if (!parent) return false;


	while (1) {
		const Slot& s3 = de->__getSlotRO__("container");
		const Element* dparent =
			dynamic_cast<const Element*>(s3.__getitem__(0));
		if (!dparent) return false;

		const Slot& s4 = de->__getSlotRO__("semanticModel");
		const Element* de_sm =
			dynamic_cast<const Element*>(s4.__getitem__(0));
		if (!de_sm) return false;
		const Slot& s5 = de_sm->__getSlotRO__("element");
		const Element* diagram_parent =
			dynamic_cast<const Element*>(s5.__getitem__(0));
		
		de = dparent;

		if (!diagram_parent) {
			return false;
		} else {
			if (diagram_parent == parent) return false;
			return true;
		}
	}
}


NAMESPACE_BEGIN();
coral::Element*
XMIDI_find_a_property_upwards(coral::Element* e,
			      const std::set<std::string> properties) {

	while (e) {
		const Slot* s_property;
		try {
			const Slot& ss_property = e->__getSlotRO__("property");
			s_property = &ss_property;
		} catch (bad_slot& exc) {
			return NULL;
		}
		coral::Iterator i(s_property);
		coral::ref_counted* r;
		while ((r = i.next()) != NULL) {
			Element* re = dynamic_cast<coral::Element*>(r);
			if (!re) continue;
			const coral::ElementPrimitive* e_key
				= dynamic_cast<const ElementPrimitive*>(re->__getSlotOneElement__("key"));
			if (!e_key) continue;
			const coral::unicodestring& s_key(e_key->__getString__());
			const std::string s_key_utf8(s_key.toUTF8());
			if (properties.find(s_key_utf8) != properties.end())
				return re;
		}
		e = dynamic_cast<Element*>(e->__getSlotOneElement__("container"));
	}
	return NULL;
}
NAMESPACE_END();
coral::Element*
XMIDI_find_property_upwards(coral::Element* e,
			    const std::string& propname) {
	std::set<std::string> s;
	s.insert(propname);
	return XMIDI_find_a_property_upwards(e, s);
}


coral::Element*
XMIDI_find_either_property_upwards(coral::Element* e,
				   const std::string& propname1,
				   const std::string& propname2) {
	std::set<std::string> s;
	s.insert(propname1);
	s.insert(propname2);
	return XMIDI_find_a_property_upwards(e, s);
}

const ElementString* Element__getName(const Element* e) {
	const Slot* s;
	try {
		const Slot& ss = e->__getSlotRO__("name");
		s = &ss;
	} catch (...) {
		return NULL;
	}
	const Property* p = s->getProperty();
	coral_assert(p);
	if (!p->isAtMostOne())
		return NULL;
	if (!p->isComposition())
		return NULL;
	const AbstractElementDefinition* target = p->getTarget();
	if (target != coral::CORE::String
	    && target != coral::CORE::Name)
		return NULL;
	if (s->__len__() != 1)
		return NULL;
	return dynamic_cast<const ElementString*>(s->__getitem__(0));
}

NAMESPACE_END();
NAMESPACE_END();
