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

#include "check_bidi_slots.h"

#include <coral/core/Collection.h>
#include <coral/lib/autovector.h>

NAMESPACE_BEGIN(coral);
NAMESPACE_BEGIN(util);

NAMESPACE_BEGIN();

void error_msg(const Property* p1, const Property* p2, const Element* e1) {
	dlog << DEBUGLEVEL_ERROR
	     << "ERROR Something REALLY bad has happened." << std::endl
	     << "Contents of bidirectional slots " << p1->getName()
	     << " and " << p2->getName()
	     << " are completely out of sync and Coral cannot fix them."
	     << std::endl
	     << " (UUID for first element is " << e1->__XMIid__()
	     << std::endl;
}

void warn_msg(Element* e, Slot& s) {
	dlog << DEBUGLEVEL_WARNING
	     << "WARNING Coral found a bad, unidirectional connection "
	     << "from " << e->__type__()->getName() << ":" << s.getProperty()->getName()
	     << std::endl
	     << " (UUID " << e->__XMIid__() << ")"
	     << " (fixed, continuing)"
	     << std::endl;
}


void ch_ref(Element* e, Slot& s) {
	const Property* p = s.getProperty();
	if (p->getTarget()->isDatatypeDefinition())
		return;
	const Property* oppo
		= p->getOppositeProperty();
	coral_assert(oppo);
	// BUG this could be more robust
	if (oppo == p) return;

	const char* opposlotname = oppo->getName();
	coral_assert(oppo);
	coral_assert(opposlotname);
	Iterator o(s);
	ref_counted* t;
	while ((t = o.next()) != NULL) {
		Element* c = dynamic_cast<Element*>(t);
		if (!c) continue;
		Slot& opposlot =
			c->__getSlotByName__(opposlotname);
		if (oppo->isAtMostOne()) {
			Element* other =
				dynamic_cast<Element*>(opposlot.__getitem__(0));
			if (!other) {
				opposlot.insert(e, -1, -1, true);
				warn_msg(e, s);
				continue;
			}
			if (other != e) {
				coral_assert(other);
				error_msg(p, oppo, e);
				s.drop(c, true);
				//throw std::exception();
			}
		} else {
			Collection* oppo_collection
				= opposlot.getCollection();
			std::size_t cnt1;
			std::size_t cnt2;
			if (p->isAtMostOne()) {
				cnt1 = 1;
			} else {
				Collection* collection
					= s.getCollection();
				cnt1 = collection->count(c);
			}
			cnt2 = oppo_collection->count(e);
			if (cnt1 == cnt2)
				continue;
			while (cnt1 > cnt2) {
				oppo_collection->add(e);
				++cnt2;
				warn_msg(e, s);
			}
			while (cnt1 < cnt2) {
				if (p->isAtMostOne()) {
					oppo_collection->drop(e);
					--cnt2;
					warn_msg(c, opposlot);
				} else {
					Collection* collection
						= s.getCollection();
					collection->add(c);
					++cnt1;
					warn_msg(e, s);
				}
			}
		}
	}
}
NAMESPACE_END();

void check_bidi_slots(Element* root) {
	coral_assert(root);
	autounref<Set> _o(root->__getAllParts__());
	Set* o = _o.get();
	Iterator i(o);
	ref_counted* t;
	while ((t = i.next()) != NULL) {
		Element* e = dynamic_cast<Element*>(t);
		if (!e) continue;
		for (coral::Element::slots_type::const_iterator j = e->begin();
		     j != e->end();
		     ++j) {
			Slot* s = (*j).second;
			ch_ref(e, *s);
		}
	}
}
NAMESPACE_END();
NAMESPACE_END();
