import time
import wx, ws, constants, re
from analysisresult import AnalysisResult, ProblemSet
from resultset import FilterResult, QueryResult, UniqResult, CountResult, SetResult
from graphresult import GraphResult
# Testing
from views import DefaultViewFactory
import traceback, pdb
count = 0

class MySplitterWindow(wx.SplitterWindow):
    def __init__(self, *args, **kargs):
        wx.SplitterWindow.__init__(self, *args, **kargs)
    def OnDoubleClickSash(self, x, y):
        return

# Display object should support
#   GetParent
#   GetName
#   SetName
#   Delete
#
# 0. Use GetName() and SetName() (move functionality for editing name?)
# 1. SetPageText     --> replace with UpdateViewName()
# 2. GetPageIndex    --> don't use anymore
# 3. SelectDataSet   --> set selection
# 4. DeleteSelection --> Delete() method on result


class MyTree(wx.TreeCtrl):
    def __init__(self, *args, **kargs):
        wx.TreeCtr.__init__(self, *args, **kargs)

    def Enable(self, v):
        print "**** IS THIS CALLED *****?"
        wx.Skip()

class ViewBook(wx.Panel):
    def __init__(self, parent):
        wx.Panel.__init__(self, parent, -1, style=wx.WANTS_CHARS)
        self.splitter = MySplitterWindow(self, style=wx.SP_LIVE_UPDATE)
        self.splitter.SetSashSize(7)
        self.splitter.SetMinimumPaneSize(250)
        left = self.tree = wx.TreeCtrl(self.splitter, -1,
            style=wx.TR_DEFAULT_STYLE|wx.TR_EDIT_LABELS|wx.TR_SINGLE)
        self.tree.SetFont(ws.SmFont(wx.SWISS, wx.NORMAL, wx.NORMAL))
        self.tree.SetBackgroundColour(wx.Color(245,245,255))                
        s = (24,18)
        il = wx.ImageList(s[0], s[1])
        images = self.images = []
        images.append((ProblemSet,  "icons/result-problemset.gif"))
        images.append((GraphResult, "icons/result-graph.gif"))
        images.append((SetResult,   "icons/result-set.gif"))
        images.append((FilterResult,"icons/result-filter.gif"))
        images.append((QueryResult, "icons/result-query.gif"))
        images.append((UniqResult,  "icons/result-uniq.gif"))
        images.append((CountResult,  "icons/result-count.gif"))
        
        il.Add(wx.Image("icons/result-default.gif",
            wx.BITMAP_TYPE_GIF).Size(s, (1,1)).ConvertToBitmap())
        for (c, fname) in images:
            il.Add(wx.Image(fname, wx.BITMAP_TYPE_GIF).Size(s, (1,1)).ConvertToBitmap())
        self.tree.AssignImageList(il)                
        self.root = None
        # Map result -> tree item
        self._treeitems = {}
        self._views = {}
        # Create a splitter with a dummy second pane
        self.splitter.SplitVertically(left,
            wx.TextCtrl(self.splitter, -1, ""))
        sz = wx.BoxSizer(wx.HORIZONTAL)
        sz.Add(self.splitter)
        self.SetSizerAndFit(sz)
        self.splitter.SetSashGravity(0.0)
        self.splitter.SetSashPosition(250)
        # Force tree to resize (not sure why this is necessary)
        self.Bind(wx.EVT_SIZE, self.OnSize)
        # Keep view in synch with tree selection
        self.Bind(wx.EVT_TREE_SEL_CHANGED, self.OnSelChanged, self.tree)
        # Right click to display menu
        self.Bind(wx.EVT_TREE_ITEM_RIGHT_CLICK, self.OnRightClick, self.tree)
        # Tool tips
        #self.Bind(wx.EVT_TREE_ITEM_GETTOOLTIP, self.OnTip, self.tree)
        self.Bind(wx.EVT_TREE_BEGIN_LABEL_EDIT, self.OnBeginEditLabel, self.tree)
        self.Bind(wx.EVT_TREE_END_LABEL_EDIT, self.OnEditLabel, self.tree)
        self.Bind(wx.EVT_TREE_KEY_DOWN, self.OnTreeKeyDown,
            self.GetTreeCtrl())
        self.Bind(wx.EVT_MENU, self.OnDeleteItem,
            id=constants.ID_DELETE_RESULT)
        
    def OnDeleteItem(self, event):
        self.DeleteSelection()
            
    # FIXME -- REMOVE THESE AND USE OTHER METHODS
    def GetSelectedData(self):
        try:
            (result, view) = self.GetSelection()
            return result
        except:
            return None
            
    def GetSelectedView(self):
        try:
            (result, view) = self.GetSelection()
            return view
        except:
            return None
            
    def SelectDataSet(self, result):
        self.SetSelection(result)
    def SetPageText(self, i, val):
        pass
    def GetPageIndex(self, v):
        return -1
    def GetPage(self, result):
        return self._views[result]
    # FIXME -- ABOVE AND REFERENCES SHOULD BE REMOVED

    def GetViewCount(self):
        return len(self._treeitems.keys())
        
    def GetPopupMenu(self):
        return None

    def OnTreeKeyDown(self, evt):
        print "OnTreeKeyDown"
        (result, view) = self.GetSelection()
        if evt.GetKeyCode() in [wx.WXK_DELETE, wx.WXK_BACK]:
            self.DeleteSelection()
        else:
            evt.Skip(True)
                
    def DeleteSelection(self, showConfirmation=True):
        print "Deleting selection"
        (result, view) = self.GetSelection()
        self.DeleteResult(result, showConfirmation)
        
    def DeleteResult(self, result, showConfirmation=True):
        if showConfirmation:
            d = wx.MessageDialog(self,
                "This will delete any files associated with this data set.",
                "Are you sure?", wx.YES_NO | wx.ICON_QUESTION)
            res = d.ShowModal()
        else:
            res = wx.ID_YES
        if res == wx.ID_YES:
            # Delete tree item
            #self.GetTreeCtrl().Delete(self.GetTreeCtrl().GetSelection())
            tree_item = self._treeitems[result]
            self.GetTreeCtrl().Delete(tree_item)
            
            # Remove from result to item map
            del self._treeitems[result]
            del self._views[result]
            # Ask the object to delete itself
            try:         
                if result.GetParent():
                    result.GetParent().DeleteChild(result)       
                else:
                    result.Delete()
            except:
                print "WARNING: DeleteResult() - No delete method on object"

    def GetTreeCtrl(self):
        return self.tree
        
    def OnTip(self, event):
        print "OnTip"
        
    def OnRightClick(self, event):
        print "On right click"
        print "Selected menu item"
        menu = self.GetPopupMenu(self.GetSelectedData())
        if menu:
            self.PopupMenu(menu)
        return
        
    def OnBeginEditLabel(self, evt):
        id = evt.GetItem()
        (ds, view) = self.GetTreeCtrl().GetPyData(id)
        if isinstance(ds,ProblemSet):
            if ds.HasActiveChildren():
                evt.Veto()
            #evt.Veto()
        elif isinstance(ds, AnalysisResult):
            if ds.IsProcessing():
                evt.Veto()
    
    def OnEditLabel(self, evt):
        if evt.IsEditCancelled(): 
            return
        id = evt.GetItem()
        (ds, view) = self.GetTreeCtrl().GetPyData(id)
        r = re.compile(r'^[\w\_][\w\s\-\_\.]*$')
        if r.match(evt.GetLabel()):
            ds.SetName(evt.GetLabel())
        else:
            dlg = wx.MessageDialog(self, "Illegal character in name",
                "Illegal name", wx.ICON_EXCLAMATION)
            dlg.ShowModal()
            evt.Veto()
            return
            
    def SetRoot(self, result, view):
        if self.root != None:
            raise Exception, "ViewBook: Can only add one root view!"
        try:
            name = result.GetName()
        except:
            name = str(result)
        self.root = item = self.tree.AddRoot(name, 1)
        self.tree.SetPyData(item, (result, view))
        self._treeitems[result] = item
        self._views[result] = view
        self.tree.SelectItem(item)
        self._updateView(item)
        
    def AddView(self, parent, result, view):
        view.Reparent(self.splitter)
        # Determine image to use
        image = 0
        for i, (c, f) in enumerate(self.images):
            if isinstance(result, c): 
                image = i + 1
                break
        else:
            image = 0
        # Add the item
        if parent == None:
            raise Exception, "INTERNAL ERROR: view must have parent"

        if not parent in self._treeitems.keys():
            print self._treeitems
            raise Exception, "ViewBook: Can't find parent: %s" % parent
        parent_item = self._treeitems[parent]
        if type(result) == str:
            name = str(result)
        else:
            name = result.GetName()
            item = self.tree.AppendItem(parent_item, name, image)
            if result.IsFileAvailable():
                self.tree.SetItemTextColour(item, wx.Colour(0,0,0))
            else:
                self.tree.SetItemTextColour(item, wx.Colour(200,200,200))
            
            
        self.tree.SetPyData(item, (result, view))
        self._treeitems[result] = item
        self._views[result] = view
        self.tree.SelectItem(item)
        self._updateView(item)
        
    def GetSelection(self):
        sel = self.tree.GetSelection()
        return self.tree.GetPyData(sel)

    def SetSelection(self, result):
        item = self._treeitems[result]
        self.tree.SelectItem(item)
        self._updateView(item)
        
    def OnSelChanged(self, event):
        sel = self.tree.GetSelection()
        self._updateView(sel)
        self._postViewChangeEvent(sel)
        
    def _postViewChangeEvent(self, sel):
        class ResultBookEvent(wx.PyCommandEvent):
           def __init__(self, type, id, newSel):
               wx.PyCommandEvent.__init__(self, type, id)
               self.newSel = newSel
           def GetSelection(self):
               return self.newSel
        event = ResultBookEvent(ws.EVTTYPE_RESULTBOOK_VIEW_CHANGED, -1, sel)
        wx.PostEvent(self.GetParent(), event)
    
    def _updateView(self, sel):
        if self.tree.GetPyData(sel) == None:
            return
        (result, view) = self.tree.GetPyData(sel)
        if self.splitter.GetWindow2() == view:
            return
        self.splitter.GetWindow2().Enable(False)
        self.splitter.GetWindow2().Show(False)
        self.splitter.ReplaceWindow(self.splitter.GetWindow2(), view)
        # Don't rebuild the view each time we select it
        view.RefreshContents()
        view.Show(True)
        view.Enable(True)
        
    def RefreshContents(self):
        self.splitter.GetWindow2().RefreshContents()
        
    def OnSize(self, event):
        child = self.splitter
        w,h = self.GetClientSizeTuple()
        child.SetDimensions(0, 0, w, h)


# FIXME - Handle popup menu
class ResultViewBook(ViewBook):
    def __init__(self, parent, id, problemset, viewFactory=None):
        ViewBook.__init__(self, parent)
        self.viewFactory = viewFactory
        if self.viewFactory == None:
            self.viewFactory = DefaultViewFactory(self)
        self.problemset = problemset
        # Populate the view with the main view and the children
        view = self.viewFactory.CreateView(problemset)
        # Problem set events should be propagated too
        problemset.AddChangeListener(view)
        problemset.AddChangeListener(self)
        
        self.SetRoot(problemset, view)
        for dataset in problemset.GetChildren():
            self._createInitialViews(dataset)
            
        self.normalFont = ws.SmFont(wx.SWISS, wx.NORMAL, wx.NORMAL)
        self.busyFont = ws.SmFont(wx.SWISS, wx.ITALIC, wx.NORMAL)
                        
        # Process status events while it's loading
        self.Bind(ws.EVT_UPDATE_STATUS, self.OnUpdateStatus)
        self.Bind(wx.EVT_MENU, self.OnRename, id=constants.ID_RENAME_RESULT)
        self.Bind(wx.EVT_MENU, self.OnDelete, id=constants.ID_DELETE_RESULT)
        # Handle page changes
        #self.Bind(ws.EVT_RESULTBOOK_VIEW_CHANGED, self.OnPageChanged)
        
    def OnRename(self, evt):
        self.tree.EditLabel(self.tree.GetSelection())
        
    def OnDelete(self, evt):
        self.DeleteSelection()
        
    def OnUpdateStatus(self, evt):
        #print "ResultViewBook.OnUpdateStatus"
        self._updateItemStatus(evt.GetData())
        
    def _updateItemStatus(self, result):
        item = self._treeitems[result]
        if result.IsFileAvailable():
            self.tree.SetItemTextColour(item, wx.Colour(0,0,0))
            self.tree.SetItemText(item, result.GetName())
            self.tree.SetItemFont(item, self.normalFont)
        else:
            self.tree.SetItemTextColour(item, wx.Colour(200,200,200))
            self.tree.SetItemFont(item, self.busyFont)
            self.tree.SetItemText(item, result.GetName() + " (Running...)")
        
    def _createInitialViews(self, dataset):
        self.NewView(dataset)
        # Add all the child views
        for resultset in dataset.GetChildren():
            self._createInitialViews(resultset)
        
    def NewView(self, dataset):
        v = self.viewFactory.CreateView(dataset)
        dataset.AddChangeListener(v)
        dataset.AddChangeListener(self)
        self.AddView(dataset.GetParent(), dataset, v)
        v.RefreshContents()
        return v  
        
    def GetPopupMenu(self, result):
        menu = wx.Menu()
        menu.Append(constants.ID_SHOW_INFO, "Info")
        menu.Append(constants.ID_RENAME_RESULT, "Rename")        
        if result.GetParent() != None:
            menu.AppendSeparator()
            menu.Append(constants.ID_DELETE_RESULT, "Delete")
            if result.IsSetResult():
                menu.AppendSeparator()                
                menu.Append(constants.ID_ANALYSIS_ADD_SET_TO_LIBRARY,
                "Add set to library...")
            menu.AppendSeparator()
            menu.Append(constants.ID_ANALYSIS_SAVE_IN_LIBRARY,
                "Create recipe based on result")
                
        return menu

    def RefreshContents(self):
        if self.GetViewCount() == 0:
            return None
        (result, view) = self.GetSelection()
        view.RefreshContents()
        
    def RebuildView(self):
        if self.GetViewCount() == 0:
            return None
        (result, view) = self.GetSelection()
        view.RebuildView()


class TestFrame(wx.Frame):
    def __init__(self):
        wx.Frame.__init__(self, None, -1, "Test", style=wx.DEFAULT_FRAME_STYLE) 
        self.statusbar = self.CreateStatusBar(1, wx.ST_SIZEGRIP)       
        self.Bind(wx.EVT_SIZE, self.OnSize)
        book = self.p = ViewBook(self)
        sz = wx.BoxSizer(wx.HORIZONTAL)
        sz.Add(self.p, flag=wx.EXPAND)
        self.SetSizerAndFit(sz)
        # Testing views
        p0 = wx.Window(self)
        p0.SetBackgroundColour("green")
        wx.StaticText(p0, -1, "Root Panel", (5,5))
        p1 = wx.Window(self)
        p1.SetBackgroundColour("sky blue")
        wx.StaticText(p1, -1, "Panel One", (5,5))
        p2 = wx.Window(self)
        p2.SetBackgroundColour("red")
        wx.StaticText(p2, -1, "Panel Two", (5,5))
        root = "Root Object"
        obj1 = "Panel One"
        obj1 = GraphResult("foo", "gif", None)
        obj2 = "Panel Two"
        book.AddView(None, root, p0)
        book.AddView(root, obj1, p1)
        book.AddView(root, obj2, p2)
        book.SetSelection(obj1)

        self.Bind(ws.EVT_RESULTBOOK_VIEW_CHANGED, self.OnChangeSel)

    def OnChangeSel(self, evt):
        print "Change is good"

    def OnSize(self, evt):
        print "Size"
        self.p.SetSize(self.GetSize())


class TestApp(wx.App):
    def __init__(self):
        wx.App.__init__(self, redirect=False)
        f = TestFrame()
        f.Show()
if False:
    app = TestApp()
    app.MainLoop()