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

#include <coral/core/PrimitiveProperties.h>
#include <coral/core/Element.h>
#include <coral/core/CorePrimitives.h>

#include <coral/lib/assert.h>
#include <stdarg.h>

#include <string>

#include <iostream>

namespace coral {

	DatatypeDefinition::DatatypeDefinition(Package* metamodel, const char* name) : AbstractElementDefinition(metamodel, NULL, name) {
		;
	}

	DatatypeDefinition::~DatatypeDefinition() throw () {
		;
	}

	std::string DatatypeDefinition::__str__() const {
		std::string s;
		s += "DatatypeDefinition ";
		s += getName();
		return s;
	}

	Atom* DatatypeDefinition::newAtom() {
		coral_assert(!isAbstractED);
		/* how's this for sucky OOP... */

		if (this->isSubclass(CORE::String))           return new ElementString(this);
		//if (this->isSubclass(CORE::Integer))        return new ElementInteger(this);
		if (this->isSubclass(CORE::UnlimitedInteger)) return new ElementInteger(this);
		//if (this->isSubclass(CORE::Float))          return new ElementDouble(this);
		//if (this->isSubclass(CORE::Double))         return new ElementDouble(this);
		if (this->isSubclass(CORE::UnlimitedReal))    return new ElementDouble(this);
		if (this->isSubclass(CORE::coralPyObj))         return new ElementPyObject(this);

		std::cout << "Can't initialize new Atom with name " << this->getName() << std::endl;
		coral_assert(0);
		return NULL;
	}

	EnumerationDefinition::EnumerationDefinition(Package* metamodel, const char* name) : DatatypeDefinition(metamodel, name) {
		coral_assert(metamodel);
		// This varargs piece doesn't seem to work...
#if 0		
		const char* args;
		va_list arg_list;
		va_start(arg_list, name);
		while ((args = va_arg(arg_list, const char*)) != NULL) {
			this->descriptions.push_back(args);
		}
		va_end(arg_list);
#endif
		/*
		 * Can't have enumerations with no values!
		 * This is not a good warning, since we construct the enumerations using __addEnum__ anyway :(
		 */
#if 0		
		if (this->descriptions.size() == 0) {
			MAY_FAIL(std::cout << "Warning! Enumeration " << package->__language__()->getName() << ":" << name << " without any values!" << std::endl);
		}
#endif
		coral_assert(this->descriptions.size() == 0);
	}

	void EnumerationDefinition::__addEnum__(const char* c) throw () {
		this->descriptions.push_back(c);
	}

	EnumerationDefinition::~EnumerationDefinition() throw () {
		;
	}

	Atom* EnumerationDefinition::newAtom() {
		coral_assert(!isAbstractED);
		return new ElementEnumeration(this);
	}

	std::string EnumerationDefinition::getDescription(int i) const throw () {
		coral_assert(i >= 0);
		coral_assert(static_cast<unsigned int>(i) < this->descriptions.size());
		const std::string& c = this->descriptions[i];
		/* strchr seems to be more portable than index(c, '_') */
		const char* underscore_ptr = strchr(c.c_str(), '_');
		if (underscore_ptr) return std::string(underscore_ptr+1);
		return c;
	}

	int EnumerationDefinition::getIndexByDescription(const char* c) const throw () {
		const char* in_underscore_ptr = strchr(c, '_');
		if (in_underscore_ptr)
			c = in_underscore_ptr + 1;
		int dindex = 0;
		for (std::vector<std::string>::const_iterator i
			     = this->descriptions.begin();
		     i != this->descriptions.end();
		     ++i, ++dindex) {
			if (strcmp((*i).c_str(), c) == 0) {
				return dindex;
			}
			/* Special case when we give "public" instead
			 * of "vk_public".
			 * Accept both. */
			/* strchr seems to be more portable than index(c, '_') */
			const char* underscore_ptr = strchr((*i).c_str(), '_');
			if (underscore_ptr) {
				++underscore_ptr;
				if (strcmp(underscore_ptr, c) == 0) {
					return dindex;
				}
			}
		}

		return -999;
	}

	std::string EnumerationDefinition::__str__() const {
		std::string s;
		s += "EnumerationDefinition ";
		s += getName();
		return s;
	}

}
