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

#include <coral/core/Element.h>
#include "ElementIterator.h"

#include <coral/core/Collection.h>

#include <coral/lib/assert.h>

namespace coral {

	AtomIterator::AtomIterator(Atom* e, iteration_type type) {
		m_the_next = NULL;
		if (e) {
			m_parents.push_back(e);
			e->ref();
			coral_assert(m_parents.size() == 1);
		}
		m_status = STATUS_NEWELEM;
		coral_assert(type == TYPE_DFS);
	}

	AtomIterator::~AtomIterator() {
		;
	}

	void AtomIterator::discardCurrentBranch() {
		coral_assert(m_status == STATUS_MM_ITERATE);
		coral_assert(!m_parents.empty());
		m_status = STATUS_MF_ITERATE;
		m_parents.pop_back();
		m_properties_iterators.pop_back();
	}

	bool AtomIterator::hasNext() {
		m_the_next = next();
		return m_the_next != NULL;
	}

	Atom* AtomIterator::next() {
		if (m_the_next != NULL) {
			Atom* t = m_the_next;
			m_the_next = NULL;
			return t;
		}
		switch (m_status) {
		case STATUS_NEWELEM: {
			if (m_parents.empty()) return NULL;
			m_status = STATUS_MM_ITERATE;
			Atom* p = m_parents.back();
			AbstractElementDefinition* _mp = p->__type__();
			if (_mp->isDatatypeDefinition()) return p;
			ElementDefinition* mp = static_cast<ElementDefinition*>(_mp);
			m_properties_iterators.push_back(mp->getProperties()->begin());
			return p;
			break;
		}
		case STATUS_MM_ITERATE: {
			coral_assert(!m_parents.empty());
			Atom* _p = m_parents.back();
			coral_assert(_p);
			AbstractElementDefinition* _mp = _p->__type__();
			coral_assert(_mp);
			if (_mp->isDatatypeDefinition()) {
				m_status = STATUS_NEWELEM;
				return next();
			}
			Element* p = static_cast<Element*>(_p);
			ElementDefinition* mp = static_cast<ElementDefinition*>(_mp);
			ElementDefinition::properties_type::const_iterator& i = m_properties_iterators.back();
			for (; i != mp->getProperties()->end(); ++i) {
				Property* f = (*i).second;
				if (!f->isComposition())
					continue;
				if (f->getTarget()->isDatatypeDefinition())
					continue;

				const char* feature = (*i).first;
				Slot* mf = p->__hasSlotByName__(feature);
				if (mf == NULL || mf->size() == 0) continue;
				Iterator* mf_iter = new Iterator(mf);
				autounref<ref_counted> t(mf_iter);
				m_slot_iterators.push_back(mf_iter);
				t.release();
				m_status = STATUS_MF_ITERATE;
				++i;
				return next();
			}
			m_properties_iterators.pop_back();
			m_parents.back()->unref();
			m_parents.pop_back();
			m_status = STATUS_MF_ITERATE;
			return next();
			break;
		}
		case STATUS_MF_ITERATE: {
			if (m_slot_iterators.empty()) return NULL;
			ref_counted* _res = m_slot_iterators.back()->next();
			Atom* res = dynamic_cast<Atom*>(_res);
			if (res == NULL) {
				m_slot_iterators.back()->unref();
				m_slot_iterators.pop_back();
				m_status = STATUS_MM_ITERATE;
				return next();
			}
			m_status = STATUS_NEWELEM;
			m_parents.push_back(res);
			res->ref();
			return next();
			break;
		}
		default:
			coral_assert(0);
			break;
		}
		coral_assert(0);
		return NULL;
	}
}

