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

import wx
import wxx
import tran
import app
import app.ui.dlg
import model
import deco
import model.git
import app.resources as rc
from ctx import localcontext as context
from activity.git.ui import ui
from activity.git.ui import dlg as ui_dlg
from app.ui.view import BaseView


# Implementing GitView
class GitView(BaseView, ui.GitView):
    """View pane for git repository"""
    perspective = ''
    clipboard_handler = False

    #command ids
    _newRepoId = wx.Window.NewControlId()
    _openRepoId = wx.Window.NewControlId()
    _addItemId = wx.Window.NewControlId()
    _stageItemId = wx.Window.NewControlId()
    _commitItemId = wx.Window.NewControlId()
    _refreshRepoId = wx.Window.NewControlId()
    _addRemoteId = wx.Window.NewControlId()
    _pushRemoteId = wx.Window.NewControlId()

    def __init__(self, parent, root=None):
        """Initialize pane"""
        super(GitView, self).__init__(parent)
        self.frame = parent
        self.selected = None
        self.imglist = rc.GetBitmapImageList()
        self.m_tree.SetImageList(self.imglist)
        self.m_tree.AddRoot('Repositories')
        self._create_menus()
        self._bind_events()
        self._set_accelerators()
        # when we create a models view, we need to update elements
        if root is None:
            # reload full stack
            for wrk in context.app.workspaces:
                self.insert(wrk)
        else:
            self.insert(root)

    def insert(self, element):
        """Nested insert elements in tree"""
        if self.DoRenderAddElement(element):
            for cls in element._child:
                for k in element[cls]:
                    self.insert(k)

    def GetViewStatus(self):
        """Get working info about the view"""
        return str(self.m_tree.GetExpansionState())

    def SetViewStatus(self, status):
        """Set working info about the view"""
        try:
            l = eval(status)
            self.m_tree.SetExpansionState(l)
            return True
        except:
            return False


    @deco.classproperty
    def name(cls):
        """returns the name of this view"""
        return 'Git'

    @classmethod
    def bitmap_index(cls):
        """return the bitmap index"""
        return rc.GetBitmapIndex('git')

    @classmethod
    def bitmap(cls):
        """return the bitmap"""
        return rc.GetBitmap('git')

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

        # New repository
        self._newRepo = wx.MenuItem(self._menu, self._newRepoId, u"New repository",
            u"create new git repository", wx.ITEM_NORMAL)
        self._newRepo.SetBitmap(wx.ArtProvider.GetBitmap(wx.ART_NEW, wx.ART_MENU))
        self._menu.AppendItem(self._newRepo)

        #Open repository
        self._openRepo = wx.MenuItem(self._menu, self._openRepoId, u"Open repository",
            u"open existing repository", wx.ITEM_NORMAL)
        self._openRepo.SetBitmap(wx.ArtProvider.GetBitmap(wx.ART_FILE_OPEN, wx.ART_MENU))
        self._menu.AppendItem(self._openRepo)

        self._menu.AppendSeparator()
        #Add remote
        self._addRemote = wx.MenuItem(self._menu, self._addRemoteId, u"Add remote",
            u"add remote repository", wx.ITEM_NORMAL)
        self._addRemote.SetBitmap(wx.Bitmap(u"app/res/git_remote.xpm", wx.BITMAP_TYPE_ANY))
        self._menu.AppendItem(self._addRemote)

        #push remote
        self._pushRemote = wx.MenuItem(self._menu, self._pushRemoteId, u"Push remote",
            u"push to remote repository", wx.ITEM_NORMAL)
        self._pushRemote.SetBitmap(wx.Bitmap(u"app/res/git_push.xpm", wx.BITMAP_TYPE_ANY))
        self._menu.AppendItem(self._pushRemote)

        self._menu.AppendSeparator()

        #Add item to repo
        self._addItem = wx.MenuItem(self._menu, self._addItemId, u"Add to repository",
            u"add item to repository control", wx.ITEM_NORMAL)
        self._menu.AppendItem(self._addItem)

        #Add item to repo
        self._stageItem = wx.MenuItem(self._menu, self._stageItemId, u"Stage item",
            u"stage item to repository control", wx.ITEM_NORMAL)
        self._menu.AppendItem(self._stageItem)

        #Commit repo
        self._commitItem = wx.MenuItem(self._menu, self._commitItemId, u"Commit repository",
            u"commit changes to repository", wx.ITEM_NORMAL)
        self._menu.AppendItem(self._commitItem)

        self._refreshRepo = wx.MenuItem(self._menu, self._refreshRepoId, u"Refresh",
            u"update repository status", wx.ITEM_NORMAL)
        self._refreshRepo.SetBitmap(rc.GetBitmap('reload'))
        self._menu.AppendItem(self._refreshRepo)

        self.RegisterMenu('Git', self._menu)

    def _bind_events(self):
        """Binds events"""
        # Tree events
        self.m_tree.Bind(wx.EVT_TREE_SEL_CHANGED, self.OnTreeSelChanged)
        self.m_tree.Bind(wx.EVT_RIGHT_DOWN, self.OnTreeMenu)

        self.BindSpecial(wx.EVT_UPDATE_UI, self.OnUpdateDelete, id=app.ID_DELETE)
        self.BindSpecial(wx.EVT_MENU, self.OnDelete, id=app.ID_DELETE)

        self.BindSpecial(wx.EVT_UPDATE_UI, self.OnUpdateNewRepository, id=self._newRepoId)
        self.BindSpecial(wx.EVT_MENU, self.OnNewRepository, id=self._newRepoId)

        self.BindSpecial(wx.EVT_UPDATE_UI, self.OnUpdateOpenExistingRepository, id=self._openRepoId)
        self.BindSpecial(wx.EVT_MENU, self.OnOpenExistingRepository, id=self._openRepoId)

        self.BindSpecial(wx.EVT_UPDATE_UI, self.OnUpdateAddRemote, id=self._addRemoteId)
        self.BindSpecial(wx.EVT_MENU, self.OnAddRemote, id=self._addRemoteId)

        self.BindSpecial(wx.EVT_UPDATE_UI, self.OnUpdatePushRemote, id=self._pushRemoteId)
        self.BindSpecial(wx.EVT_MENU, self.OnPushRemote, id=self._pushRemoteId)

        self.BindSpecial(wx.EVT_UPDATE_UI, self.OnUpdateAddItemToRepository, id=self._addItem.GetId())
        self.BindSpecial(wx.EVT_MENU, self.OnAddItemToRepository, id=self._addItemId)

        self.BindSpecial(wx.EVT_UPDATE_UI, self.OnUpdateStageItem, id=self._stageItem.GetId())
        self.BindSpecial(wx.EVT_MENU, self.OnStageItem, id=self._stageItemId)

        self.BindSpecial(wx.EVT_UPDATE_UI, self.OnUpdateCommitRepository, id=self._commitItem.GetId())
        self.BindSpecial(wx.EVT_MENU, self.OnCommitRepository, id=self._commitItemId)

        self.BindSpecial(wx.EVT_UPDATE_UI, self.OnUpdateRefreshStatus, id=self._refreshRepo.GetId())
        self.BindSpecial(wx.EVT_MENU, self.OnRefreshStatus, id=self._refreshRepoId)

        super(GitView, self)._bind_events()

    def _set_accelerators(self):
        """Set the accelerator table"""
        # ctrl_alt = wx.ACCEL_CTRL + wx.ACCEL_ALT
        # ctrl_shf = wx.ACCEL_CTRL + wx.ACCEL_SHIFT
        aTable = wx.AcceleratorTable([
            wx.AcceleratorEntry(wx.ACCEL_NORMAL, wx.WXK_LEFT, BaseView._leftKeyId)
        ])
        self.SetAcceleratorTable(aTable)

    def DoRenderAddElement(self, obj):
        """Inserts element in tree"""
        # model tree
        treeOrder = [model.Workspace, model.git.GitRepo, model.git.GitRemotes,
            model.git.GitRemote, model.git.GitDir, model.git.GitFile]
        if type(obj) not in treeOrder:
            return False
        ti = treeOrder.index(type(obj))
        #check parent
        if self.m_tree.HoldsObject(obj.parent):
            p = obj.parent
        else:
            p = self.m_tree.GetRootItem()
        if obj.parent is not None:
            # find some major friend item with the same class
            tribal = obj.parent[type(obj)]
            index = tribal.index(obj)
            pre = None
            while index > 0 and pre is None:
                index = index - 1
                candidate = tribal[index]
                if not self.m_tree.HoldsObject(candidate):
                    continue
                pre = candidate
            if pre is not None:
                self.m_tree.InsertItem(p, pre, obj.label,
                    obj.bitmap_index, obj.bitmap_index, obj)
                if type(obj) is model.cc.Constructor:
                    self.m_tree.SetItemBold(obj, bold=obj.IsPreferred())
                elif type(obj) in [model.py.Module, model.py.Package]:
                    self.m_tree.SetItemBold(obj, bold=obj._entry)
                return True
        itemCount = 0
        citem, cookie = self.m_tree.GetFirstChild(p)
        if type(citem) is wx.TreeItemId:
            citem = self.m_tree.__fer__(citem)
        if type(citem) in treeOrder:
            if ti <= treeOrder.index(type(citem)):
                self.m_tree.PrependItem(p, obj.label,
                     obj.bitmap_index, obj.bitmap_index, obj)
                return True
        while type(citem) is not wx.TreeItemId or citem.IsOk():
            itemCount = itemCount + 1
            citem, cookie = self.m_tree.GetNextChild(p, cookie)
            if type(citem) not in treeOrder:
                continue
            if ti <= treeOrder.index(type(citem)):
                self.m_tree.InsertItemBefore(p, itemCount,
                     obj.label,
                     obj.bitmap_index, obj.bitmap_index, data=obj)
                return True
        #Ok, do apppend
        self.m_tree.AppendItem(p, obj.label,
            obj.bitmap_index, obj.bitmap_index, obj)
        return True

    def DoRenderRemoveElement(self, obj):
        """Do remove element in tree"""
        if self.m_tree.HoldsObject(obj):
            self.m_tree.Delete(obj)

    def UpdateElement(self, obj):
        """Update the tree label for a object"""
        if not self.m_tree.HoldsObject(obj):
            return
        self.m_tree.SetItemText(obj, obj.label)
        self.m_tree.SetItemImage(obj,
            obj.bitmap_index, wx.TreeItemIcon_Normal)
        if hasattr(obj, 'bitmap_open_index'):
            self.m_tree.SetItemImage(obj,
                obj.bitmap_open_index, wx.TreeItemIcon_Expanded)
            self.m_tree.SetItemImage(obj,
                obj.bitmap_open_index, wx.TreeItemIcon_SelectedExpanded)
        self.m_tree.SetItemImage(obj,
            obj.bitmap_index, wx.TreeItemIcon_Selected)

    def OnTreeSelChanged(self, event):
        """Handle select changed"""
        self.selected = self.m_tree.GetSelection()

    def OnUpdateNewRepository(self, event):
        """Update new repository command"""
        event.Enable(bool(self.selected and type(self.selected) is model.Workspace))

    @tran.TransactionalMethod('new git repo {}')
    @wxx.CreationDialog(ui_dlg.GitRepoDialog, model.git.GitRepo)
    def OnNewRepository(self, event):
        """New repository command"""
        return (context.frame, self.selected.inner_repository_container)

    def OnUpdateOpenExistingRepository(self, event):
        """Update open existing repository"""
        event.Enable(bool(self.selected and type(self.selected) is model.Workspace))

    @tran.TransactionalMethod('add git repo {}')
    @wxx.CreationDialog(ui_dlg.OpenGitRepoDialog, model.git.GitRepo)
    def OnOpenExistingRepository(self, event):
        """Handle add folder command"""
        return (context.frame, self.selected.inner_repository_container)

    def OnUpdateAddRemote(self, event):
        """Update add remote repo"""
        event.Enable(bool(self.selected and type(self.selected) is not model.Workspace
            and self.selected.repo))

    @tran.TransactionalMethod('add remote repo {}')
    @wxx.CreationDialog(ui_dlg.GitRemoteDialog, model.git.GitRemote)
    def OnAddRemote(self, event):
        """Handle add remote repo"""
        rl = self.selected.repo(model.git.GitRemotes)
        return (context.frame, rl[0])

    def OnUpdatePushRemote(self, event):
        """Update add remote repo"""
        event.Enable(bool(self.selected and type(self.selected) is model.git.GitRemote))

    def OnPushRemote(self, event):
        """Handle add remote repo"""
        _repo = self.selected.repo._repo
        remote = _repo.remote(self.selected.name)
        dialog = ui_dlg.ProgressDialog(self.frame)
        dialog.Show()
        wx.YieldIfNeeded()
        try:
            remote.push(progress=dialog.link())
            dialog.update('--', 0, message='DONE')
        except:
            dialog.update('--', 100, message='FAILED')
            pass
        dialog.Finished()
        return
        import git
        g = git.cmd.Git(self.selected.repo._uri)
        g.push('--set-upstream', self.selected.name, 'master')
        return
        _repo = self.selected.repo._repo
        ref = _repo.remote(self.selected.name)
        try:
            dialog = ui_dlg.ProgressDialog(self, self.frame)
            ui_dlg.Show()
            if self.selected.password:
                ref.push(refspec='refs/heads/master:refs/heads/master', password=self.selected.password,
                    progress=dialog)
            else:
                ref.push(refspec='refs/heads/master:refs/heads/master', progress=dialog)
        except Exception, e:
            wx.MessageBox("Push to repository {name} failed: {error}.".format(
                name=self.selected.name, error=e), "Error",
                wx.OK | wx.CENTER | wx.ICON_ERROR, self)
        dialog.Finished()

    def TreeLeftKey(self, event):
        """If the selected node is expanded, simply collapse it.
        If not, navigate through parent"""
        if not self.selected:
            return
        if self.m_tree.IsExpanded(self.selected):
            self.m_tree.Collapse(self.selected)
        else:
            parent = self.selected.parent
            if self.m_tree.HoldsObject(parent):
                self.m_tree.SelectItem(parent)

    def OnTreeMenu(self, event):
        """Handles context tree popup menu"""
        (item, where) = self.m_tree.HitTest(event.GetPosition())
        if item is None:
            return
        self.m_tree.SelectItem(item)
        if self.selected is None:
            return
        menu = app.ui.clone_mnu(self._menu, enabled=True, notitle=True)
        if menu is None:
            return
        self.PopupMenu(menu, event.GetPosition())
        menu.Destroy()

    def OnDelete(self, event):
        """delete element"""
        obj = self.selected
        aliasMap = {
            model.git.GitRepo: "git repository {}",
            model.git.GitFile: "git file {}",
            model.git.GitDir: "git dir {}",
            model.git.GitRemote: "git remote {}"}
        tran.TransactionStack.DoBeginTransaction("delete " +
             aliasMap[type(obj)].format(obj._name))
        obj.Delete()
        tran.TransactionStack.DoCommit()

    def OnUpdateDelete(self, event):
        """Update delete element"""
        obj = self.selected
        event.Enable((not obj is None) and type(obj) is not model.Workspace
            and obj.can_delete)

    def OnUpdateCommitRepository(self, event):
        """Update commit to repo"""
        obj = self.selected
        event.Enable(bool(obj and type(obj) is model.git.GitRepo
        and obj.stage_size > 0))

    def OnCommitRepository(self, event):
        """Process a commit"""
        dialog = ui_dlg.CommitGitDialog(context.frame)
        if dialog.ShowModal() == wx.ID_OK:
            self.selected.commit(dialog._message)

    def OnUpdateRefreshStatus(self, event):
        """Update refresh status"""
        obj = self.selected
        event.Enable((not obj is None) and type(obj) is not model.Workspace)

    @tran.TransactionalMethod('refresh git status')
    def OnRefreshStatus(self, event):
        """Refresh repository status"""
        working = app.ui.dlg.WorkingDialog(context.frame)
        working.Show(True)
        wx.YieldIfNeeded()
        self.selected.update_status()
        working.Close()
        return True

    def OnUpdateAddItemToRepository(self, event):
        """Update the operation of adding elements to repo"""
        obj = self.selected
        if not obj:
            event.Enable(False)
            return
        if type(obj) is model.git.GitFile:
            if obj._status == 'file':
                event.Enable(True)
            else:
                event.Enable(False)
            return
        #ok, buscamos un hijo
        fn = lambda x: (x._status == 'file')
        if obj(model.git.GitFile, filter=fn):
            event.Enable(True)
        else:
            event.Enable(False)

    def OnAddItemToRepository(self, event):
        """Add element to track"""
        obj = self.selected
        repo = obj.repo
        if type(obj) is model.git.GitFile:
            repo.add(obj)
            return
        fn = lambda x: (x._status == 'file')
        for item in obj(model.git.GitFile, filter=fn):
            repo.add(item)

    def OnUpdateStageItem(self, event):
        """Update the operation of adding elements to repo"""
        obj = self.selected
        if not obj:
            event.Enable(False)
            return
        # para hacer stage es suficiente que el elemento
        # seleccionado o un hijo sea un GitFile con status modified
        if type(obj) is model.git.GitFile:
            if obj._status == 'git_file_modified':
                event.Enable(True)
            else:
                event.Enable(False)
            return
        # ok, buscamos un hijo
        fn = lambda x: (x._status == 'git_file_modified')
        if obj(model.git.GitFile, filter=fn):
            event.Enable(True)
        else:
            event.Enable(False)

    def OnStageItem(self, event):
        """Add element to track"""
        obj = self.selected
        repo = obj.repo
        if type(obj) is model.git.GitFile:
            repo.add(obj)
            return
        fn = lambda x: (x._status == 'git_file_modified')
        for item in obj(model.git.GitFile, filter=fn):
            repo.add(item)

    def OnUpdateEditProperties(self, event):
        """"""
        event.Enable(False)

    def OnEditProperties(self, event):
        """"""
        pass