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

#include <coral/coral.h>
#include <coral/core/DCE_UUID.h>
#include <coral/lib/struct_helpers.h>
#include <coral/lib/noncopy.h>

#include <coral/core/python_compat.h>
#include <string>
#include <list>

#include <coral/lib/autovector.h>
#include <coral/core/exception.h>
#include <coral/core/PyOperation.h>

#include <coral/core/Collection.h>

namespace coral {

     class EXPORTFWD_coral EnumerationDefinition;
     class EXPORTFWD_coral Property;
     class EXPORTFWD_coral AbstractElementDefinition;
     class EXPORTFWD_coral ElementDefinition;
     class EXPORTFWD_coral Atom;
     class EXPORTFWD_coral DCE_UUID;
     class EXPORTFWD_coral Element;
     class EXPORTFWD_coral Slot;
     class EXPORTFWD_coral Language;
     class EXPORTFWD_coral Package;
     class EXPORTFWD_coral Meta2ModelBridge;

     CORAL_HIDDEN void setCorrespondingElement(coral::Meta2ModelBridge*,
					       coral::Element*);

     // This is the __meta__ hack
     class EXPORT_coral Meta2ModelBridge : public ref_counted {
	  coral::autounref<coral::Element> m_correspondingElement;
	  friend void setCorrespondingElement(coral::Meta2ModelBridge* mm,
					      coral::Element* e);
     public:
	  coral::autounref<coral::Element> getCorrespondingElement();
     };

/** The general metamodel element in Coral. */
class EXPORT_coral AbstractElementDefinition:
     public Meta2ModelBridge, public DCE_UUID {
 private:
	PyObject* bindOperation(const std::string& name);
	PyObject* bindOperation_single(const std::string& name);
	std::string m_name;
 protected:
	Package* package;
 public:
	typedef std::list<AbstractElementDefinition*> subclasses_type;
	typedef std::list<AbstractElementDefinition*> superclasses_type;
	typedef std::list<AbstractElementDefinition*> topologic_all_superclasses_type;
	typedef autounreflist<PyOperation*> operations_type;
	coral::PyOperation* getOperation(const std::string& name);
 protected:
	bool isAbstractED;
	operations_type operations;
	subclasses_type subclasses;
	superclasses_type superclasses;
	topologic_all_superclasses_type all_superclasses;
	CORAL_HIDDEN void recalculateAllSuperclasses();
	CORAL_HIDDEN void
	  recalculateForMyselfAndSubclasses(std::set<AbstractElementDefinition*>& visited);
	AbstractElementDefinition(Package*, ref_counted* type, const char* name);
     public:
	static coral::autounref<coral::AbstractElementDefinition>
	  getAbstractElementDefinitionFromSMD(const Element* e);
	void __addOperation__(coral::PyOperation*);
	void __removeOperation__(PyObject*);
	PyObject* __bindOperation__(const std::string& name,
				PyObject* self);

	bool isAbstract() { return isAbstractED; }
	void setAbstract(bool newvalue) { isAbstractED = newvalue; }

	virtual ~AbstractElementDefinition() throw ();

	const std::string& getName() const throw () {
	  return this->m_name;
	}

	void addSubclass(AbstractElementDefinition* m) {
		m->addSuperclass(this);
	}
	void addSuperclass(AbstractElementDefinition* m);
#ifndef SWIG
	const operations_type& getOperations() const throw () { return this->operations; }
	const subclasses_type& getSubclasses() const throw () { return this->subclasses; }
	const superclasses_type& getSuperclasses() const throw () { return this->superclasses; }
	const topologic_all_superclasses_type& getAllSuperclasses() const throw () { return this->all_superclasses; }
#endif
	virtual bool isDatatypeDefinition() const throw () { return false; }
	virtual bool isEnumerationDefinition() const throw () { return false; }
	Package* __package__() const throw () { return this->package; }
	Language* __language__() const throw (); 
	/** Returns true is this is a root element definition in its language */
	bool isRootDefinition() const throw ();
	/** Returns true if this is a subclass of e*/
	bool isSubclass(const AbstractElementDefinition* e) const throw ();
	/** Returns true if this is a subclass of e*/
	bool isProperSubclass(const AbstractElementDefinition* e) const throw () {
	     return (this != e) && this->isSubclass(e);
	}
	PythonSequenceIteratorDirect<coral::AbstractElementDefinition::superclasses_type::iterator, coral::AbstractElementDefinition*>* __superclasses__() {
	     return new PythonSequenceIteratorDirect<coral::AbstractElementDefinition::superclasses_type::iterator, coral::AbstractElementDefinition*>(this, this->superclasses.begin(), this->superclasses.end());
	}

	PythonSequenceIteratorDirect<coral::AbstractElementDefinition::superclasses_type::iterator, coral::AbstractElementDefinition*>* __all_superclasses__() {
	     return new PythonSequenceIteratorDirect<coral::AbstractElementDefinition::superclasses_type::iterator, coral::AbstractElementDefinition*>(this, this->all_superclasses.begin(), this->all_superclasses.end());
	}

	PythonSequenceIteratorDirect<coral::AbstractElementDefinition::subclasses_type::iterator, coral::AbstractElementDefinition*>* __subclasses__() {
	     return new PythonSequenceIteratorDirect<coral::AbstractElementDefinition::subclasses_type::iterator, coral::AbstractElementDefinition*>(this, this->subclasses.begin(), this->subclasses.end());
	}

	PythonSequenceIteratorDirect<coral::AbstractElementDefinition::operations_type::iterator, coral::PyOperation*>* __operations__() {
	     return new PythonSequenceIteratorDirect<coral::AbstractElementDefinition::operations_type::iterator, coral::PyOperation*>(this, this->operations.begin(), this->operations.end());
	}

	/** Returns true if this is a presentation metamodel element. */
	virtual bool isPresentation() const throw ();
	/** Returns new atom instance. */
	virtual Atom* newAtom() CORAL_VIRTUAL;
	Element* newElement();
	virtual std::string __str__() const CORAL_VIRTUAL;
	
};

     coral::autounref<coral::Set> inheritanceTree(coral::AbstractElementDefinition* e);

/**
 * Returns true if exactly either one of the two given types
 * are presentations.
 */
 bool crossesPresentationLayer(const AbstractElementDefinition*, const AbstractElementDefinition*);

class EXPORTFWD_coral Property;

class EXPORT_coral ElementDefinition: public AbstractElementDefinition {
 public:
	typedef auto2unrefmap<const char*, Property*, less_strcmp<const char*> > properties_type;
	/*
	 * Properties in order required for MOF Datatypes.
	 * A bit crude, since it makes all lists of Properties in SMD models
	 * ordered. :(
	 */
	typedef std::vector<Property*> ordered_properties_type;
 private:
	properties_type properties;
	ordered_properties_type ordered_properties;
	bool MOFDatatype;
 public:
	CORAL_HIDDEN void __propagateProperties(ElementDefinition* super);
	ElementDefinition(Package*, const char* name);
	virtual ~ElementDefinition() throw ();

#ifdef COMPILING_coral
	CORAL_HIDDEN bool _isWellFormed(/* const */ Element*) const throw();
#endif
	/* These functions should only be used when creating
	   the metamodel element. */
	CORAL_HIDDEN void addProperty(Property* f);
	void addPropertyRecursive(Property* f);
#ifndef SWIG
	const properties_type* getProperties() const throw ();
	CORAL_HIDDEN const ordered_properties_type*
	  getPropertiesInOrder() const throw ();
#endif
	Property* getPropertyByName(const char* name) const throw ();
	Property* getPropertyByOpposite(const ElementDefinition* oppo,
					const char* opponame);
	//@deprecated
	CORAL_HIDDEN bool isMOFDatatype() const throw () {
	  return MOFDatatype;
	}
	//@deprecated
	CORAL_HIDDEN void setMOFDatatype(bool newvalue) throw () {
	  MOFDatatype = newvalue;
	}
	Element* operator()(bool generate_element_attributes = true);
	bool isPresentation() const throw ();
	PythonSequenceIteratorDirect<ElementDefinition::ordered_properties_type::const_iterator, coral::Property*>* __properties__() {
	     return new PythonSequenceIteratorDirect<ElementDefinition::ordered_properties_type::const_iterator, coral::Property*>(this, this->getPropertiesInOrder()->begin(), this->getPropertiesInOrder()->end());
	}
#ifndef SWIG
	virtual Atom* newAtom();
	virtual std::string __str__() const;
	
	CORAL_HIDDEN void __setCorrectPropertiesOrder(ordered_properties_type& t);
#endif
};


#ifndef SWIG
     struct kind_t {
	     /* composition is (at least now) mutually excl. with association
	      * but don't count on it... */
	     static const int KIND_ORDERED = 1;
	     static const int KIND_BAG = 2;
	     static const int KIND_BIDI = 4;
	     static const int KIND_NOSERIALIZE = 8;
	     static const int KIND_ANONYMOUS = 16;
	     static const int KIND_COMPOSITION = 32;
	     static const int KIND_ASSOCIATION = 64;
	     int kind;
	     bool isOrdered() const throw () { return kind & KIND_ORDERED; }
	     bool isBag() const throw () { return kind & KIND_BAG;}
	     bool isBidirectional() const throw () {return kind & KIND_BIDI; }
	     bool isComposition() const throw () { return kind & KIND_COMPOSITION;}
	     bool isAssociation() const throw () { return kind & KIND_ASSOCIATION;}
	     bool isUnserializable() const throw () { return kind & KIND_NOSERIALIZE; }
	     bool isAnonymous() const throw () { return kind & KIND_ANONYMOUS; }
	     kind_t(const int t = 0) : kind(t) { ; }
	     const kind_t& operator|=(const int t) { kind |= t; return *this; }
     };
#endif

class EXPORT_coral Property : public Meta2ModelBridge, public noncopy {
 private:
	  /* Name of the Property */
	  std::string key;
	  /* Default value (for primitives) */
	  std::string m_default_value;
	/* What Slots of this Property should contain. */
	AbstractElementDefinition* element_kind;
	/* Top-level owner of this property. */
	AbstractElementDefinition* owner;
	/* Opposite Property */
	Property* other;
	/* A read-only slot with no contents. Used for Element.__getSlotRO__() */
	Slot* read_only_slot;
	kind_t kind;
	int mult_lower;
	// -1 for unlimited
	int mult_upper;
	Property* __findOtherProperty(AbstractElementDefinition*, const char*) const throw ();
	friend class ElementDefinition;
	void __init(const char* _key, AbstractElementDefinition* _element_kind,
		    const char* other_key,
		    int m_lower, int m_upper,
		    kind_t k,
		    const std::string& default_value);
 public:
	virtual ~Property() throw ();
	AbstractElementDefinition* getOwner() const throw () { return owner; }
	Slot* getROSlot() const throw () {
		coral_assert(this->read_only_slot);
		return this->read_only_slot;
	}
	const std::string& getDefaultValue() const throw () { return m_default_value; }
	bool isMultSingle() const throw () { return mult_lower == 1 && mult_upper == 1; }
	bool isMultUnlimited() const throw () { return mult_upper == -1; }
	bool isAtMostOne() const throw () { return mult_upper == 1; }
	bool isAtLeastOne() const throw () { return mult_lower >= 1; }
	int getMultLower() const throw () { return mult_lower; }
	int getMultUpper() const throw () { return mult_upper; }
	DELEGATE_FUNC(bool, isOrdered, const throw (), kind);
	DELEGATE_FUNC(bool, isBag, const throw (), kind);
	DELEGATE_FUNC(bool, isBidirectional, const throw (), kind);
	DELEGATE_FUNC(bool, isComposition, const throw (), kind);
	DELEGATE_FUNC(bool, isAssociation, const throw (), kind);
	DELEGATE_FUNC(bool, isUnserializable, const throw (), kind);
	DELEGATE_FUNC(bool, isAnonymous, const throw (), kind);

#ifndef SWIG
	kind_t getKindT() const throw() { return kind; }
	CORAL_HIDDEN void __makeAnonymousProperty();
	/** New Slot for e, based on this Property */
	Slot* newSlot(Element* e) throw (std::bad_alloc);
#endif
	const char* getName() const throw() {
		return this->key.c_str();
	}
	AbstractElementDefinition* getTarget() const throw () {
		return this->element_kind;
	}
	Property* getOppositeProperty() const throw () {
		return this->other;
	}

#ifdef SWIG
	Property(const char* _key, AbstractElementDefinition* element_kind, const char* other_key,
		 int multiplicity_lower, int multiplicity_upper,
		 int kind,
		 const std::string& default_value = "") {
		__init(_key, element_kind, other_key, multiplicity_lower,
		       multiplicity_upper, kind, default_value);
	}
#else
	Property(const char* _key, AbstractElementDefinition* elem_kind,
		 const char* other_key,
		 int multiplicity_lower, int multiplicity_upper,
		 kind_t k,
		 const std::string& default_value = ""
	     );
#endif
};

class EXPORT_coral Package : public AbstractElementDefinition {
public:	
	typedef autounreflist<AbstractElementDefinition*> elements_type;
private:
	std::string xmlns;
	elements_type imported_elements;
	elements_type elements;
public:
	const std::string getXMLNS() const { return this->xmlns; }
	void setXMLNS(const std::string& newxmlns);

	PythonSequenceIteratorDirect<coral::Package::elements_type::iterator,
	     coral::AbstractElementDefinition*>* __elements__() {
	     return new PythonSequenceIteratorDirect<
		  coral::Package::elements_type::iterator,
		  coral::AbstractElementDefinition*>(
		       this,
		       this->elements.begin(),
		       this->elements.end()
		       );
	}
	PythonSequenceIteratorDirect<coral::Package::elements_type::iterator,
	     coral::AbstractElementDefinition*>* __imported_elements__() {
	     return new PythonSequenceIteratorDirect<
		  coral::Package::elements_type::iterator,
		  coral::AbstractElementDefinition*>(
		       this,
		       this->imported_elements.begin(),
		       this->imported_elements.end()
		       );
	}


	Language* __language__() const throw();
	Package(Package* parent, const std::string& name);
	virtual ~Package() throw ();
	virtual coral::autounref<coral::AbstractElementDefinition>
	     getElement(const char* name) const throw (std::exception);
	coral::autounref<coral::AbstractElementDefinition> __getattr__(const char* name)
	     const throw (std::exception) {
	     return getElement(name);
	}
	void addElement(AbstractElementDefinition* mm);
	void removeElement(AbstractElementDefinition* mm);
	void importElement(AbstractElementDefinition* mm);
#ifndef SWIG
	virtual Atom* newAtom();
	virtual std::string __str__() const;
#endif
	static coral::autounref<coral::Package>
	     __getLanguageByXMLNS__(const std::string& xmlns,
				    bool allowdynamicloading = true)
	     throw (coral::language_not_found_error, std::exception);
 protected:
	static CORAL_HIDDEN coral::autounref<Language> tryDynLoad(const std::string& smd_model_filename);
};

#ifdef COMPILING_coral
	/* only Coral needs to have these */

	/* Protects all_languages. */
	EXTERN_MUTEX(mutex_languages);
#endif

class EXPORT_coral Language : public Package {
private:
	int version;
	int revision;
	ElementDefinition* root_ed;
 public:
	Language(const std::string& name,
		 int version, int revision);
	virtual ~Language() throw ();
	void setRoot(ElementDefinition* e) throw ();
	ElementDefinition* getRoot() const throw ();
	const int getVersion() const throw() { return this->version; }
	const int getRevision() const throw() { return this->revision; }

	static coral::autounref<coral::Language>
	     __getLanguage__(const std::string& name,
			     int ver, int rev,
			     bool allowdynamicloading = true)
	     throw (coral::language_not_found_error, std::exception);
#ifndef SWIG
	virtual std::string __str__() const;
#endif
};


}

#endif
