%module mforms
%{

#include <boost/signals2/signal.hpp>
#include <mforms/view.h>
#include <mforms/form.h>
#include <mforms/button.h>
#include <mforms/checkbox.h>
#include <mforms/textentry.h>
#include <mforms/textbox.h>
#include <mforms/label.h>
#include <mforms/selector.h>
#include <mforms/listbox.h>
#include <mforms/tabview.h>
#include <mforms/box.h>
#include <mforms/panel.h>
#include <mforms/filechooser.h>
#include <mforms/radiobutton.h>
#include <mforms/imagebox.h>
#include <mforms/progressbar.h>
#include <mforms/table.h>
#include <mforms/scrollpanel.h>
#include <mforms/treeview.h>
#include <mforms/wizard.h>
#include <mforms/drawbox.h>
#include <mforms/tabswitcher.h>
#include <mforms/grttreeview.h>
#include <mforms/app.h>
#include <mforms/appview.h>
#include <mforms/utilities.h>
#include <mforms/uistyle.h>
#include <mforms/appview.h>
#include <mforms/sectionbox.h>
#include <mforms/widgets.h>
#include <mforms/menu.h>
#include <mforms/splitter.h>
#include <mforms/webbrowser.h>
#include <mforms/popup.h>
#include <mforms/code_editor.h>
#include <mforms/menubar.h>
#include <mforms/toolbar.h>
#include <mforms/task_sidebar.h>
#include <mforms/simplegrid.h>
#include <mforms/hypertext.h>
#include <mforms/popover.h>
#include <mforms/fs_object_selector.h>
#include <mforms/simpleform.h>

/// begin python specific stuff

struct PyObjectRef 
{
  PyObject *object;
  
  PyObjectRef(PyObject *obj)
    : object(obj)
  {
    Py_XINCREF(obj);
  }

  PyObjectRef(const PyObjectRef &obj)
    : object(obj.object)
  {
    Py_XINCREF(object);
  }

  ~PyObjectRef()
  {
    Py_XDECREF(object);
  }
  
  operator PyObject*()
  {
    return object;
  }
  
  PyObjectRef &operator =(const PyObjectRef &other)
  {
    if (object) Py_XDECREF(object);
    object = other.object;
    Py_XINCREF(object);
    
    return *this;
  }
};



static void show_python_exception()
{
  if (!PyErr_Occurred())
    return;
  std::string reason;
  PyObject *exc, *val, *tb;

  PyErr_Fetch(&exc, &val, &tb);
  PyErr_NormalizeException(&exc, &val, &tb);

  if (val)
  {
    PyObject *tmp = PyObject_Str(val);
    if (tmp)
    {
      reason = PyString_AsString(tmp);
      Py_DECREF(tmp);
    }
  }
  PyErr_Restore(exc, val, tb);

  mforms::Utilities::show_error("Error", std::string("Unhandled exception: ").append(reason), "OK", "", "");
}

class WillEnterPython
{
    PyGILState_STATE state;
  public:
    WillEnterPython() : state(PyGILState_Ensure()) {}
    ~WillEnterPython() {PyGILState_Release(state);}
};

static void call_void_pycallable(PyObjectRef &callable)
{
  PyObject *ret;

  WillEnterPython lock;

  PyObject *args = Py_BuildValue("()");
  ret = PyObject_Call(callable, args, NULL);
  Py_DECREF(args);
  if (!ret)
  {
    show_python_exception();
    PyErr_Print();
  }
  else
    Py_DECREF(ret);
}

static void call_void_string_pycallable(const std::string &s, PyObjectRef &callable)
{
  PyObject *ret;

  WillEnterPython lock;

  PyObject *args = Py_BuildValue("(s)", s.c_str());
  ret = PyObject_Call(callable, args, NULL);
  Py_DECREF(args);
  if (!ret)
  {
    show_python_exception();
    PyErr_Print();
  }
  else
    Py_DECREF(ret);
}


static void call_void_int_pycallable(int arg, PyObjectRef &callable)
{
  PyObject *ret;

  WillEnterPython lock;

  PyObject *args = Py_BuildValue("(i)", arg);
  ret = PyObject_Call(callable, args, NULL);
  Py_DECREF(args);
  if (!ret)
  {
    show_python_exception();
    PyErr_Print();
  }
  else
    Py_DECREF(ret);
}

static void call_void_int_int_pycallable(int row, int column, PyObjectRef &callable)
{
  PyObject *ret;

  WillEnterPython lock;

  PyObject *args = Py_BuildValue("(ii)", row, column);
  ret = PyObject_Call(callable, args, NULL);
  Py_DECREF(args);
  if (!ret)
  {
    show_python_exception();
    PyErr_Print();
  }
  else
    Py_DECREF(ret);
}


static bool call_bool_pycallable(PyObjectRef &callable)
{
  PyObject *ret;

  WillEnterPython lock;

  PyObject *args = Py_BuildValue("()");
  ret = PyObject_Call(callable, args, NULL);
  Py_DECREF(args);
  if (!ret)
  {
    show_python_exception();
    PyErr_Print();
    return false;
  }
  else
  {
    bool r = ret == Py_True;
    Py_DECREF(ret);
    return r;
  }
}

static void call_cell_edited_pycallable(int row, int col, const std::string &value, PyObjectRef &callable)
{
  WillEnterPython lock;
  PyObject *ret;
  PyObject *args = Py_BuildValue("(iis)", row, col, value.c_str());
  ret = PyObject_Call(callable, args, NULL);
  Py_DECREF(args);
  if (!ret)
  {
    show_python_exception();
    PyErr_Print();
  }
  else
    Py_DECREF(ret);
}

static void call_simplegrid_pycallable(const mforms::SimpleGridPath& path, const int col, PyObjectRef &callable)
{
  WillEnterPython lock;

  PyObject* pypath = SWIG_NewPointerObj(SWIG_as_voidptr(&path), SWIGTYPE_p_mforms__SimpleGridPath, SWIG_POINTER_DISOWN | 0 );

  PyObject *args = PyTuple_Pack(2, pypath, PyInt_FromLong(col));
  PyObject *ret = PyObject_Call(callable, args, NULL);
  Py_DECREF(args);
  if (!ret)
  {
    show_python_exception();
    PyErr_Print();
  }
  else
    Py_DECREF(ret);
}

static void call_simplegrid_iconpos_pycallable(const mforms::SimpleGridPath& path, const int col, const mforms::IconPos pos, PyObjectRef &callable)
{
  WillEnterPython lock;

  PyObject* pypath = SWIG_NewPointerObj(SWIG_as_voidptr(&path), SWIGTYPE_p_mforms__SimpleGridPath, SWIG_POINTER_DISOWN | 0 );

  PyObject *args = PyTuple_Pack(3, pypath, PyInt_FromLong(col), PyInt_FromLong((long)pos));
  PyObject *ret = PyObject_Call(callable, args, NULL);
  Py_DECREF(args);
  if (!ret)
  {
    show_python_exception();
    PyErr_Print();
  }
  else
    Py_DECREF(ret);
}


inline boost::function<void ()> pycall_void_fun(PyObject *callable)
{
  return boost::bind(call_void_pycallable, PyObjectRef(callable));
}

inline boost::function<void (std::string)> pycall_void_string_fun(PyObject *callable)
{
  return boost::bind(call_void_string_pycallable, _1, PyObjectRef(callable));
}

inline boost::function<void (int)> pycall_void_int_fun(PyObject *callable)
{
  return boost::bind(call_void_int_pycallable, _1, PyObjectRef(callable));
}

inline boost::function<void (int,int)> pycall_void_int_int_fun(PyObject *callable)
{
  return boost::bind(call_void_int_int_pycallable, _1, _2, PyObjectRef(callable));
}

inline boost::function<bool ()> pycall_bool_fun(PyObject *callable)
{
  return boost::bind(call_bool_pycallable, PyObjectRef(callable));
}

inline boost::function<void (const mforms::SimpleGridPath&, const int)> pycall_simplegridpath_int_fun(PyObject *callable)
{
  return boost::bind(call_simplegrid_pycallable, _1, _2, PyObjectRef(callable));
}

inline boost::function<void (const mforms::SimpleGridPath&, const int, const mforms::IconPos)> pycall_simplegridpath_int_int_fun(PyObject *callable)
{
  return boost::bind(call_simplegrid_iconpos_pycallable, _1, _2, _3, PyObjectRef(callable));
}

/// end python specific stuff


%}

// Macros to define methods for adding Python callbacks as signal handlers
#define SWIG_ADD_SIGNAL_VOID_CALLBACK(method, signal)\
	void add_##method(PyObject *callback) { signal->connect(pycall_void_fun(callback)); }\
	void call_##method() { (*signal)(); }

#define SWIG_ADD_SIGNAL_VOID_STRING_CALLBACK(method, signal)\
	void add_##method(PyObject *callback) { signal->connect(pycall_void_string_fun(callback)); }\
	void call_##method(const char *s) { (*signal)(s ? s : ""); }

#define BOOST_ADD_SIGNAL_VOID_STRING_CALLBACK(method, signal)\
	void add_##method(PyObject *callback) { signal->connect(pycall_void_string_fun(callback)); }\
	void call_##method(const char *s) { (*(signal))(s ? s : ""); }

#define SWIG_ADD_SIGNAL_VOID_INT_CALLBACK(method, signal)\
	void add_##method(PyObject *callback) { signal->connect(pycall_void_int_fun(callback)); }\
	void call_##method(int i) { (*signal)(i); }


#define SWIG_ADD_SIGNAL_VOID_INT_INT_CALLBACK(method, signal)\
	void add_##method(PyObject *callback) { signal->connect(pycall_void_int_int_fun(callback)); }\
	void call_##method(int i, int j) { (*signal)(i, j); }

#define SWIG_ADD_SIGNAL_SIMPLEGRIDPATH_INT_CALLBACK(method, signal)\
	void add_##method(PyObject *callback) { signal->connect(pycall_simplegridpath_int_fun(callback)); }

#define SWIG_ADD_SIGNAL_SIMPLEGRIDPATH_INT_INT_CALLBACK(method, signal)\
	void add_##method(PyObject *callback) { signal->connect(pycall_simplegridpath_int_int_fun(callback)); }


#define SWIG_ADD_SET_BOOL_CALLBACK(method, setter)\
        void method(PyObject *callback) { if (callback != Py_None) setter(pycall_bool_fun(callback)); else setter(boost::function<bool ()>()); }


#if 0
%init %{
  extern void init_mforms_bindings();
  init_mforms_bindings();
%}
#endif

//%feature("ref") Object "$this->retain();"
%feature("unref") Object "$this->release();"

%include std_string.i
%include std_vector.i

%typemap (in) int64_t, boost::int64_t = long long;
%typemap (out) int64_t, boost::int64_t = long long;

%typemap(in) const std::string& {
  if (PyUnicode_Check($input))
  {
    PyObject *tmp = PyUnicode_AsUTF8String($input);
    $1 = new std::string(PyString_AsString(tmp));
    Py_DECREF(tmp);
  }
  else if (PyString_Check($input))
    $1 = new std::string(PyString_AsString($input));
  else
  {
    PyErr_SetString(PyExc_TypeError, "not a string");
    SWIG_fail;
  }
}

%typemap(freearg) const std::string& {
  delete $1;
}

%typemap(out) std::string {
   $result = PyUnicode_DecodeUTF8($1.data(), $1.size(), NULL);
}

%typemap(in) const std::list<std::string>& {
  if (PyList_Check($input)) {
    $1 = new std::list<std::string>();
    for (int c= PyList_Size($input), i= 0; i < c; i++)
    {
      PyObject *item = PyList_GetItem($input, i);
      if (PyUnicode_Check(item))
      {
        PyObject *tmp = PyUnicode_AsUTF8String(item);
        $1->push_back(PyString_AsString(tmp));
        Py_DECREF(tmp);
      }
      else if (PyString_Check(item))
        $1->push_back(PyString_AsString(item));
      else
      {
        delete $1;
        $1 = 0;
        SWIG_exception_fail(SWIG_TypeError, "expected list of strings");
      }
    }
  }
  else
    SWIG_exception_fail(SWIG_TypeError, "expected list of strings");
}

%typemap(freearg) const std::list<std::string>& {
  delete $1;
}

%typemap(in) const std::vector<std::string>& {
  if (PyList_Check($input)) {
    $1 = new std::vector<std::string>();
    for (int c= PyList_Size($input), i= 0; i < c; i++)
    {
      PyObject *item = PyList_GetItem($input, i);
      if (PyUnicode_Check(item))
      {
        PyObject *tmp = PyUnicode_AsUTF8String(item);
        $1->push_back(PyString_AsString(tmp));
        Py_DECREF(tmp);
      }
      else if (PyString_Check(item))
        $1->push_back(PyString_AsString(item));
      else
      {
        delete $1;
        $1 = 0;
        SWIG_exception_fail(SWIG_TypeError, "expected vector of strings");
      }
    }
  }
  else
    SWIG_exception_fail(SWIG_TypeError, "expected vector of strings");
}


%typemap(freearg) const std::vector<std::string>& {
  delete $1;
}

%typemap(argout) std::string &ret_password { 
    PyObject *o= PyUnicode_DecodeUTF8(($1)->data(), ($1)->size(), NULL);
    $result= SWIG_Python_AppendOutput($result, o); 
} 

%typemap(in,numinputs=0) std::string &ret_password(std::string temp) {
    $1 = &temp;
}

%typemap(argout) std::string &ret_value { 
    PyObject *o= PyUnicode_DecodeUTF8(($1)->data(), ($1)->size(), NULL);
    $result= SWIG_Python_AppendOutput($result, o); 
} 

%typemap(in,numinputs=0) std::string &ret_value(std::string temp) {
    $1 = &temp;
}


%typemap(argout) bool &ret_store {
    if (*$1) Py_INCREF(Py_True); else Py_INCREF(Py_False);
    $result= SWIG_Python_AppendOutput($result, *$1 ? Py_True : Py_False);
}

%typemap(in,numinputs=0) bool &ret_store(bool temp) {
    temp = false;
    $1 = &temp;
}



%include std_list.i

namespace std {
%template(doubleList) list<double>;
};

%typemap(in) std::list<double> {
    if (PyList_Check($input)) {
    for (int c= PyList_Size($input), i= 0; i < c; i++)
    {
      PyObject *item = PyList_GetItem($input, i);
      if (PyFloat_Check(item))
        $1.push_back(PyFloat_AsDouble(item));
      else if (PyInt_Check(item))
        $1.push_back(PyInt_AsLong(item));
      else
      {
        SWIG_exception_fail(SWIG_TypeError, "expected list of doubles");
      }
    }
  }
  else
    SWIG_exception_fail(SWIG_TypeError, "expected list of doubles");
}


%pythoncode %{
def newLabel(text=""):
    c = Label(text)
    c.set_managed()
    return c

def newButton(*args):
    c = Button(*args)
    c.set_managed()
    return c

def newCheckBox(*args):
    c = CheckBox(*args)
    c.set_managed()
    return c

def newTextEntry(*args):
    c = TextEntry(*args)
    c.set_managed()
    return c

def newTextBox(*args):
    c = TextBox(*args)
    c.set_managed()
    return c

def newSelector(*args):
    c = Selector(*args)
    c.set_managed()
    return c

def newListBox(*args):
    c = ListBox(*args)
    c.set_managed()
    return c

def newTabView(*args):
    c = TabView(*args)
    c.set_managed()
    return c

def newBox(*args):
    c = Box(*args)
    c.set_managed()
    return c

def newPanel(*args):
    c = Panel(*args)
    c.set_managed()
    return c

def newFileChooser(*args):
    c = FileChooser(*args)
    c.set_managed()
    return c

def newRadioButton(*args):
    c = RadioButton(*args)
    c.set_managed()
    return c

def newImageBox(*args):
    c = ImageBox(*args)
    c.set_managed()
    return c

def newProgressBar(*args):
    c = ProgressBar(*args)
    c.set_managed()
    return c

def newTable(*args):
    c = Table(*args)
    c.set_managed()
    return c

def newScrollPanel(*args):
    c = ScrollPanel(*args)
    c.set_managed()
    return c

def newTreeView(*args):
    c = TreeView(*args)
    c.set_managed()
    return c

def newAppView(*args):
    c = AppView(*args)
    return c

def newDrawBox(*args):
    c = DrawBox(*args)
    c.set_managed()
    return c

def newTabSwitcher(*args):
    c = TabSwitcher(*args)
    c.set_managed()
    return c

def newSectionBox(*args):
    c = SectionBox(*args)
    c.set_managed()
    return c

def newWidgetSeparator(*args):
    c = WidgetSeparator(*args)
    c.set_managed()
    return c

def newWidgetContainer(*args):
    c = WidgetContainer(*args)
    c.set_managed()
    return c

def newHeartbeatWidget(*args):
    c = HeartbeatWidget(*args)
    c.set_managed()
    return c

def newServerInfoWidget(*args):
    c = ServerInfoWidget(*args)
    c.set_managed()
    return c

def newBarGraphWidget(*args):
    c = BarGraphWidget(*args)
    c.set_managed()
    return c

def newLineDiagramWidget(*args):
    c = LineDiagramWidget(*args)
    c.set_managed()
    return c

def newWebBrowser(*args):
    c = WebBrowser(*args)
    c.set_managed()
    return c

def newPopup(*args):
    c = Popup(*args)
    c.set_managed()
    return c

def newTaskSidebar(*args):
    c = TaskSidebar.create(*args)
    c.set_managed()
    return c

def newSimpleGrid(*args):
    c = SimpleGrid(*args)
    c.set_managed()
    return c

def newHyperText(*args):
    c = HyperText(*args)
    c.set_managed()
    return c

%}

%include "../mforms/base.h"
%include "../mforms/view.h"
%include "../mforms/container.h"
%include "../mforms/form.h"
%include "../mforms/button.h"
%include "../mforms/checkbox.h"
%include "../mforms/textentry.h"
%include "../mforms/textbox.h"
%include "../mforms/label.h"
%include "../mforms/selector.h"
%include "../mforms/listbox.h"
%include "../mforms/tabview.h"
%include "../mforms/box.h"
%include "../mforms/panel.h"
%include "../mforms/filechooser.h"
%include "../mforms/radiobutton.h"
%include "../mforms/imagebox.h"
%include "../mforms/progressbar.h"
%include "../mforms/table.h"
%include "../mforms/scrollpanel.h"
%include "../mforms/treeview.h"
%include "../mforms/wizard.h"
//%include "../mforms/grttreeview.h"
%include "../mforms/utilities.h"
%include "../mforms/appview.h"
%include "../mforms/app.h"
%include "../mforms/drawbox.h"
%include "../mforms/tabswitcher.h"
%include "../mforms/sectionbox.h"
%include "../mforms/widgets.h"
%include "../mforms/webbrowser.h"
%include "../mforms/popup.h"
%include "../mforms/menu.h"
%include "../mforms/code_editor.h"
%include "../mforms/task_sidebar.h"
%include "../mforms/simplegrid.h"
%include "../mforms/hypertext.h"
//%include "../../base/base/geometry.h"

 
// Extend classes to include callback support

%extend mforms::Button {
SWIG_ADD_SIGNAL_VOID_CALLBACK(clicked_callback, self->signal_clicked());
}

%extend mforms::ListBox {
SWIG_ADD_SIGNAL_VOID_CALLBACK(changed_callback, self->signal_changed());
}

%extend mforms::TreeView {
SWIG_ADD_SIGNAL_VOID_INT_INT_CALLBACK(activated_callback, self->signal_row_activated());
SWIG_ADD_SIGNAL_VOID_CALLBACK(changed_callback, self->signal_changed());

void set_cell_edited_callback(PyObject *callable)
{
  self->set_cell_edit_handler(boost::bind(call_cell_edited_pycallable, _1, _2, _3, PyObjectRef(callable)));
}
}

%extend mforms::Panel {
SWIG_ADD_SIGNAL_VOID_CALLBACK(toggled_callback, self->signal_toggled());
}

%extend mforms::RadioButton {
SWIG_ADD_SIGNAL_VOID_CALLBACK(clicked_callback, self->signal_clicked());
}

%extend mforms::Selector {
SWIG_ADD_SIGNAL_VOID_CALLBACK(changed_callback, self->signal_changed());
}

%extend mforms::Form {
SWIG_ADD_SIGNAL_VOID_CALLBACK(closed_callback, self->signal_closed());
}

%extend mforms::TextBox {
SWIG_ADD_SIGNAL_VOID_CALLBACK(changed_callback, self->signal_changed());
}

%extend mforms::TextEntry {
SWIG_ADD_SIGNAL_VOID_CALLBACK(changed_callback, self->signal_changed());
}

%extend mforms::Wizard {
SWIG_ADD_SIGNAL_VOID_CALLBACK(next_callback, self->signal_next_clicked());
SWIG_ADD_SIGNAL_VOID_CALLBACK(back_callback, self->signal_back_clicked());
SWIG_ADD_SIGNAL_VOID_CALLBACK(extra_callback, self->signal_extra_clicked());
}

%extend mforms::AppView {
SWIG_ADD_SET_BOOL_CALLBACK(on_close, self->set_on_close);
}

%extend mforms::TabView {
SWIG_ADD_SIGNAL_VOID_CALLBACK(tab_changed_callback, self->signal_tab_changed());
SWIG_ADD_SIGNAL_VOID_INT_CALLBACK(tab_closed_callback, self->signal_tab_closed());
}

%extend mforms::TabSwitcher {
SWIG_ADD_SIGNAL_VOID_CALLBACK(changed_callback, self->signal_changed());
}


%extend mforms::Utilities {
static void add_timeout(float interval, PyObject *callback) { mforms::Utilities::add_timeout(interval, (pycall_bool_fun(callback))); }
}

%extend mforms::WebBrowser {
SWIG_ADD_SIGNAL_VOID_STRING_CALLBACK(loaded_callback, self->signal_loaded());
}

%extend mforms::TaskSidebar {
SWIG_ADD_SIGNAL_VOID_STRING_CALLBACK(on_section_command_callback, self->on_section_command());
}

%extend mforms::SimpleGrid {
SWIG_ADD_SIGNAL_SIMPLEGRIDPATH_INT_CALLBACK(content_edited_callback, self->signal_content_edited());
SWIG_ADD_SIGNAL_SIMPLEGRIDPATH_INT_CALLBACK(ro_content_clicked_callback, self->signal_ro_content_clicked());
SWIG_ADD_SIGNAL_SIMPLEGRIDPATH_INT_INT_CALLBACK(content_action_callback, self->signal_content_action());
}

%extend mforms::HyperText {
BOOST_ADD_SIGNAL_VOID_STRING_CALLBACK(link_click_callback, self->signal_link_click());
}
