#!/usr/bin/env python
cache = 0
python = 0
collecting_swig_code = 0
collect_here = ""

import re
import sys

f = sys.argv[1]
for l in file(f, "r"):
    #
    # Weird hack around SwigValueWrapper... It shouldn't be generated(??)
    # but still is
    #
    l = re.sub("SwigValueWrapper<\s*(.*)\s*>\s*result\s*;", "\\1 result;", l)
    #
    # Export the main entry function
    #
    l = re.sub("define SWIGEXPORT(a) a", 'define SWIGEXPORT(a) a __attribute__((visibility("default")))', l)

    
    m = re.search("^(\s+)PyErr_SetString\(PyExc_RuntimeError,\s*\"(.*)\"\);", l)
    if m:
        print m.group(1) + "std::string s(\"" + m.group(2) + "\");"
        print m.group(1) + "(s += \": \") += _e.what();"
        print m.group(1) + "PyErr_SetString(PyExc_RuntimeError, s.c_str());"
        continue

    if python == 0:
        m = re.search("\} swig_type_info;", l)
        if m:
            python = 1
            print l
            l = """
#ifdef __cplusplus
}
#endif


/*
 * Hack so that SWIG can recognize difference between
 * Atoms and Elements, and downcast Commands
 * properly.
 */
#include <coral/core/Element.h>
#include <coral/core/util/TransactionManager.h>
#include <typeinfo>

/*
 * coral extension to SWIG
 * Since SWIG creates new PyObjects for each C++ object,
 * leading to horrible memory consumtion, we must create a map
 * which can return the old Python Object...
 */
#include <ext/hash_map>
#include <utility>

typedef std::pair<void*, swig_type_info*> cpp2py_pair;

struct cpp2py_hash {
       size_t operator()(const cpp2py_pair& c) const {
               return reinterpret_cast<size_t>(c.first) | reinterpret_cast<size_t>(c.second);
       }
};

typedef __gnu_cxx::hash_map<cpp2py_pair, PyObject*, cpp2py_hash> cpp2py_type;
static cpp2py_type cpp2py_cache;

PyObject* getElementPyObject(const coral::Element* ptr);

#ifdef __cplusplus
extern "C" {
#endif

"""
            print l
            l = ""

    if collecting_swig_code == 0:
        m = re.search("^SWIGRUNTIME", l)
        if m:
            collecting_swig_code = 1

    if collecting_swig_code == 1:
        m = re.search("TYPES TABLE \(BEGIN\)", l)
        if m:
            collecting_swig_code = 2

    if collecting_swig_code == 2:
        m = re.search("TYPES TABLE \(END\)", l)
        if m:
            collecting_swig_code = 3
            print l;
            print collect_here;
            l = ""

    if collecting_swig_code != 2 and cache == 0:
        m = re.search("return Py_None", l)
        if m:
            cache = 1
            collect_here += l
            l = ""
            collect_here += "  }\n"
            collect_here += """
	if (type == SWIGTYPE_p_coral__ref_counted) {
		if (coral::Atom* a = dynamic_cast<coral::Atom*>(static_cast<coral::ref_counted*>(ptr))) {
			type = SWIGTYPE_p_coral__Atom;
			ptr = a;
		} else
		if (coral::AbstractElementDefinition* a = dynamic_cast<coral::AbstractElementDefinition*>(static_cast<coral::ref_counted*>(ptr))) {
			type = SWIGTYPE_p_coral__AbstractElementDefinition;
			ptr = a;
		} else 
		if (coral::Collection* a = dynamic_cast<coral::Collection*>(static_cast<coral::ref_counted*>(ptr))) {
			type = SWIGTYPE_p_coral__Collection;
			ptr = a;
                        if (coral::Set* as = dynamic_cast<coral::Set*>(a)) {
                                type = SWIGTYPE_p_coral__Set;
                                ptr = as;
                        } else 
                        if (coral::Sequence* aq = dynamic_cast<coral::Sequence*>(a)) {
                                type = SWIGTYPE_p_coral__Sequence;
                                ptr = aq;
                        } else 
                        if (coral::Bag* ab = dynamic_cast<coral::Bag*>(a)) {
                                type = SWIGTYPE_p_coral__Bag;
                                ptr = ab;
                        } else 
                        if (coral::OrderedSet* ao = dynamic_cast<coral::OrderedSet*>(a)) {
                                type = SWIGTYPE_p_coral__OrderedSet;
                                ptr = ao;
                        }
		}
	}
	if (type == SWIGTYPE_p_coral__AbstractElementDefinition) {
		if (coral::Package* a = dynamic_cast<coral::Package*>(static_cast<coral::AbstractElementDefinition*>(ptr))) {
			type = SWIGTYPE_p_coral__Package;
			ptr = a;
		} else if (coral::ElementDefinition* a = dynamic_cast<coral::ElementDefinition*>(static_cast<coral::AbstractElementDefinition*>(ptr))) {
			type = SWIGTYPE_p_coral__ElementDefinition;
			ptr = a;
		} else if (coral::DatatypeDefinition* a = dynamic_cast<coral::DatatypeDefinition*>(static_cast<coral::AbstractElementDefinition*>(ptr))) {
			type = SWIGTYPE_p_coral__DatatypeDefinition;
			ptr = a;
		}
	}
	if (type == SWIGTYPE_p_coral__Package) {
		if (coral::Language* a = dynamic_cast<coral::Language*>(static_cast<coral::Package*>(ptr))) {
			type = SWIGTYPE_p_coral__Language;
			ptr = a;
		}
	}
	if (type == SWIGTYPE_p_coral__Atom) {
		if (coral::Element* e = dynamic_cast<coral::Element*>(static_cast<coral::Atom*>(ptr))) {
			type = SWIGTYPE_p_coral__Element;
			ptr = e;
		} else
		if (coral::ElementPrimitive* e = dynamic_cast<coral::ElementPrimitive*>(static_cast<coral::Atom*>(ptr))) {
			type = SWIGTYPE_p_coral__ElementPrimitive;
			ptr = e;
		}
	} else if (type == SWIGTYPE_p_coral__Transaction__Command) {
		coral::Transaction::Command* c = static_cast<coral::Transaction::Command*>(ptr);
		if (dynamic_cast<coral::Transaction::ProjectChangeCommand*>(c)) type = SWIGTYPE_p_coral__Transaction__ProjectChangeCommand;
		if (dynamic_cast<coral::Transaction::ProjectNameChangeCommand*>(c)) type = SWIGTYPE_p_coral__Transaction__ProjectNameChangeCommand;
		if (dynamic_cast<coral::Transaction::NewProjectCommand*>(c)) type = SWIGTYPE_p_coral__Transaction__NewProjectCommand;
		if (dynamic_cast<coral::Transaction::ElementChangeSlotCommand*>(c)) type = SWIGTYPE_p_coral__Transaction__ElementChangeSlotCommand;
		if (dynamic_cast<coral::Transaction::ChangeElementPrimitiveCommand*>(c)) type = SWIGTYPE_p_coral__Transaction__ChangeElementPrimitiveCommand;
		if (dynamic_cast<coral::Transaction::ElementChangeSlotCommand*>(c)) type = SWIGTYPE_p_coral__Transaction__ElementChangeSlotCommand;
		if (dynamic_cast<coral::Transaction::ElementNewCommand*>(c)) type = SWIGTYPE_p_coral__Transaction__ElementNewCommand;
		if (dynamic_cast<coral::Transaction::ElementUUIDCommand*>(c)) type = SWIGTYPE_p_coral__Transaction__ElementUUIDCommand;
	}
	if (type == SWIGTYPE_p_coral__DatatypeDefinition) {
		coral::DatatypeDefinition* m = static_cast<coral::DatatypeDefinition*>(ptr);
		if (dynamic_cast<coral::EnumerationDefinition*>(m)) { ptr = m; type = SWIGTYPE_p_coral__EnumerationDefinition; }
	}

       cpp2py_pair findthis(ptr, type);
       cpp2py_type::const_iterator i = cpp2py_cache.find(findthis);
	if (i != cpp2py_cache.end()) {
	       robj = (*i).second;
	       Py_INCREF(robj);
	       return robj;

"""
            continue
    # Note, we forgot the last '}' char because it comes with the next line..
    # urk.
    m = re.search("^\s+return robj", l)
    if m:
        collect_here += """
	
	/*
	 * SPURIOUS INCREF!
	 * This will guarantee that the object is valid when
	 * it is in the cache.
	 */
	Py_INCREF(robj);

	static size_t a = 0;
        static bool reaping_cache = false;
	++a;
	/* Just clear the cache sometimes, _based on cache size_ */
	if (!reaping_cache && (a >= cpp2py_cache.size() / 2)) {
	       reaping_cache = true;
	       a = 0;
	       for (cpp2py_type::iterator i = cpp2py_cache.begin();
	            i != cpp2py_cache.end();
	               ) {
	               PyObject* o = (*i).second;
	               if (o->ob_refcnt == 1) {
	                       cpp2py_type::iterator old = i;
	                       ++i;
	                       cpp2py_cache.erase(old);
			       /* We can probably re-enter
				  SWIG_NewPointerObj via this. */
	                       Py_DECREF(o);
	               } else {
	                       ++i;
	               }
	       }
	       reaping_cache = false;
       }
	cpp2py_cache[cpp2py_pair(ptr, type)] = robj;
        """

    if collecting_swig_code == 1:
        collect_here += l
        l = ""

    print l,

print """
PyObject* getElementPyObject(const coral::Element* ptr) {
  PyObject* resultobj;
  cpp2py_type::iterator i = cpp2py_cache.find(std::make_pair(const_cast<coral::Element*>(ptr), SWIGTYPE_p_coral__Element));
  if (i == cpp2py_cache.end()) return NULL;
  resultobj = SWIG_NewPointerObj((void *) ptr, SWIGTYPE_p_coral__Element, 0);
  return resultobj;
}
"""
