/*
 * autounref
 */
%define UNREF_WRAP(classname)
%typemap(python,out) coral::autounref<coral:: ## classname> {
     $result = SWIG_NewPointerObj((void *) result.get(), SWIGTYPE_p_coral__ ## classname, 0);
};
%enddef

UNREF_WRAP(Project)
UNREF_WRAP(Language)
UNREF_WRAP(Package)
UNREF_WRAP(Collection)
UNREF_WRAP(OrderedSet)
UNREF_WRAP(Set)
UNREF_WRAP(Sequence)
UNREF_WRAP(Bag)
UNREF_WRAP(Element)
UNREF_WRAP(Atom)
UNREF_WRAP(ElementDefinition)
UNREF_WRAP(EnumerationDefinition)
UNREF_WRAP(DatatypeDefinition)
UNREF_WRAP(AbstractElementDefinition)

/*
 * Coral UUID
 */

%typemap(python,out) const uuid_t& {
	$result = PyString_FromString($1->c_str());
}

/*
 * std::string
 */
%typemap(python,out) std::string {
	$result = PyString_FromString($1.c_str());
}

%typemap(python,out) const std::string& {
	$result = PyString_FromString($1->c_str());
}

// new swig version 1.3.2x requires fancy typecheck junk
// the objects we receive can in fact be whatever
// but we are guaranteed ( by the %typechecks below)
// that at least it should be possible to convert it
%typemap(python,in) std::string& (std::string temp) {
     PyObject* p1 = PyObject_Str($input);
     temp = std::string(PyString_AsString(p1));
     $1 = &temp;
     Py_DECREF(p1);
}

%typemap(python,in) std::string (std::string temp) {
     PyObject* p1 = PyObject_Str($input);
     temp = std::string(PyString_AsString(p1));
     $1 = &temp;
     Py_DECREF(p1);
}

%typemap(python,in) int {
	$1 = PyInt_AsLong($input);
}

// We don't use floats in Coral, so only converting Double is ok...
%typemap(python,in) double {
	$1 = PyFloat_AsDouble($input);
}

%typemap(python,in) bool {
	$1 = PyObject_IsTrue($input);
}



// new swig version 1.3.2x requires fancy typecheck junk
%typecheck(SWIG_TYPECHECK_CHAR) std::string {
     $1 = ($input->ob_type->tp_str != NULL);
}

%typemap(typecheck) std::string& = std::string;
%typemap(typecheck) const std::string = std::string;
%typemap(typecheck) const std::string& = std::string;

%typecheck(SWIG_TYPECHECK_CHAR) int {
  PyNumberMethods* nb;
  $1 = ((nb = $input->ob_type->tp_as_number) != NULL && nb->nb_int != NULL);
}

// We don't use floats in Coral, so only checking double is OK.
%typecheck(SWIG_TYPECHECK_CHAR) double {
  PyNumberMethods* nb;
  $1 = ((nb = $input->ob_type->tp_as_number) != NULL && nb->nb_float != NULL);
}

// accept anything as bool
%typecheck(SWIG_TYPECHECK_CHAR) bool {
  $1 = 1;
}

%typecheck(SWIG_TYPECHECK_CHAR) coral::modelcopy_map_t* {
  $1 = PyDict_Check($input);
}

/*
 * unicodestring
 */
%typemap(python,out) coral::unicodestring {
	$result = $1.toPython();
}

%typemap(python,out) const coral::unicodestring& {
	$result =  $1->toPython();
}

%typemap(python,in) coral::unicodestring& (coral::unicodestring temp) {
     temp = coral::unicodestring($input);
     $1 = &temp;
}

/*
 * std::vector<std::string>
 */
%typemap(python,out) std::vector<std::string> {
	$result = PyList_New($1.size());
	if (!$result) {
		$result = Py_None;
		Py_INCREF($result);
	} else {
		int pos = 0;
		for (std::vector<std::string>::const_iterator i = $1.begin();
		     i != $1.end();
		     ++i, ++pos) {
			PyObject* s = PyString_FromString((*i).c_str());
			if (!s) {
				Py_DECREF($result);
				$result = Py_None;
				Py_INCREF($result);
				break;
			}
			(void) PyList_SetItem($result, pos, s);
		}
	}
}

%typemap(python,in) std::vector<std::string> {
	if (!PyList_Check($input)) {
		PyErr_SetString(PyExc_TypeError,
				"Must receive list as input!");
		return NULL;

	}
	int size =  PyList_Size($input);
	for (int i = 0; i < size; ++i) {
		PyObject* o = PyList_GetItem($input, i);
		if (!PyString_Check(o)) {
			PyErr_SetString(PyExc_TypeError,
					"Must receive list of strings as input!");
			return NULL;
		}
		$1.push_back(PyString_AsString(o));
	}
}

/*
 * std::map<Element*, Element*>
 */
%typemap(python,out) coral::modelcopy_map_t* {
	$result = PyDict_New();
	if (!$result) {
		$result = Py_None;
		Py_INCREF($result);
	} else {
	}
}

%typemap(python, in) coral::modelcopy_map_t* {
     if (!PyDict_Check($input)) {
	  PyErr_SetString(PyExc_TypeError,
			  "Must receive dict as input!");
	  return NULL;
     } else {
	  $1 = new coral::modelcopy_map_t();
     }
}

%typemap(python, freearg) coral::modelcopy_map_t* {
     if ($1) {
	  delete $1;
     }
}

%typemap(python, argout) coral::modelcopy_map_t* {
     if ($1) {
	  for (std::map<const coral::Element*, const coral::Element*>::const_iterator i = $1->begin();
	       i != $1->end();
	       ++i) {
	       const coral::Element* e1 = i->first;
	       const coral::Element* e2 = i->second;
	       // BUG should unref in case of exceptions
	       PyObject* p1 =
		    SWIG_NewPointerObj(const_cast<coral::Element*>(e1),
				       SWIGTYPE_p_coral__Element,
				       0);
	       PyObject* p2 =
		    SWIG_NewPointerObj(const_cast<coral::Element*>(e2),
				       SWIGTYPE_p_coral__Element,
				       0);
	       int err = PyObject_SetItem($input, p1, p2);
	       coral_assert(err == 0);
	  }
     }
}



/*
 * std::pair<double, double>
 */
%typemap(python,out) std::pair<double, double> {
	$result = PyList_New(2);
	if (!$result) {
		$result = Py_None;
		Py_INCREF($result);
	} else {
		PyObject* d1 = PyFloat_FromDouble($1.first);
		PyObject* d2 = PyFloat_FromDouble($1.second);
		if (d1 && d2) {
			(void) PyList_SetItem($result, 0, d1);
			(void) PyList_SetItem($result, 1, d2);
		} else {
			if (d1) Py_DECREF(d1);
			if (d2) Py_DECREF(d2);
			Py_DECREF($result);
			$result = Py_None;
			Py_INCREF($result);
		}
	}
}


/*
 * std::vector<coral::Project*>
 */
%typemap(python,out) std::vector<coral::Project*> {
	$result = PyList_New($1.size());
	if (!$result) {
		$result = Py_None;
		Py_INCREF($result);
	} else {
		int pos = 0;
		for (std::vector<coral::Project*>::const_iterator i = $1.begin();
		     i != $1.end(); ++i, ++pos) {
			PyObject* s =
				SWIG_NewPointerObj(*i,
						   SWIGTYPE_p_coral__Project,
						   0);
			if (!s) {
				Py_DECREF($result);
				$result = Py_None;
				Py_INCREF($result);
				break;
			}
			(void) PyList_SetItem($result, pos, s);
		}
	}
}

/*
 * FILE*
 */
%typemap(python,in) std::FILE* {
     $1 = PyFile_AsFile($input);
}

%typemap(python,out) std::size_t {
     $result = PyInt_FromLong($1);
}

/* This doesn't work for some reason... */
%typemap(python,out) const subclasses_type& {
  $result = PyList_New($input.size());
  coral_assert($result);
  for (int pos = 0; $input::const_iterator i = $input.begin(); i != $input.end(); ++i, ++pos) {
    PyObject* obj;
    obj = SWIG_NewPointerObj((void *) *i, SWIGTYPE_p_Coral__AbstractElementDefinition, 0);
    coral_assert(obj);
    PyList_SetItem($result, pos, obj);
  }
  return $result;
}

%template(ModelFeatureIterator) PythonSequenceIteratorSecond<coral::Element::slots_type::iterator, coral::Slot*>;

%template(PropertyIterator) PythonSequenceIteratorDirect<coral::ElementDefinition::ordered_properties_type::const_iterator, coral::Property*>;


%template(SuperClassesIterator) PythonSequenceIteratorDirect<coral::AbstractElementDefinition::superclasses_type::iterator, coral::AbstractElementDefinition*>;
%template(SubClassesIterator) PythonSequenceIteratorDirect<coral::AbstractElementDefinition::subclasses_type::iterator, coral::AbstractElementDefinition*>;
%template(AllSuperClassesIterator) PythonSequenceIteratorDirect<coral::AbstractElementDefinition::topologic_all_superclasses_type::iterator, coral::AbstractElementDefinition*>;

%template(MDAEDIterator) PythonSequenceIteratorDirect<coral::Package::elements_type::iterator, coral::AbstractElementDefinition*>;

%template(OperationsIterator) PythonSequenceIteratorDirect<coral::AbstractElementDefinition::operations_type::iterator, coral::PyOperation*>;


%exception __getitem__ {
     try {
	  $action
     } catch (std::out_of_range &e) {
	  PyErr_SetString(PyExc_IndexError, e.what());
	  return NULL;
     }
}

%exception __getSlotByNameInsert__ {
     try {
	  $action
     } catch (std::invalid_argument &e) {
	  PyErr_SetString(PyExc_IndexError, e.what());
	  return NULL;
     }
}

%exception __getSlotByNameInsertElement__ {
     try {
	  $action
     } catch (std::invalid_argument &e) {
	  PyErr_SetString(PyExc_IndexError, e.what());
	  return NULL;
     }
}

