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

import copy
import re
import wx
import wx.lib.newevent
import wxx
from activity.models.ui import ui as ui
import app.resources as rc
import model


# Implementing NewArgument
class ArgumentDialog(ui.NewArgument):
    """
    This dialog allows to specify name, type and attributes for an method
    argument. For the argument type, you can choose between known types
    or, if applicable, template type arguments determined by context.
    """
    @wxx.SetInfo(__doc__)
    def __init__(self, parent, method):
        """Initialization. The parent argument represents the mainWindow """
        super(ArgumentDialog, self).__init__(parent)
        self._container = method
        self._types = dict([(x._name, x) for x in method.types])
        self._typei = model.cc.typeinst(type=self._types["int"])
        self._origin = None
        self._autoname = ''  # proposed name
        # add types but not on-the-fly template type
        self.m_type.AppendItems([x for x in self._types.keys() if x != '@'])

        #get nested template types (now easy)
        self._template_types = method.template_types
        if len(self._template_types) > 0:
            self.m_type.AppendItems(self._template_types)
        self.choiceStr = ""
        self.m_type.SetFocus()
        icon = wx.EmptyIcon()
        icon.CopyFromBitmap(rc.GetBitmap("member"))
        self.SetIcon(icon)

    def AutoName(self):
        """Suggest the argument name, based on type"""
        iSel = self.m_type.GetCurrentSelection()
        if iSel == wx.NOT_FOUND:
            return
        s = self.m_name.GetValue()
        if self._autoname != s and s:
            return
        kwargs = {
            'const': (self.m_const.IsChecked() and 'c') or '',
            'reference': (self.m_reference.IsChecked() and 'r') or '',
            'ptr': (self.m_ptr.IsChecked() and 'p') or '',
            'pptr': (self.m_pptr.IsChecked() and 'p') or '',
            'constptr': (self.m_constptr.IsChecked() and 'c') or '',
            'array': (self.m_array.IsChecked() and 'a') or '',
            'typename': self.m_type.GetString(iSel),
        }
        #volatile = (self.m_volatile.IsChecked() and 'v') or ''
        self._autoname = '{const}{reference}{ptr}{pptr}{constptr}{array}{typename}'.format(
            **kwargs)
        self.m_name.SetValue(self._autoname)

    def OnTogglePtr(self, event):
        """Suggest argument name, if untouched"""
        if self.m_ptr.IsChecked():
            self.m_constptr.Enable(True)
            self.m_pptr.Enable(True)
        else:
            self.m_constptr.SetValue(False)
            self.m_pptr.SetValue(False)
            self.m_constptr.Enable(False)
            self.m_pptr.Enable(False)
        self.AutoName()

    def OnToggleConst(self, event):
        """Suggest argument name, if untouched"""
        self.AutoName()

    def OnToggleConstPtr(self, event):
        """Suggest argument name, if untouched"""
        self.AutoName()

    def OnTogglePointerPointer(self, event):
        """Suggest argument name, if untouched"""
        self.AutoName()

    def OnToggleArray(self, event):
        "toggle array event"
        if  self.m_array.IsChecked():
            self.m_textCtrl7.Show(True)
            self.m_textCtrl7.Enable(True)
        else:
            self.m_textCtrl7.Show(False)
            self.m_textCtrl7.Enable(False)
        self.AutoName()

    def OnToggleReference(self, event):
        """Suggest argument name, if untouched"""
        self.AutoName()

    def OnTypeChanged(self, event):
        """This event happens when the return type is changed. The main goal
        of this callback is handling template types for argument specification"""
        iSel = self.m_type.GetCurrentSelection()
        _type = self._types.get(self.m_type.GetString(iSel), None)
        template_args = False
        if _type is not None:
            if _type._template is not None:
                template_args = True
        if template_args is True:
            self.m_staticText67.Enable(True)
            self.m_template_args.Enable(True)
            self.m_staticText68.Enable(True)
        else:
            self.m_staticText67.Enable(False)
            self.m_template_args.Enable(False)
            self.m_staticText68.Enable(False)
            self.m_template_args.SetValue('')
        event.Skip()

    def CopyAttributes(self, argument):
        """Get the atributes"""
        argument._name = self._name
        argument._typei = copy.copy(self._typei)
        argument._default = self._default
        argument.note = self._note

    def SetAttributes(self, argument):
        """Set the attributes"""
        self._origin = argument._name
        self.m_name.SetValue(argument._name)
        ti = argument._typei
        iSel = self.m_type.FindString(ti.type_name)
        self.m_type.SetSelection(iSel)
        self.m_textCtrl8.SetValue(argument._default)
        self.m_const.SetValue(ti._const)
        self.m_ptr.SetValue(ti._ptr)
        self.m_reference.SetValue(ti._ref)
        self.m_pptr.SetValue(ti._ptr_to_ptr)
        self.m_constptr.SetValue(ti._const_ptr)
        self.m_array.SetValue(ti._array)
        if ti._array is True:
            self.m_textCtrl7.Show(True)
            self.m_textCtrl7.Enable(True)
            self.m_textCtrl7.SetValue(str(ti._array_size))
        else:
            self.m_textCtrl7.SetValue('0')
        if ti._type_args is not None:
            self.m_staticText67.Enable(True)
            self.m_template_args.Enable(True)
            self.m_staticText68.Enable(True)
            self.m_template_args.SetValue(ti._type_args)
        if self.m_ptr.IsChecked():
            self.m_constptr.Enable(True)
            self.m_pptr.Enable(True)
        self.m_richText1.SetValue(argument.note)
        self.SetTitle("Edit argument")

    def Validate(self):
        """Validation"""
        self._name = self.m_name.GetValue()
        if len(self._name) == 0:
            wx.MessageBox("Argument name must not be 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("Argument name contains invalid characters", "Error",
                wx.OK | wx.CENTER | wx.ICON_ERROR, self)
            return False
        if  self._origin != self._name:
            if self._container.ExistArgumentNamed(self._name):
                wx.MessageBox("Argument name already exists", "Error",
                    wx.OK | wx.CENTER | wx.ICON_ERROR, self)
                return False
        iSel = self.m_type.GetCurrentSelection()
        if iSel == wx.NOT_FOUND:
            wx.MessageBox("Invalid type", "Error",
                wx.OK | wx.CENTER | wx.ICON_ERROR, self)
            return False
        typename = self.m_type.GetString(iSel)
        if self.m_array.IsChecked():
            asize = int(self.m_textCtrl7.GetValue())
        else:
            asize = 0
        if typename in self._template_types:
            self._typei = model.cc.typeinst(
                type=self._types['@'],
                type_alias=typename,
                const=self.m_const.IsChecked(),
                ptr=self.m_ptr.IsChecked(),
                ref=self.m_reference.IsChecked(),
                ptrptr=self.m_pptr.IsChecked(),
                constptr=self.m_constptr.IsChecked(),
                array=self.m_array.IsChecked(),
                arraysize=asize)
        else:
            _type = self._types[typename]
            if _type._template is not None:
                #we construct type instance with explicit arguments
                type_args = self.m_template_args.GetValue()
                self._typei = model.cc.typeinst(
                    type=_type,
                    type_args=type_args,
                    const=self.m_const.IsChecked(),
                    ptr=self.m_ptr.IsChecked(),
                    ref=self.m_reference.IsChecked(),
                    ptrptr=self.m_pptr.IsChecked(),
                    constptr=self.m_constptr.IsChecked(),
                    array=self.m_array.IsChecked(),
                    arraysize=asize
                )
            else:
                self._typei = model.cc.typeinst(
                    type=self._types[typename],
                    const=self.m_const.IsChecked(),
                    ptr=self.m_ptr.IsChecked(),
                    ref=self.m_reference.IsChecked(),
                    ptrptr=self.m_pptr.IsChecked(),
                    constptr=self.m_constptr.IsChecked(),
                    array=self.m_array.IsChecked(),
                    arraysize=asize
            )
        self._default = self.m_textCtrl8.GetValue()
        self._note = self.m_richText1.GetValue()
        return True

    def get_kwargs(self):
        """returns arguments suitable for creating new object"""
        return {'parent': self._container, 'name': self._name,
            'type': self._typei, 'default': self._default,
            'note': self._note}

    # Handlers for NewArgument events.
    def OnKeyDown(self, event):
        """Listbox selection"""
        keycode = event.GetKeyCode()
        if keycode == wx.WXK_UP or keycode == wx.WXK_NUMPAD_UP:
            print("up\n")
            i = self.m_type.GetSelection()
            if i is not wx.NOT_FOUND and i > 0:
                self.m_type.SetSelection(i - 1)
        elif keycode == wx.WXK_DOWN or keycode == wx.WXK_NUMPAD_DOWN:
            print("down\n")
            i = self.m_type.GetSelection() + 1
            if i > wx.NOT_FOUND and i < len(self._types):
                self.m_type.SetSelection(i)
        elif keycode < 256:
            keychar = chr(keycode)
            if keychar.isalnum() or keycode is wx.WXK_SPACE:
                self.choiceStr += keychar.lower()
                for t in self._types:
                    tl = t.lower()
                    if tl.find(self.choiceStr) == 0:
                        #print "found str "+t+"from "+self.choiceStr+"\n"
                        sel = self.m_type.FindString(t)
                        if sel is not wx.NOT_FOUND:
                            self.m_type.SetSelection(sel)
                            if keycode is not wx.WXK_SPACE:
                                event.Skip()
                            return
            self.choiceStr = ""
        event.Skip()

    def OnCancel(self, event):
        """cancel event handler"""
        self.EndModal(wx.ID_CANCEL)

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


