/*
 * Project: MoleCuilder
 * Description: creates and alters molecular systems
 * Copyright (C)  2010-2012 University of Bonn. All rights reserved.
 * 
 *
 *   This file is part of MoleCuilder.
 *
 *    MoleCuilder is free software: you can redistribute it and/or modify
 *    it under the terms of the GNU General Public License as published by
 *    the Free Software Foundation, either version 2 of the License, or
 *    (at your option) any later version.
 *
 *    MoleCuilder is distributed in the hope that it will be useful,
 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *    GNU General Public License for more details.
 *
 *    You should have received a copy of the GNU General Public License
 *    along with MoleCuilder.  If not, see .
 */
/*
 * pyMoleCuilder.cpp
 *
 *  Created on: Sep 21, 2011
 *      Author: heber
 */
// include config.h
#ifdef HAVE_CONFIG_H
#include 
#endif
#include 
#include 
#include 
#include "CodePatterns/MemDebug.hpp"
//!> define all present actions
#include "GlobalListOfActions.hpp"
//!> python wrapping for all of these actions
#include "AllActionPython.hpp"
#include "cleanUp.hpp"
#include "Actions/ActionHistory.hpp"
#include "cleanUp.hpp"
namespace MoleCuilder {
namespace detail {
void module_exit()
{
  // save everything
  std::cout << "Saving." << std::endl;
  saveAll();
  // purge everything
  std::cout << "Cleaning memory." << std::endl;
  cleanUp();
}
void module_reinit()
{
  // save everything
  std::cout << "Saving." << std::endl;
  saveAll();
  // purge everything
  std::cout << "Cleaning static instances from memory." << std::endl;
  purgeStaticInstances();
  // need to init the history before any action is created
  std::cout << "Reinitializing." << std::endl;
  MoleCuilder::ActionHistory::init();
}
} /* namespace detail */
namespace PythonTypes {
inline void IndexError(){
    PyErr_SetString(PyExc_IndexError, "Index out of range");
    boost::python::throw_error_already_set();
}
template
struct vec_item{
    typedef typename T::value_type V;
    static V& get(T& x, int i){
        static V nothing;
        if(i < 0) i += x.size();
        if(i >= 0 && i < int(x.size())) return x[i];
        IndexError();
        return nothing;
    }
    static void set(T& x, int i, V const& v){
        if(i < 0) i += x.size();
        if(i >= 0 && i < int(x.size())) x[i] = v;
        else IndexError();
    }
    static void del(T& x, int i){
        if(i < 0) i += x.size();
        if(i >= 0 && i < int(x.size())) x.erase(x.begin() + i);
        else IndexError();
    }
    static void add(T& x, V const& v){
        x.push_back(v);
    }
};
} /* namespace PythonTypes */
} /* namespace MoleCuilder */
BOOST_PYTHON_MODULE(pyMoleCuilder)
{
  // need to init the history before any action is created
  MoleCuilder::ActionHistory::init();
  // from this moment on, we need to be sure to deeinitialize in the correct order
  // this is handled by the cleanup function
  atexit(MoleCuilder::detail::module_exit);
  // set the docstring of the current module scope
  boost::python::scope().attr("__doc__") = "pyMolecuilder are the python bindings to all Actions of the program suite MoleCuilder.\n\nMoleCuilder is a program to build molecular (dynamics) worlds, allowing you indefinite manipulation, control and analysis over the atoms and molecules within a simulation domain.";
  boost::python::def("reinit", MoleCuilder::detail::module_reinit, "Reinitializes the internal state of the python module as if it had been freshly imported, saves all input files beforehand.");
  // STL Vectors:
  // doubleVec
  boost::python::class_< std::vector< double > >("PythonType_doubleVec")
      .def("__len__", &std::vector< double >::size)
      .def("clear", &std::vector< double >::clear)
      .def("append", &MoleCuilder::PythonTypes::vec_item< std::vector< double > >::add,
            boost::python::with_custodian_and_ward<1, 2>()) // let container keep value
      .def("__getitem__", &MoleCuilder::PythonTypes::vec_item< std::vector< double > >::get,
           boost::python::return_value_policy())
      .def("__setitem__", &MoleCuilder::PythonTypes::vec_item< std::vector< double > >::set,
           boost::python::with_custodian_and_ward<1,2>()) // to let container keep value
      .def("__delitem__", &MoleCuilder::PythonTypes::vec_item< std::vector< double > >::del)
      .def("__iter__", boost::python::iterator< std::vector< double > >())
  ;
#define export_print(z,n,list) \
	BOOST_PP_CAT(export_, BOOST_PP_SEQ_ELEM(n, list))();
#define BOOST_PP_LOCAL_MACRO(n) export_print(~, n, GLOBALLISTOFACTIONS)
#define BOOST_PP_LOCAL_LIMITS  (0, BOOST_PP_DEC(BOOST_PP_SEQ_SIZE(GLOBALLISTOFACTIONS)))
#include BOOST_PP_LOCAL_ITERATE()
#undef instance_print  
}