"""Subclass of NewBoostPythonModule, which is generated by wxFormBuilder."""

import wx
import re
import wxx
import app.resources as rc
import model.cc
from activity.models.ui import ui as ui


# Implementing NewBoostPythonModule
class BoostPythonModuleDialog(ui.NewBoostPythonModule):
    """
    Using this dialog you can export classes and methods
    from your project using boost.python.
    """
    @wxx.SetInfo(__doc__)
    def __init__(self, parent, container):
        """Initialize dialog"""
        self._container = container
        self._project = container.project
        self._eert = {}
        super(BoostPythonModuleDialog, self).__init__(parent)
        icon = wx.EmptyIcon()
        icon.CopyFromBitmap(rc.GetBitmap("pyboost"))
        self.SetIcon(icon)
        # dialog initialization: We insert all the classes and methods
        # from the project
        self.m_textCtrl21.SetValue(self._project.name)
        self.imglist = rc.GetBitmapImageList()
        self.m_tree.SetImageList(self.imglist)
        self._root = self.m_tree.AddRoot(self._project.name, ct_type=1, data=None)
        self.m_tree.SetItemImage(self._root, image=self._project.bitmap_index)
        self.append_classes(self._root, self._project)
        self.append_functions(self._root, self._project)

    def append_classes(self, pid, container):
        """Append classes to tree"""
        cls = container.inner_class
        f = lambda x: x.parent.inner_class == cls
        for cls in container(model.cc.Class, filter=f, cut=True):
            if cls._template:
                continue
            item = self.m_tree.AppendItem(pid, cls.tree_label, ct_type=1, data=cls)
            self._eert[cls] = item
            self.m_tree.SetItemImage(item, image=cls.bitmap_index)
            # add nested classes
            # future : self.append_classes(item, cls)
            self.append_methods(item, cls)

    def append_functions(self, pid, container):
        """Append functions to tree"""
        for fn in container(model.cc.Function):
            if fn._template:
                continue
            item = self.m_tree.AppendItem(pid, fn.tree_label, ct_type=1, data=fn)
            self._eert[fn] = item
            self.m_tree.SetItemImage(item, image=fn.bitmap_index)

    def append_methods(self, pid, container):
        """Append functions to tree"""
        cls = container.inner_class
        f = lambda x: x.parent.inner_class == cls
        classes = (model.cc.MemberMethod, model.cc.Constructor, model.cc.IsClassMethod)
        for fn in container(*classes, filter=f, cut=True):
            if fn._template:
                continue
            item = self.m_tree.AppendItem(pid, fn.tree_label, ct_type=1, data=fn)
            self._eert[fn] = item
            self.m_tree.SetItemImage(item, image=fn.bitmap_index)

    def is_checked(self, obj):
        """Check if the corresponding tree element is checked"""
        return self.m_tree.IsItemChecked(self._eert[obj])

    def is_included_class(self, cls):
        """Check if some class must be included or not in exports"""
        if self.is_checked(cls):
            return True
        # but even if the class is not included, if some method is included,
        # the class must be included
        f = lambda x: x.parent.inner_class == cls
        classes = (model.cc.MemberMethod, model.cc.Constructor, model.cc.IsClassMethod)
        try:
            (fn for fn in cls(*classes, filter=f, cut=True) if self.is_checked(fn)).next()
            return True
        except:
            return False

    def scan_classes(self, container, tabs=0):
        """Genera el codigo que corresponde a la exportacion de las clases"""
        cls = container.inner_class
        f = lambda x: x.parent.inner_class == cls
        content = ''
        self._uids = []
        for cls in container(model.cc.Class, filter=f, cut=True):
            if self.is_included_class(cls):
                self._uids.append(cls.uid)
                members = self.scan_member(cls, tabs + 1)
                note = cls.note
                if note:
                    header = '{prefix}class_<{name}>("{name}", "{note}", no_init)'
                else:
                    header = '{prefix}class_<{name}>("{name}", no_init)'
                header = header.format(
                    prefix='\t' * tabs,
                    name=cls.name,
                    note=note
                    )
                content += '{header}\n{members}{prefix};\n'.format(
                    prefix='\t' * tabs,
                    header=header,
                    members=members)
        return content

    def scan_member(self, container, tabs=0):
        """Genera el codigo que corresponde a la exportacion de los miembros de clase"""
        cls = container.inner_class
        f = lambda x: x.parent.inner_class == cls
        classes = (model.cc.MemberMethod, model.cc.Constructor, model.cc.IsClassMethod)
        content = ''
        methods = container(*classes, filter=f, cut=True)
        ctors = [x for x in methods if type(x) is model.cc.Constructor]
        other = [x for x in methods if type(x) is not model.cc.Constructor]
        for fn in ctors:
            if not self.is_checked(fn):
                continue
                # We process a ctor
            self._uids.append(fn.uid)
            args = fn[model.cc.Argument]
            note = fn.note
            if args:
                arglist = ', '.join(['"{name}"'.format(name=x.name) for x in args])
                argTemp = ', '.join([str(x._typei).format('') for x in args])
                if note:
                    expr = '{prefix}.def(init<{argTemp}>(args({arglist}), "{note}")\n'
                else:
                    expr = '{prefix}.def(init<{argTemp}>(args({arglist}))\n'
            else:
                arglist = ''
                argTemp = ''
                if note:
                    expr = '{prefix}.def(init("{note}")\n'
                else:
                    expr = '{prefix}.def(init())\n'
            content += expr.format(
                prefix='\t' * tabs,
                argTemp=argTemp,
                arglist=arglist,
                note=note)
        for fn in other:
            if not self.is_checked(fn):
                continue
            self._uids.append(fn.uid)
            # We process a standard method
            # check arguments
            args = fn[model.cc.Argument]
            note = fn.note
            if args:
                arglist = ', '.join(['arg("{name}")'.format(name=x.name) for x in args])
                if note:
                    expr = '{prefix}.def("{name}", &{clsname}::{name}, ({arglist}), "{note}")\n'
                else:
                    expr = '{prefix}.def("{name}", &{clsname}::{name}, ({arglist}))\n'
            else:
                arglist = ''
                if note:
                    expr = '{prefix}.def("{name}", &{clsname}::{name}, "{note}")\n'
                else:
                    expr = '{prefix}.def("{name}", &{clsname}::{name})\n'
            content += expr.format(
                prefix='\t' * tabs,
                name=fn.name,
                clsname=cls.name,
                arglist=arglist,
                note=note
                )
        return content

    def SetAttributes(self, bpm):
        """Copy object attributes to dialog"""
        self.m_textCtrl21.SetValue(bpm._name)
        for obj in self._eert:
            if type(obj) is model.cc.MemberMethod:
                continue
            if obj.uid in bpm._uids:
                self.m_tree.CheckItem(self._eert[obj], True)
            else:
                self.m_tree.CheckItem(self._eert[obj], False)
        for obj in self._eert:
            if type(obj) is not model.cc.MemberMethod:
                continue
            if obj.uid in bpm._uids:
                self.m_tree.CheckItem(self._eert[obj], True)
            else:
                self.m_tree.CheckItem(self._eert[obj], False)
        self.SetTitle("Edit boost.python module")

    def CopyAttributes(self, bpm):
        """Copy dialog selections to object"""
        content = self.scan_classes(self._project, 1)
        bpm._name = self._name
        bpm._header = 'bpy_{self._name}.h'.format(self=self),
        bpm._source = 'bpy_{self._name}.cpp'.format(self=self),
        bpm._content = 'BOOST_PYTHON_MODULE({self._name})\n{{\n{content}\n}}\n'.format(self=self, content=content)
        bpm._uids = self._uids

    def get_kwargs(self):
        """This method doesn't return kwargs because creation is carried out inplace"""
        # First, create the content
        content = self.scan_classes(self._project, 1)
        kwargs = {
            'parent': self._container,
            'header': 'bpy_{self._name}.h'.format(self=self),
            'source': 'bpy_{self._name}.cpp'.format(self=self),
            'content': 'BOOST_PYTHON_MODULE({self._name})\n{{\n{content}\n}}\n'.format(self=self, content=content),
            'name': self._name,
            'uids': self._uids
        }
        return kwargs

    def Validate(self):
        """implement OnOK button"""
        self._name = self.m_textCtrl21.GetValue().strip()
        if len(self._name) == 0:
            wx.MessageBox("Module name must be non empty", "Error",
                wx.OK | wx.CENTER | wx.ICON_ERROR, self)
            return False
        if re.match("^[A-Za-z_][0-9A-Za-z_]*$", self._name) is None:
            wx.MessageBox("Module name contains invalid characters", "Error",
                wx.OK | wx.CENTER | wx.ICON_ERROR, self)
            return False
        return True

    # Handlers for NewBoostPythonModule events.
    def OnCancel(self, event):
        """Handles cancel button"""
        self.EndModal(wx.ID_CANCEL)

    def OnOK(self, event):
        """Handles ok button"""
        if self.Validate():
            self.EndModal(wx.ID_OK)
