import wx
import sys

from wx.lib import masked

#from Utility.compat import convertINI


################################################################
#
# Class: ColumnsDialog
#
# Allows a user to alter the settings and appearance of 
# columns in a ManagedList.
#
################################################################
class ColumnsDialog(wx.Dialog):
    def __init__(self, list):
        
        self.utility = list.utility
        
        title = self.utility.lang.get('columns')
        
        pre = wx.PreDialog()
        pre.Create(list, -1, title)
        self.this = pre.this

        outerbox = wx.BoxSizer(wx.VERTICAL)
        
        self.columnsPanel = ColumnsPanel(self, list)
        
        applybtn  = wx.Button(self, -1, self.utility.lang.get('apply'))
        self.Bind(wx.EVT_BUTTON, self.onApply, applybtn)
        
        okbtn  = wx.Button(self, -1, self.utility.lang.get('ok'))
        self.Bind(wx.EVT_BUTTON, self.onOK, okbtn)
        
        cancelbtn = wx.Button(self, wx.ID_CANCEL, self.utility.lang.get('cancel'))
        
#        setDefaultsbtn = wx.Button(self, -1, self.utility.lang.get('reverttodefault'))
        
        buttonbox = wx.BoxSizer(wx.HORIZONTAL)
        buttonbox.Add(applybtn, 0, wx.ALL, 5)
        buttonbox.Add(okbtn, 0, wx.ALL, 5)
        buttonbox.Add(cancelbtn, 0, wx.ALL, 5)
        
        outerbox.Add(self.columnsPanel, 0, wx.EXPAND|wx.ALL, 5)
        outerbox.Add(buttonbox, 0, wx.ALIGN_CENTER)
       
        self.SetAutoLayout(True)
        self.SetSizer(outerbox)
        self.Fit()
        
    def onOK(self, event = None):
        if self.onApply(event):
            self.EndModal(wx.ID_OK)
        
    def onApply(self, event = None):
        self.columnsPanel.apply()
        return True


################################################################
#
# Class: ColumnsPanel
#
# Contains the interface elements for a ColumnsDialog
#
################################################################
class ColumnsPanel(wx.Panel):
    def __init__(self, parent, list):
        wx.Panel.__init__(self, parent, -1)
        
        # Constants
        self.RANK = 0
        self.COLID = 1
        self.TEXT = 2
        self.WIDTH = 3

        self.utility = parent.utility
        
        self.list = list
        self.columns = list.columns
        
        self.changed = False
        
        self.changingvalue = False

        self.leftid = []
        self.rightid = []
        self.leftindex = -1
        self.rightindex = -1
        
        sizer = wx.BoxSizer(wx.VERTICAL)
        
        listsizer = wx.BoxSizer(wx.HORIZONTAL)
        
        # unselected list ctrl
       
        self.checklistbox = wx.CheckListBox(self, -1, size = wx.Size(150, 200), style = wx.LB_SINGLE)

        listsizer.Add(self.checklistbox, 0, wx.ALL, 5)

        # Up & Down button
        ###################        
        self.upbutton = self.utility.makeBitmapButton(self, 'moveup.bmp', 'move_up', self.OnMove)
        self.downbutton = self.utility.makeBitmapButton(self, 'movedown.bmp', 'move_down', self.OnMove)

        updownsizer = wx.BoxSizer(wx.VERTICAL)
        
        updownsizer.Add(self.upbutton, 0, wx.BOTTOM, 5)
        updownsizer.Add(self.downbutton, 0, wx.TOP, 5)
        
        listsizer.Add(updownsizer, 0, wx.ALL, 5)
        
        sizer.Add(listsizer, 0)
        
        labelbox = wx.BoxSizer(wx.HORIZONTAL)
        labelbox.Add(wx.StaticText(self, -1, self.utility.lang.get('displayname')), 0, wx.ALIGN_CENTER_VERTICAL|wx.RIGHT, 5)
        columnlabel = ""
        self.labelsetting = wx.TextCtrl(self, -1, columnlabel)
        labelbox.Add(self.labelsetting, 0, wx.ALIGN_CENTER_VERTICAL)
        
        sizer.Add(labelbox, 0, wx.ALL, 5)
        
        widthbox = wx.BoxSizer(wx.HORIZONTAL)
        widthbox.Add(wx.StaticText(self, -1, self.utility.lang.get('columnwidth')), 0, wx.ALIGN_CENTER_VERTICAL|wx.RIGHT, 5)
        initialvalue = 0
        self.widthsetting = self.utility.makeNumCtrl(self, initialvalue, integerWidth = 4, max = 2000)
        widthbox.Add(self.widthsetting, 0, wx.ALIGN_CENTER_VERTICAL)
        
        sizer.Add(widthbox, 0, wx.ALL, 5)
               
        self.getDefaultValues()

        self.labelsetting.Enable(False)
        self.widthsetting.Enable(False)
        
        self.SetSizerAndFit(sizer)

        # Add Event
        #########################
        self.Bind(wx.EVT_LISTBOX, self.OnSelect, self.checklistbox)
        self.Bind(wx.EVT_TEXT, self.OnChangeLabel, self.labelsetting)
        self.Bind(masked.EVT_NUM, self.OnChangeWidth, self.widthsetting)

    def getDefaultValues(self):
        unselected = []
        
        selected = []

        for colid in range(self.columns.minid, self.columns.maxid):
            rank = self.utility.config.Read(self.list.prefix + str(colid) + "_rank", "int")
            text = self.utility.lang.get(self.list.prefix + str(colid) + "_text")
            width = self.utility.config.Read(self.list.prefix + str(colid) + "_width", "int")
            if colid in self.list.exclude:
                pass
            elif rank == -1:
                unselected.append([rank, colid, text, width])
            else:
                selected.append([rank, colid, text, width])
        
        unselected.sort()
        selected.sort()
        
        self.columnlist = selected + unselected
        
        self.checklistbox.Set([item[2] for item in self.columnlist])
        
        for i in range(len(self.columnlist)):
            if self.columnlist[i][0] != -1:
                self.checklistbox.Check(i)
    
    # Select one of the items in the list
    def OnSelect(self, event):
        # The index of the selection within the checklistbox
        index = self.checklistbox.GetSelection()
        if index == wx.NOT_FOUND:
            self.labelsetting.Enable(False)
            self.widthsetting.Enable(False)
            return
        self.labelsetting.Enable(True)
        self.widthsetting.Enable(True)
            
        textstring = self.checklistbox.GetString(index)

        self.changingvalue = True
        self.labelsetting.SetValue(textstring)
        self.widthsetting.SetValue(self.columnlist[index][self.WIDTH])
        self.changingvalue = False
        
    def OnChangeLabel(self, event):
        if self.changingvalue:
            return
        
        index = self.checklistbox.GetSelection()
        if index == wx.NOT_FOUND:
            return
        
        oldlabel = self.columnlist[index][self.TEXT]
        newlabel = self.labelsetting.GetValue()
        if oldlabel == newlabel:
            return
            
        self.columnlist[index][self.TEXT] = newlabel
        self.checklistbox.SetString(index, newlabel)
        
    def OnChangeWidth(self, event):
        if self.changingvalue:
            return
        
        index = self.checklistbox.GetSelection()
        if index == wx.NOT_FOUND:
            return
        
        self.columnlist[index][self.WIDTH] = self.widthsetting.GetValue()
    
    # Move a list item up or down           
    def OnMove(self, event):
        # Move up
        if event.GetId() == self.upbutton.GetId():
            direction = -1
        # Move down
        else:
            direction = 1
        
        index = self.checklistbox.GetSelection()
        if index == wx.NOT_FOUND:
            # Nothing is selected:
            return

        if (direction == 1) and (index == self.checklistbox.GetCount() - 1):
            #Last Item can't move down anymore
            return
        elif (direction == -1) and (index == 0):
            # First Item can't move up anymore
            return
        else:
            self.columnlist[index], self.columnlist[index + direction] = self.columnlist[index + direction], self.columnlist[index]

            col1text = self.checklistbox.GetString(index)
            col2text = self.checklistbox.GetString(index + direction)

            col1checked = self.checklistbox.IsChecked(index)
            col2checked = self.checklistbox.IsChecked(index + direction)

            #Update display
            self.checklistbox.SetString(index + direction, col1text)
            self.checklistbox.SetString(index, col2text)
            
            self.checklistbox.Check(index + direction, col1checked)
            self.checklistbox.Check(index, col2checked)
            
            self.checklistbox.SetSelection(index + direction)

    def apply(self):
        selected = 0
        for i in range(0, self.checklistbox.GetCount()):
            colid = self.columnlist[i][1]
            if self.checklistbox.IsChecked(i):
                self.columnlist[i][self.RANK] = selected
                selected += 1
            else:
                self.columnlist[i][self.RANK] = -1

        # Check to see if anything has changed
        overallchange = False                
        for item in self.columnlist:
            colid = item[self.COLID]

            rank = item[self.RANK]
            changed = self.utility.config.Write(self.list.prefix + str(colid) + "_rank", rank)
            if changed:
                overallchange = True
            
            changed = self.utility.lang.writeUser(self.list.prefix + str(colid) + "_text", item[self.TEXT])
            if changed:
                overallchange = True

            width = item[self.WIDTH]
            changed = self.utility.config.Write(self.list.prefix + str(colid) + "_width", width)
            if changed:
                overallchange = True

        # APPLY on-the-fly
        if overallchange:
            self.utility.config.Flush()
            self.utility.lang.flush()
            self.list.updateColumns()


################################################################
#
# Class: ColumnManager
#
# Keep track of the settings for order and appearance of
# columns in a ManagedList
#
################################################################
class ColumnManager:
    def __init__(self, list):
        self.utility = list.utility
        
        self.minid = list.minid
        self.maxid = list.maxid
        self.prefix = list.prefix
        
        self.exclude = list.exclude

        self.active = []
        
        self.getColumnData()

    # Method used to compare two elements of self.active
    def compareRank(self, a, b):
        if a[1] < b[1]:
            return -1
        if a[1] > b[1]:
            return 1
        else:
            return 0
        
    def getNumCol(self):
        return len(self.active)
        
    def getRankfromID(self, colid):
        return self.utility.config.Read(self.prefix + str(colid) + "_rank", "int")

    def getIDfromRank(self, rankid):
        return self.active[rankid][0]
    
    def getTextfromRank(self, rankid):
        colid = self.active[rankid][0]
        return self.utility.lang.get(self.prefix + str(colid) + "_text")
    
    def getValuefromRank(self, rankid):
        colid = self.active[rankid][0]
        return self.utility.config.Read(self.prefix + str(colid) + "_width", "int")

    def getColumnData(self):
        self.active = []

        # Get the list of active columns
        for colid in range(self.minid, self.maxid):
            rank = self.utility.config.Read(self.prefix + str(colid) + "_rank", "int")
            
            if (rank < 0 or colid in self.exclude):
                self.utility.config.Write(self.prefix + str(colid) + "_rank", -1)
            elif (rank > -1):
                self.active.append([colid, rank])
                
        # Sort the columns by rank
        self.active.sort(self.compareRank)
        
        # Make sure that the columns are in an order that makes sense
        # (i.e.: if we have a config with IDs of 4, 99, 2000 then
        #        we'll convert that to 0, 1, 2)
        for i in range(len(self.active)):
            colid = self.active[i][0]
            rank = i
            self.active[i][1] = rank
            self.utility.config.Write(self.prefix + str(colid) + "_rank", rank)
        self.utility.config.Flush()

    def getSortedColumn(self):
        sorted_column = self.utility.config.Read(self.prefix + "_sortedcolumn", "int")
        reversesort = self.utility.config.Read(self.prefix + "_reversesort", "int")
        return sorted_column, reversesort
    
    def writeSortedColumn(self, sorted_column, reversesort):
        self.utility.config.Write(self.prefix + "_sortedcolumn", sorted_column)
        self.utility.config.Write(self.prefix + "_reversesort", reversesort)
    
################################################################
#
# Class: ManagedList
#
# An extension of wx.ListCtrl that keeps track of column
# settings in a unified manner.
#
################################################################       
class ManagedList(wx.ListCtrl):
    """ 
    IDs in rightalign and centeralign must be set in Utility.constants;
    Column labels must be set in the language file;
    If some columns are not displayed by default, they can be set in 
    Utility.utility.setupConfig()
    """
        
    def __init__(self, parent, style, prefix, minid, maxid, exclude = [], rightalign = [], centeralign = []):
        wx.ListCtrl.__init__(self, parent, -1, style = style)
        
        self.parent = parent
        
        self.prefix = prefix
        self.minid = minid
        self.maxid = maxid
        self.exclude = exclude
        
        self.rightalign = rightalign
        self.centeralign = centeralign
        
        self.utility = parent.utility
        
        self.columns = ColumnManager(self)
        
        self.Bind(wx.EVT_LIST_COL_END_DRAG, self.OnResizeColumn)
        self.Bind(wx.EVT_LIST_COL_RIGHT_CLICK, self.OnColRightClick)
        
        self.loadColumns()
        
        self.loadFont()
        
        # Add to the list of ManagedList objects
        self.utility.lists[self] = True
        
    def loadFont(self):
        # Get font information
        fontinfo = self.utility.config.Read('listfont', "bencode-fontinfo")
        font = self.utility.getFontFromInfo(fontinfo)

        # Only change if we've gotten an acceptable font
        if font.Ok():
            # Jump to the top of the list
            # self.EnsureVisible(0)    #_# remove this line to be compatible on linux
            # Change the font
            self.SetFont(font)
            # Refresh to make the change visable
            self.Refresh()

    def loadColumns(self):
        # Delete Old Columns (if they exist)
        #################################################
        numcol = self.GetColumnCount()
        for i in range(numcol):
            self.DeleteColumn(0)

        # Read status display
        ####################################
        
        for rank in range(self.columns.getNumCol()):
            colid = self.columns.getIDfromRank(rank)
            if colid in self.rightalign:
                style = wx.LIST_FORMAT_RIGHT
            elif colid in self.centeralign:
                style = wx.LIST_FORMAT_CENTER
            else:
                style = wx.LIST_FORMAT_LEFT
            text = self.columns.getTextfromRank(rank)
            width = self.columns.getValuefromRank(rank)
            # Don't allow a column to have a size of 0
            if width == 0:
                width = -1
            self.InsertColumn(rank, text, style, width)
        
        # Save the width of the column that was just resized
    def OnResizeColumn(self, event):
        if self.utility.config.Read('savecolumnwidth', "boolean"):
            rank = event.GetColumn()
            width = self.GetColumnWidth(rank)
            colid = self.columns.getIDfromRank(rank)
            self.utility.config.Write(self.prefix + str(colid) + "_width", width)
            self.utility.config.Flush()
            
    # Create a list of columns that are active/inactive
    def OnColRightClick(self, event):
        if not hasattr(self, "columnpopup"):
            self.makeColumnPopup()
            
        # Check off columns for all that are currently active
        for colid in range(self.minid, self.maxid):
            if colid in self.exclude:
                continue
            
            if self.utility.config.Read(self.prefix + str(colid) + "_rank", "int") > -1:
                self.columnpopup.Check(777 + colid, True)
            else:
                self.columnpopup.Check(777 + colid, False)
        
        self.lastcolumnselected = event.GetColumn()
               
        self.PopupMenu(self.columnpopup, event.GetPosition())
        
    def makeColumnPopup(self):
        self.columnpopup = wx.Menu()
        
        for colid in range(self.minid, self.maxid):
            if colid in self.exclude:
                continue
            
            text = self.utility.lang.get(self.prefix + str(colid) + '_text')
            self.columnpopup.Append(777 + colid, text, text, wx.ITEM_CHECK)
            
        self.columnpopup.AppendSeparator()
        
        self.utility.makePopup(self.columnpopup, self.onColumnDialog, 'more')

        startid = 777 + self.minid
        endid = 777 + (self.maxid - 1)

        self.Bind(wx.EVT_MENU, self.onSelectColumn, id=startid, id2=endid)
        
    def onColumnDialog(self, event = None):
        dialog = ColumnsDialog(self)
        dialog.ShowModal()
        dialog.Destroy()

    def onSelectColumn(self, event):
        eventid = event.GetId()
        colid = eventid - 777
        oldrank = self.utility.config.Read(self.prefix + str(colid) + "_rank", "int")

        if oldrank > -1:
            # Column was deselected, don't show it now
            # (update ranks for the rest of the columns that appeared after it)
            for i in range (self.minid, self.maxid):
                temprank = self.utility.config.Read(self.prefix + str(i) + "_rank", "int")
                if (i == colid):
                    self.utility.config.Write(self.prefix + str(i) + "_rank", -1)
                elif (temprank > oldrank):
                    self.utility.config.Write(self.prefix + str(i) + "_rank", temprank - 1)
                else:
                    self.utility.config.Write(self.prefix + str(i) + "_rank", temprank)
        else:
            # Column was selected, need to show it
            # Put it after the closest column
            if hasattr(self, 'lastcolumnselected'):
                if self.lastcolumnselected == -1:
                    newrank = self.GetColumnCount()
                else:
                    newrank = self.lastcolumnselected    # + 1
            # (just tack it on the end of the display)
            else:
                newrank = self.GetColumnCount()
            
            for i in range (self.minid, self.maxid):
                temprank = self.utility.config.Read(self.prefix + str(i) + "_rank", "int")
                if (i == colid):
                    self.utility.config.Write(self.prefix + str(i) + "_rank", newrank)
                elif (temprank >= newrank):
                    self.utility.config.Write(self.prefix + str(i) + "_rank", temprank + 1)
                else:
                    self.utility.config.Write(self.prefix + str(i) + "_rank", temprank)
        self.utility.config.Flush()
        
        # Write changes to the config file and refresh the display
        self.updateColumns()
    
    def updateColumns(self):
        self.columns.getColumnData()
        self.loadColumns()

        self.parent.updateColumns(force = True)
    
    def getSelected(self, firstitemonly = False, reverse = False):
        selected = []
        index = self.GetNextItem(-1, wx.LIST_NEXT_ALL, wx.LIST_STATE_SELECTED)
        while index != -1:
            selected.append(index)
            if (firstitemonly):
                return selected
            index = self.GetNextItem(index, wx.LIST_NEXT_ALL, wx.LIST_STATE_SELECTED)
        selected.sort()
        if reverse:
            selected.reverse()
        return selected
        
    def selectAll(self):
        for index in range(self.GetItemCount()):
            self.Select(index)
    
    def invertSelection(self):
        for index in range(self.GetItemCount()):
            self.SetItemState(index, 4 - self.GetItemState(index, wx.LIST_STATE_SELECTED), wx.LIST_STATE_SELECTED)
