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

import wx
import wxx
import tran
import app
from ctx import theContext as context
import model
import gettext
from app.ui.pane import BasePane
from activity.models.ui import ui as ui
from  activity.models.ui import view as view
from activity.models.ui import dlg

_ = gettext.gettext

OP_INSERT_CLASS = 1000
OP_MOVE_CLASS = 1001
OP_CLICK_CLASS = 1002
OP_CLICK_ELEMENT = 1003
OP_CLICK_SEGMENT = 1005
OP_MOVE_ELEMENT = 1004
OP_MOVE_SEGMENT = 1006

NEW_CLASS_RECTSIZE = wx.Size(80, 20)

ID_PRINT = 1026
CLOSE_PANE_ID = wx.ID_HIGHEST + 100


# Implementing ClassDiagramPanePane
class DiagramPane(BasePane, ui.ClassDiagramPane):
    """Implements classsDiagramPane pane"""
    _addNoteId = wx.Window.NewControlId()
    _selectClassesId = wx.Window.NewControlId()

    def __init__(self, parent, obj):
        """Initialization"""
        super(DiagramPane, self).__init__(parent)
        self._object = obj  # ClassDiagram object
        self.parent = parent  # notebook object
        self._operation = None
        self._element = None  # context element
        self._create_menus()
        #self._create_toolbars()
        self._bind_events()
        self._set_accelerators()

        szP = self.parent.GetClientSize()
        szP.y -= self.parent.GetTabCtrlHeight()
        sz = wx.Size(self._object._width, self._object._height)
        self.SetSize(szP)
        self.DoSetVirtualSize(sz.x, sz.y)
        self.SetScrollbars(5, 5, sz.x // 5, sz.y // 5, 0, 0, True)
        self._inPopup = False
        self.SetFocus()
        self.EnableScrolling(True, True)
        self.selected = None

    def Refresh(self):
        """Update editor from external changes (like undo/redo)"""
        self.Freeze()
        dc = self.AdjustedDC()
        dc.Clear()
        self._object.DrawClassDiagram(dc)
        self.Thaw()
        self.Update()

    def _create_menus(self):
        """Create a custom menu entry in main menu"""
        self._menu = wx.Menu()

        # Select classes
        self._select_classes_menu = wx.MenuItem(self._menu, self._selectClassesId, u"Select classes",
            u"select classes to be shown in this diagram", wx.ITEM_NORMAL)
        #self._newRepo.SetBitmap(wx.ArtProvider.GetBitmap(wx.ART_NEW, wx.ART_MENU))
        self._menu.AppendItem(self._select_classes_menu)

        #Add note
        self._add_note_menu = wx.MenuItem(self._menu, self._addNoteId, u"Add note",
            u"add text note tho this diagram", wx.ITEM_NORMAL)
        #self._openRepo.SetBitmap(wx.ArtProvider.GetBitmap(wx.ART_FILE_OPEN, wx.ART_MENU))
        self._menu.AppendItem(self._add_note_menu)

    def _bind_events(self):
        """Bind events to this frame"""
        self.BindSpecial(wx.EVT_MENU, self.OnSelectClasses, id=self._selectClassesId)
        self.BindSpecial(wx.EVT_UPDATE_UI, self.OnUpdateSelectClasses, id=self._selectClassesId)

        self.BindSpecial(wx.EVT_UPDATE_UI, self.OnUpdateDiagramAddNote, id=self._addNoteId)
        self.BindSpecial(wx.EVT_MENU, self.OnDiagramAddNote, id=self._addNoteId)

        frame = context.frame

        self.BindSpecial(wx.EVT_UPDATE_UI, self.OnUpdateEditProperties, id=app.ID_EDIT_PROPERTIES)
        self.BindSpecial(wx.EVT_MENU, self.OnEditProperties, id=app.ID_EDIT_PROPERTIES)

        # add class
        self.BindSpecial(wx.EVT_UPDATE_UI, self.OnUpdateAddClass, id=view.ModelsView._addClassId)
        self.BindSpecial(wx.EVT_MENU, self.OnAddClass, id=view.ModelsView._addClassId)
        # save diagram
        self.BindSpecial(wx.EVT_UPDATE_UI, self.OnUpdateSave, id=app.ID_SAVE_PROJECT)
        self.BindSpecial(wx.EVT_MENU, self.OnSave, id=app.ID_SAVE_PROJECT)
        # delete element
        self.BindSpecial(wx.EVT_UPDATE_UI, self.OnUpdateDelete, id=app.ID_DELETE)
        self.BindSpecial(wx.EVT_MENU, self.OnDelete, id=app.ID_DELETE)
        # close pane
        self.BindSpecial(wx.EVT_MENU, frame.CloseCurrentDocPane, id=CLOSE_PANE_ID)
        # add relations
        self.BindSpecial(wx.EVT_UPDATE_UI, self.OnUpdateAddRelation, id=view.ModelsView._addRelationId)
        self.BindSpecial(wx.EVT_MENU, self.OnAddRelation, id=view.ModelsView._addRelationId)

    def _set_accelerators(self):
        """Set the acceleratos"""
        frame = context.frame
        aTable = wx.AcceleratorTable([
            wx.AcceleratorEntry(wx.ACCEL_NORMAL, wx.WXK_DELETE, frame.delete.GetId()),
            wx.AcceleratorEntry(wx.ACCEL_ALT | wx.ACCEL_CTRL, ord('C'), view.ModelsView._addClassId),
            wx.AcceleratorEntry(wx.ACCEL_ALT | wx.ACCEL_CTRL, ord('F'), ui.ID_TOGGLE_FULLSCREEN),
            wx.AcceleratorEntry(wx.ACCEL_CTRL, ord('W'), CLOSE_PANE_ID)
        ])
        self.SetAcceleratorTable(aTable)

    def NotifyShow(self):
        """Called where the view is show"""
        context.frame.AddMainMenu('Diagram', self._menu)
        super(DiagramPane, self).NotifyShow()

    def OnGetFocus(self, event):
        """Called where the view get focus"""
        super(DiagramPane, self).NotifyShow()

    def NotifyHide(self):
        """Called where the git view is hidden"""
        context.frame.RemoveMainMenu('Diagram', self._menu)
        super(DiagramPane, self).NotifyHide()

    def OnLeaveFocus(self, event):
        """Called where the view get focus"""
        super(DiagramPane, self).NotifyHide()

    @tran.TransactionalMethod('delete elements')
    def OnDelete(self, event):
        """Handle delete event"""
        dead = [x for x in self._object._elements if x.Selected()]
        for element in dead:
            element._obj.Delete()
        return True

    def OnUpdateDelete(self, event):
        """Handle update delete event"""
        for element in self._object._elements:
            if element.Selected():
                event.Enable(True)
                return
        event.Enable(False)

    @tran.TransactionalMethod('modify diagram')
    def OnSave(self, event):
        """Handle save event"""
        self.Commit()
        return True

    def OnUpdateSave(self, event):
        """Handle update save event"""
        event.Enable(False)
        return True

    def Commit(self):
        """Update document"""
        pass

    # Handlers for ClassDiagramPanePane events.
    def OnPaintClassDiagram(self, event):
        """Repaint class DiagramPane"""
        dc = wx.PaintDC(self)
        self.PrepareDC(dc)
        self._object.DrawClassDiagram(dc)

    def OnDiagramMenu(self, event):
        """Do DiagramPane menu"""
        if not type(self._object) is model.ClassDiagram:
            return
        self.SetFocus()
        wx.SetCursor(wx.StockCursor(wx.CURSOR_ARROW))
        position = self.GetLocalPosition(False)
        (item, where) = self._object.HitTest(position)
        if type(item) is tuple:
            # se ha hecho click en un segmento
            item = item[0]
            menu = wx.Menu()
            if type(item._obj) is model.cc.Relation:
                self.selected = item._obj._FROM
            else:
                self.selected = item._obj
            menu.Append(context.frame.editProperties.GetId(),
                "Edit properties")
        elif item is None:
            menu = app.ui.clone_mnu(self._menu, enabled=True, notitle=True)
            if menu is None:
                return
        elif where is model.IN_LINE_MARK:
            menu = wx.Menu()
            self.selected = item._obj
            menu.Append(context.frame.editProperties.GetId(),
                "Edit properties")
        elif where is model.IN_SHAPE:
            if type(item) is model.ClassElement:
                menu = wx.Menu()
                self.selected = item._obj
                menu.Append(view.ModelsView._addRelationId, "Add relation")
                menu.Append(context.frame.editProperties.GetId(),
                    "Edit properties")
            elif type(item) is model.NoteElement:
                menu = wx.Menu()
                self.selected = item._obj
                menu.Append(context.frame.editProperties.GetId(),
                    "Edit properties")
        else:
            return
        self._inPopup = True
        self.PopupMenu(menu, event.GetPosition())
        menu.Destroy()

    def AdjustedDC(self):
        """Prepare DC for drawing"""
        dc = wx.ClientDC(self)
        self.PrepareDC(dc)
        return dc
        pen = wx.BLACK_PEN
        brush = wx.Brush(wx.TheColourDatabase.Find('AQUAMARINE'))
        dc.SetPen(pen)
        pen.SetWidth(2)
        dc.SetBrush(brush)
        dc.SetLogicalFunction(wx.COPY)
        return dc

    def GetLocalPosition(self, normalize=True, pos=wx.DefaultPosition):
        """Get the local position normalized to grid"""
        if pos == wx.DefaultPosition:
            pos = wx.GetMousePosition()
        point = self.ScreenToClient(pos)
        point = self.CalcUnscrolledPosition(point)
        if normalize:
            point.x -= (point.x % (5 * self._object._zoom))
            point.y -= (point.y % (5 * self._object._zoom))
        return point

    @tran.TransactionalMethod('add class')
    def OnAddClass(self, event):
        """addClass handler"""
        parent = self._object.parent
        topClass = parent.outer_class
        #for the moment we dont allow nested classes
        if topClass is not None:
            parent = topClass.parent
        position = self.GetLocalPosition()
        dialog = dlg.ClassDialog(self, parent)
        if dialog.ShowModal() != wx.ID_OK:
            return False
        cls = model.cc.Class(parent=parent, name=dialog._name,
            template=dialog._template, template_types=dialog._template_types,
            prefix=dialog._memberPrefix, notes=dialog._note)
        # draw in diagram
        self._object.SaveState()
        self._element = self._object.AddClass(cls, position)
        #self._point = self.GetLocalPosition()
        #self._offset = wx.Point(0, 0)
        #self._element = self._object.AddClass(cls, self._point)
        self.SetFocus()
        self._element.Draw(self.AdjustedDC())
        return True

    def OnMouseMove(self, event):
        """Handles mouse move event"""
        #if not self.m_bitmap1.HasCapture():
        #    return
        if self._operation == OP_CLICK_ELEMENT:
            # TransactionStack.DoBeginTransaction("move element")
            # TransactionStack.DoSaveState(self._object)
            self._operation = OP_MOVE_ELEMENT
        if self._operation == OP_CLICK_SEGMENT:
            # TransactionStack.DoBeginTransaction("move segment")
            # TransactionStack.DoSaveState(self._object)
            self._operation = OP_MOVE_SEGMENT
        if self._operation == OP_MOVE_ELEMENT:
            dc = self.AdjustedDC()
            self._point = self.GetLocalPosition()
            self.Freeze()
            shift = self._point + self._offset - self._element._pos
            self._object.EraseSelected(dc)
            self._object.ShiftSelected(shift)
            self._object.DrawClassDiagram(dc)
            self.Thaw()
        if self._operation == OP_MOVE_SEGMENT:
            dc = self.AdjustedDC()
            self._point = self.GetLocalPosition()
            self.Freeze()
            pos = self._point + self._offset
            self._element.Erase(dc)
            self._index = self._element.ShiftSegment(self._index, pos)
            self._object.DrawClassDiagram(dc)
            self.Thaw()
        elif self._operation in (OP_INSERT_CLASS, OP_MOVE_CLASS):
            dc = self.AdjustedDC()
            self._point = self.GetLocalPosition()
            self.Freeze()
            self._element.Erase(dc)
            self._object.MoveElement(self._element, self._point + self._offset)
            self._object.DrawClassDiagram(dc)
            self.Thaw()
        else:
            (obj, where) = self._object.HitTest(self.GetLocalPosition())
            if where is model.IN_SHAPE:
                wx.SetCursor(wx.StockCursor(wx.CURSOR_HAND))
                return
            elif where is model.IN_LINE_MARK:
                segment = obj[1]
                if segment._orientation is wx.HORIZONTAL:
                    wx.SetCursor(wx.StockCursor(wx.CURSOR_SIZENS))
                else:
                    wx.SetCursor(wx.StockCursor(wx.CURSOR_SIZEWE))
            else:
                wx.SetCursor(wx.StockCursor(wx.CURSOR_ARROW))

    def OnMouseDown(self, event):
        "mouse left down"
        self.SetFocus()
        wx.YieldIfNeeded()
        if self._operation is None:
            p = self.GetLocalPosition(False)
            (element, where) = self._object.HitTest(p)
            # some hit-test returns subelements
            if type(element) is tuple:
                subelement = element[1]
                element = element[0]
            else:
                subelement = None
            #new
            dc = self.AdjustedDC()
            if where is not model.NO_WHERE:
                self._object.BringToFront(element)
                if wx.GetKeyState(wx.WXK_CONTROL):
                    element.ToggleSelected()
                    # if there are two classes selected quick show relation dialog
                    if element.Selected():
                        self._element = element
                        self._point = self.GetLocalPosition()
                        if type(element) is model.ClassElement:
                            l = [x for x in self._object.selected_elements if type(x) is model.ClassElement and x != element]
                            if len(l) == 1:
                                other = l[0]
                                # Ok we can popup the relation
                                self.AddRelation(other.object, element.object)
                                self.Freeze()
                                self._object.DrawClassDiagram(dc)
                                self.Thaw()
                                self._operation = None
                                return
                    element.Erase(dc)
                else:
                    self.Freeze()
                    if not wx.GetKeyState(wx.WXK_SHIFT):
                        self._object.EraseSelected(dc)
                        self._object.UnselectAll()
                    element.Select()
                    self._element = element
                    self._point = self.GetLocalPosition()
                if element.Selected():
                    if where is model.IN_LINE_MARK:
                        self._operation = OP_CLICK_SEGMENT
                        self._offset = subelement._mark - self._point
                        self._index = element.segments.index(subelement)
                    else:
                        self._operation = OP_CLICK_ELEMENT
                        self._offset = element._pos - self._point
                else:
                    self._operation = None
            else:
                self.Freeze()
                self._object.EraseSelected(dc)
                self._object.UnselectAll()
            self._object.DrawClassDiagram(dc)
            self.Thaw()

        elif self._operation in (OP_INSERT_CLASS, OP_MOVE_CLASS):
            self._operation = None
            self._object.PositionClass(self._element, self._point)
        event.Skip()

    def OnMouseUp(self, event):
        "mouse left up"
        if self._operation == OP_CLICK_ELEMENT:
            self._operation = None
        elif self._operation in (OP_MOVE_ELEMENT, OP_MOVE_SEGMENT):
            #tran.TransactionStack.DoCommit()
            self._operation = None
        elif self._operation == OP_CLICK_CLASS:
            self._operation = None
            if wx.GetKeyState(wx.WXK_CONTROL):
                self._element.ToggleSelected()
            else:
                self._object.UnselectAll()
                self._element.Select()
            dc = self.AdjustedDC()
            self.Freeze()
            self._object.DrawClassDiagram(dc)
            self.Thaw()
        event.Skip()

    def OnUpdateAddClass(self, event):
        """addClass update"""
        event.Enable(True)

    def OnUpdateSelectClasses(self, event):
        """Update classDiagramPane command"""
        event.Enable(True)

    def OnSelectClasses(self, event):
        """Show dialog for select classes"""
        dlg.SelectClassesDialog(self, self._object).ShowModal()

    @tran.TransactionalMethod('add diagram note')
    def OnDiagramAddNote(self, event):
        """Handles add note to diagram"""
        position = self.GetLocalPosition()
        dialog = dlg.NoteDialog(self)
        parent = self._object.project
        if dialog.ShowModal() != wx.ID_OK:
            return False
        note = model.Note(parent=parent, note=dialog._note)
        # draw in diagram
        self._object.SaveState()
        self._element = self._object.AddNote(note, position)
        self.SetFocus()
        self._element.Draw(self.AdjustedDC())
        return True

        # pending : copy attributes & do rendering
        return False

    def OnUpdateDiagramAddNote(self, event):
        """Handles add note to diagram"""
        return True

    def OnUpdateAddRelation(self, event):
        """Handles add relation to diagram"""
        s = self._element and self._element._obj
        s = s and isinstance(s, model.TComponent
            ) and s.inner_relation_container
        event.Enable(bool(s))

    def OnAddRelation(self, event):
        """Handle add relation method"""
        obj = self._element and self._element._obj
        obj = obj and obj.inner_relation_container
        self.AddRelation(obj)

    @tran.TransactionalMethod('add relation')
    @wxx.CreationDialog(dlg.RelationDialog, model.cc.Relation)
    def AddRelation(self, objFrom, objTo=None):
        """Handle add relation method"""
        return (context.frame, objFrom, objTo)

    def OnUpdateEditProperties(self, event):
        """Handle update edit properties"""
        event.Enable(bool(self.selected and not self.selected._readOnly))

    def OnEditProperties(self, event):
        """Handle edit properties method"""
        app.ui.edit(context.frame, self.selected)
