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

import sys
import importlib
import traceback
import json
import six
import six.moves
import wx
import wx.lib.agw.aui as aui
import wx.py
import wx.py.dispatcher as dp
import wx.adv
from .frameplus import FramePlus
from .mainframexpm import bsmedit_xpm, header_xpm, bsmedit_svg
from . import __version__, to_byte
from .bsm.utility import PopupMenu, svg_to_bitmap
from .bsm import auto_load_module

class FileDropTarget(wx.FileDropTarget):
    def __init__(self):
        wx.FileDropTarget.__init__(self)

    def OnDropFiles(self, x, y, filenames):
        for fname in filenames:
            wx.CallAfter(dp.send, signal='frame.file_drop', filename=fname)
        return True

class TaskBarIcon(wx.adv.TaskBarIcon):
    TBMENU_RESTORE = wx.NewId()
    TBMENU_CLOSE   = wx.NewId()
    TBMENU_CHANGE  = wx.NewId()
    TBMENU_REMOVE  = wx.NewId()

    def __init__(self, frame, icon):
        wx.adv.TaskBarIcon.__init__(self, iconType=wx.adv.TBI_DOCK)
        self.frame = frame

        # Set the image
        self.SetIcon(icon, "bsmedit")
        self.imgidx = 1

        # bind some events
        #self.Bind(wx.EVT_TASKBAR_LEFT_DCLICK, self.OnTaskBarActivate)
        self.Bind(wx.EVT_MENU, self.OnTaskBarActivate, id=self.TBMENU_RESTORE)
        self.Bind(wx.EVT_MENU, self.OnTaskBarClose, id=self.TBMENU_CLOSE)


    def CreatePopupMenu(self):
        """
        This method is called by the base class when it needs to popup
        the menu for the default EVT_RIGHT_DOWN event.  Just create
        the menu how you want it and return it from this function,
        the base class takes care of the rest.
        """
        menu = wx.Menu()
        menu.Append(self.TBMENU_RESTORE, "Restore bsmedit")
        menu.Append(self.TBMENU_CLOSE,   "Close bsmedit")
        return menu


    def MakeIcon(self, img):
        """
        The various platforms have different requirements for the
        icon size...
        """
        if "wxMSW" in wx.PlatformInfo:
            img = img.Scale(16, 16)
        elif "wxGTK" in wx.PlatformInfo:
            img = img.Scale(22, 22)
        # wxMac can be any size upto 128x128, so leave the source img alone....
        icon = wx.Icon(img.ConvertToBitmap() )
        return icon


    def OnTaskBarActivate(self, evt):
        if self.frame.IsIconized():
            self.frame.Iconize(False)
        if not self.frame.IsShown():
            self.frame.Show(True)
        self.frame.Raise()


    def OnTaskBarClose(self, evt):
        wx.CallAfter(self.frame.Close)


class MainFrame(FramePlus):

    ID_VM_RENAME = wx.NewId()
    ID_CONTACT = wx.NewId()

    def __init__(self, parent, **kwargs):
        FramePlus.__init__(self,
                           parent,
                           title='bsmedit',
                           size=wx.Size(800, 600),
                           style=wx.DEFAULT_FRAME_STYLE | wx.TAB_TRAVERSAL)
        self.InitMenu()
        self._mgr.SetAGWFlags(self._mgr.GetAGWFlags()
                              | aui.AUI_MGR_ALLOW_ACTIVE_PANE
                              | aui.AUI_MGR_SMOOTH_DOCKING
                              | aui.AUI_MGR_USE_NATIVE_MINIFRAMES
                              | aui.AUI_MGR_LIVE_RESIZE)
        # set mainframe icon
        icon = wx.Icon()
        icon.CopyFromBitmap(svg_to_bitmap(bsmedit_svg))
        self.SetIcon(icon)

        if 'wxMac' in wx.PlatformInfo:
            icon.CopyFromBitmap(svg_to_bitmap(bsmedit_svg, 1024, 1024))
            self.tbicon = TaskBarIcon(self, icon)

        # status bar
        self.statusbar = wx.StatusBar(self)
        self.SetStatusBar(self.statusbar)
        self.statusbar_width = [-1]
        self.statusbar.SetStatusWidths(self.statusbar_width)

        # persistent configuration
        conf = kwargs.get('config', 'bsmedit')
        self.config = wx.FileConfig(conf, style=wx.CONFIG_USE_LOCAL_FILE)

        # recent file list
        self.filehistory = wx.FileHistory(8)
        self.config.SetPath('/FileHistory')
        self.filehistory.Load(self.config)
        self.filehistory.UseMenu(self.menuRecentFiles)
        self.filehistory.AddFilesToMenu()
        self.Bind(wx.EVT_MENU_RANGE,
                  self.OnMenuFileHistory,
                  id=wx.ID_FILE1,
                  id2=wx.ID_FILE9)

        self.closing = False

        # Create & Link the Drop Target Object to main window
        self.SetDropTarget(FileDropTarget())

        self.Bind(wx.EVT_ACTIVATE, self.OnActivate)
        self.Bind(aui.EVT_AUI_PANE_ACTIVATED, self.OnPaneActivated)
        dp.connect(self.SetPanelTitle, 'frame.set_panel_title')
        dp.connect(self.ShowStatusText, 'frame.show_status_text')
        dp.connect(self.AddFileHistory, 'frame.add_file_history')
        dp.connect(self.SetConfig, 'frame.set_config')
        dp.connect(self.GetConfig, 'frame.get_config')

        # append sys path
        sys.path.append('.')
        for p in kwargs.get('path', []):
            sys.path.append(p)

        self.bsm_packages = auto_load_module
        self.addon = {}
        self.InitAddOn(kwargs.get('module', ()))

        # initialization done, broadcasting the message so plugins can do some
        # after initialization processing.
        dp.send('frame.initialized')

        # load the perspective
        if not kwargs.get('ignore_perspective', False):
            perspective = self.GetConfig('mainframe', 'perspective')
            if perspective and not wx.GetKeyState(wx.WXK_SHIFT):
                self._mgr.LoadPerspective(perspective)
                self.UpdatePaneMenuLabel()

        self.Bind(aui.EVT_AUINOTEBOOK_TAB_RIGHT_DOWN, self.OnPageRightDown)
        self.Bind(wx.EVT_RIGHT_DOWN, self.OnRightDown)

    def InitMenu(self):
        """initialize the menubar"""
        menubar = wx.MenuBar()
        self.SetMenuBar(menubar)

        self.AddMenu('&File:New', kind="Popup", autocreate=True)
        self.AddMenu('&File:Open', kind="Popup")
        self.AddMenu('&File:Sep', kind="Separator")
        self.AddMenu('&File:Recent Files', kind="Popup")
        self.menuRecentFiles = self.GetMenu(['File', 'Recent Files'])
        self.AddMenu('&File:Sep', kind="Separator")
        self.AddMenu('&File:&Quit', id=wx.ID_CLOSE)

        # add the common Edit menus to menubar; otherwise, the context-menu
        # from shell or editor may not work (e.g., Mac)
        self.AddMenu('&Edit:&Undo', id=wx.ID_UNDO, autocreate=True)
        self.AddMenu('&Edit:&Redo', id=wx.ID_REDO)
        self.AddMenu('&Edit:Sep', kind="Separator")
        self.AddMenu('&Edit:&Cut', id=wx.ID_CUT)
        self.AddMenu('&Edit:&Copy', id=wx.ID_COPY)
        self.AddMenu('&Edit:&Paste', id=wx.ID_PASTE)
        self.AddMenu('&Edit:&Clear', id=wx.ID_CLEAR)
        self.AddMenu('&Edit:Sep', kind="Separator")
        self.AddMenu('&Edit:&Select All', id=wx.ID_SELECTALL)

        self.AddMenu('&View:Toolbars', kind="Popup", autocreate=True)
        self.AddMenu('&View:Sep', kind="Separator")
        self.AddMenu('&View:Panels', kind="Popup")

        self.AddMenu('&Tools', kind="Popup", autocreate=True)

        self.AddMenu('&Help:&Home', id=wx.ID_HOME, autocreate=True)
        self.ID_CONTACT = self.AddMenu('&Help:&Contact')
        self.AddMenu('&Help:Sep', kind="Separator")
        self.AddMenu('&Help:About', id=wx.ID_ABOUT)

        # Connect Events
        self.Bind(wx.EVT_MENU, self.OnFileQuit, id=wx.ID_CLOSE)
        self.Bind(wx.EVT_MENU, self.OnHelpHome, id=wx.ID_HOME)
        self.Bind(wx.EVT_MENU, self.OnHelpContact, id=self.ID_CONTACT)
        self.Bind(wx.EVT_MENU, self.OnHelpAbout, id=wx.ID_ABOUT)

    def InitAddOn(self, modules):
        if not modules:
            # load all modules
            modules = ["default"]

        for module in modules:
            module = module.split('+')
            options = {}
            if len(module) == 2:
                if all([c in 'htblr' for c in module[1]]):
                    if 'h' in module[1]:
                        options['active'] = False
                    if 't' in module[1]:
                        options['direction'] = 'Top'
                    if 'b' in module[1]:
                        options['direction'] = 'bottom'
                    if 'l' in module[1]:
                        options['direction'] = 'left'
                    if 'r' in module[1]:
                        options['direction'] = 'right'
                options['data'] = module[1]
            module = module[0]
            if module == 'default':
                module = self.bsm_packages
            else:
                module = [module]
            for pkg in module:
                if pkg in self.bsm_packages:
                    # module in bsm
                    pkg = 'bsmedit.bsm.%s' % pkg

                if pkg in self.addon:
                    # already loaded
                    continue
                self.addon[pkg] = False
                try:
                    mod = importlib.import_module(pkg)
                except ImportError:
                    traceback.print_exc(file=sys.stdout)
                else:
                    if hasattr(mod, 'bsm_initialize'):
                        mod.bsm_initialize(self, **options)
                        self.addon[pkg] = True
                    else:
                        print("Error: Invalid module: %s" % pkg)

    def AddFileHistory(self, filename):
        """add the file to recent file list"""
        self.config.SetPath('/FileHistory')
        self.filehistory.AddFileToHistory(filename)
        self.filehistory.Save(self.config)
        self.config.Flush()

    def SetConfig(self, group, **kwargs):
        if not group.startswith('/'):
            group = '/' + group
        for key, value in six.iteritems(kwargs):
            if key in ['signal', 'sender']:
                # reserved key for dp.send
                continue
            if not isinstance(value, str):
                # add sign to indicate that the value needs to be deserialize
                value = '__bsm__' + json.dumps(value)
            self.config.SetPath(group)
            self.config.Write(key, value)

    def GetConfig(self, group, key):
        if not group.startswith('/'):
            group = '/' + group
        if self.config.HasGroup(group):
            self.config.SetPath(group)
            if self.config.HasEntry(key):
                value = self.config.Read(key)
                if value.startswith('__bsm__'):
                    value = json.loads(value[7:])
                return value
        return None

    def OnPageRightDown(self, evt):
        # get the index inside the current tab control
        idx = evt.GetSelection()
        tabctrl = evt.GetEventObject()
        tabctrl.SetSelection(idx)
        page = tabctrl.GetPage(idx)
        self.RenamePanel(page)

    def OnRightDown(self, evt):
        evt.Skip()

        part = self._mgr.HitTest(*evt.GetPosition())
        if not part or part.pane.IsNotebookControl():
            return

        self.RenamePanel(part.pane.window)

    def RenamePanel(self, panel):
        if not panel:
            return

        menu = wx.Menu()
        menu.Append(self.ID_VM_RENAME, "&Rename")
        command = PopupMenu(self, menu)

        if command == self.ID_VM_RENAME:
            pane = self._mgr.GetPane(panel)
            if not pane:
                return
            name = pane.caption
            name = wx.GetTextFromUser("Type in the name:", "Input Name", name,
                                      self)
            # when user click 'cancel', name will be empty, ignore it.
            if name and name != pane.caption:
                self.SetPanelTitle(pane.window, name)

    def UpdatePaneMenuLabel(self):
        # update the menu
        for (pid, panel) in six.iteritems(self.paneAddon):
            pathlist = panel['path'].split(':')
            menuitem = self.GetMenu(pathlist[:-1])
            if not menuitem:
                continue
            pane = self._mgr.GetPane(panel['panel'])
            item = menuitem.FindItemById(pid)
            if item and pane.caption != item.GetItemLabelText():
                item.SetItemLabel(pane.caption)

    def OnCloseWindow(self, evt):
        self.tbicon.Destroy()
        evt.Skip()

    def OnClose(self, event):
        """close the main program"""
        dp.send('frame.closing', event=event)
        if event.GetVeto():
            return
        self.closing = True
        self.SetConfig('mainframe', perspective=self._mgr.SavePerspective())
        dp.send('frame.exit')
        self.config.Flush()
        super(MainFrame, self).OnClose(event)

    def ShowStatusText(self, text, index=0, width=-1):
        """set the status text"""
        if index >= len(self.statusbar_width):
            exd = [0] * (index + 1 - len(self.statusbar_width))
            self.statusbar_width.extend(exd)
            self.statusbar.SetFieldsCount(index + 1)
        if self.statusbar_width[index] < width:
            self.statusbar_width[index] = width
            self.statusbar.SetStatusWidths(self.statusbar_width)
        self.statusbar.SetStatusText(text, index)

    def OnActivate(self, event):
        if not self.closing:
            dp.send('frame.activate', activate=event.GetActive())
        event.Skip()

    def OnPaneActivated(self, event):
        """notify the window managers that the panel is activated"""
        if self.closing:
            return
        pane = event.GetPane()
        if isinstance(pane, aui.auibook.AuiNotebook):
            window = pane.GetCurrentPage()
        else:
            window = pane

        dp.send('frame.activate_panel', pane=window)

    def SetPanelTitle(self, pane, title):
        """set the panel title"""
        if pane:
            info = self._mgr.GetPane(pane)
            if info and info.IsOk() and info.caption != title:
                info.Caption(title)
                self._mgr.RefreshPaneCaption(pane)
                self.UpdatePaneMenuLabel()

    # Handlers for mainFrame events.
    def OnFileQuit(self, event):
        """close the program"""
        self.Close(True)

    def OnHelpHome(self, event):
        """go to homepage"""
        wx.BeginBusyCursor()
        import webbrowser
        webbrowser.open("http://bsmedit.feiyilin.com")
        wx.EndBusyCursor()

    def OnHelpContact(self, event):
        """send email"""
        wx.BeginBusyCursor()
        import webbrowser
        webbrowser.open("mail:tq@feiyilin.com")
        wx.EndBusyCursor()

    def OnHelpAbout(self, event):
        """show about dialog"""
        dlg = AboutDialog(self)
        dlg.ShowModal()
        dlg.Destroy()

    def OnMenuFileHistory(self, event):
        """open the recent file"""
        fileNum = event.GetId() - wx.ID_FILE1
        path = self.filehistory.GetHistoryFile(fileNum)
        self.filehistory.AddFileToHistory(path)
        dp.send('frame.file_drop', filename=path)


class AboutDialog(wx.Dialog):
    def __init__(self, parent):
        wx.Dialog.__init__(self,
                           parent,
                           title="About bsmedit",
                           style=wx.DEFAULT_DIALOG_STYLE)

        szAll = wx.BoxSizer(wx.VERTICAL)

        self.panel = wx.Panel(self, style=wx.TAB_TRAVERSAL)
        self.panel.SetBackgroundColour(wx.WHITE)

        szPanel = wx.BoxSizer(wx.VERTICAL)

        self.header = wx.StaticBitmap(self.panel)
        self.header.SetBitmap(wx.Bitmap(to_byte(header_xpm)))
        szPanel.Add(self.header, 0, wx.EXPAND, 0)

        caption = 'bsmedit %s' % (__version__)
        self.stCaption = wx.StaticText(self.panel, wx.ID_ANY, caption)
        self.stCaption.SetFont(wx.Font(16, 74, 90, 92, False, "Arial"))

        szPanel.Add(self.stCaption, 0, wx.ALL | wx.EXPAND, 5)

        strCopyright = '(c) 2018 Tianzhu Qiao. All rights reserved.'

        self.stCopyright = wx.StaticText(self.panel, wx.ID_ANY, strCopyright)
        self.stCopyright.SetMaxSize((240, -1))
        self.stCopyright.SetFont(wx.Font(8, 74, 90, 90, False, "Arial"))
        szPanel.Add(self.stCopyright, 0, wx.ALL | wx.EXPAND, 5)

        build = wx.GetOsDescription() + '; wxWidgets ' + wx.version()
        self.stBuild = wx.StaticText(self.panel, wx.ID_ANY, build)
        self.stBuild.SetMaxSize((240, -1))
        self.stBuild.Wrap(240)
        self.stBuild.SetFont(wx.Font(8, 74, 90, 90, False, "Arial"))
        szPanel.Add(self.stBuild, 0, wx.ALL | wx.EXPAND, 5)

        stLine = wx.StaticLine(self.panel, style=wx.LI_HORIZONTAL)
        szPanel.Add(stLine, 1, wx.EXPAND | wx.ALL, 0)

        self.panel.SetSizer(szPanel)
        self.panel.Layout()
        szPanel.Fit(self.panel)

        szAll.Add(self.panel, 1, wx.EXPAND | wx.ALL, 0)

        szConfirm = wx.BoxSizer(wx.VERTICAL)
        self.btnOk = wx.Button(self, wx.ID_OK, u"Ok")
        szConfirm.Add(self.btnOk, 0, wx.ALIGN_RIGHT | wx.ALL, 5)

        szAll.Add(szConfirm, 0, wx.EXPAND, 5)

        self.SetSizer(szAll)
        self.Layout()
        szAll.Fit(self)
