'''
 ====================================================================
 Copyright (c) 2003-2006 Barry A Scott.  All rights reserved.

 This software is licensed as described in the file LICENSE.txt,
 which you should have received as part of this distribution.

 ====================================================================

    wb_list_panel_common.py

'''
import os
import sys
import cPickle

import wx

import wb_config
import wb_exceptions
import wb_ids
import wb_shell_commands
import wb_dialogs

class ListSortData:
    def __init__( self, order=1, field=0 ):
        self.order = order
        self.field = field

    def setField( self, field ):
        if self.field == field:
            # toggle order
            self.order *= -1
        else:
            # new field forward
            self.field = field
            self.order = 1

    def getField( self ):
        return self.field

    def getOrder( self ):
        return self.order


class ListItemState:
    def __init__( self ):
        self.modified = False
        self.versioned = False
        self.new_versioned = False
        self.unversioned = False
        self.need_checkin = False
        self.need_checkout = False
        self.conflict = False
        self.file_exists = False
        self.is_project_parent = False

    def printState( self, title='' ):
        print '-----------',title
        for item in self.__dict__.items():
            print 'ListState: %s -> %r' % item

class WbListCtrl(wx.ListCtrl):
    def __init__( self, parent, list_id ):
        wx.ListCtrl.__init__( self, parent, list_id,
                                style=wx.LC_REPORT|wx.NO_BORDER|wx.LC_HRULES|wx.LC_VIRTUAL )
        self.parent = parent

    def OnGetItemText(self, item, col):
        if self.parent.list_handler is not None:
            return self.parent.list_handler.OnGetItemText( item, col )
        else:
            return ''

    def OnGetItemImage(self, item):
        return -1

    def OnGetItemAttr(self, item):
        if self.parent.list_handler is not None:
            return self.parent.list_handler.OnGetItemAttr( item )
        else:
            return None

class WbListPanelCommon(wx.Panel):
    def __init__( self, app, frame, parent ):
        wx.Panel.__init__(self, parent, -1)

        self.app = app
        self.frame = frame
        try_wrapper = wb_exceptions.TryWrapperFactory( self.app.log )

        self.filter_field = 'Name'
        self.filter_text = ''

        self.header_panel = HeaderPanel( self, app, self.filter_field )
        self.header_panel.setFilterChangedHandler( self.OnFilterChanged )

        self.v_sizer = wx.BoxSizer( wx.VERTICAL )

        self.id_list = wx.NewId()
        self.list_ctrl = WbListCtrl( self, self.id_list )

        self.v_sizer.Add( self.header_panel, 0, wx.EXPAND|wx.ALL, 0 )
        self.v_sizer.Add( self.list_ctrl, 1, wx.EXPAND|wx.ALL, 0 )

        acc_tab = wx.AcceleratorTable( self.getAcceleratorTableInit() )
        self.list_ctrl.SetAcceleratorTable( acc_tab )

        wx.EVT_SIZE( self, try_wrapper( self.OnSize ) )

        wx.EVT_LIST_COL_CLICK( self.list_ctrl, self.id_list, self.app.eventWrapper( self.OnColClick ))

        wx.EVT_LIST_ITEM_ACTIVATED( self.list_ctrl, self.id_list, self.app.eventWrapper( self.OnItemActivated ) )

        wx.EVT_LIST_ITEM_RIGHT_CLICK( self.list_ctrl, self.id_list, self.app.eventWrapper( self.OnRightClick ) )
        if wx.Platform in ['__WXMSW__','__WXMAC__']:
            wx.EVT_LIST_BEGIN_DRAG( self.list_ctrl, self.id_list, self.app.eventWrapper( self.OnDragBegin ) )

        wx.EVT_LIST_ITEM_SELECTED( self.list_ctrl, self.id_list, self.OnItemSelected )
        wx.EVT_LIST_ITEM_DESELECTED( self.list_ctrl, self.id_list, self.OnItemDeselected )

        wx.EVT_SET_FOCUS( self.list_ctrl, self.OnSetFocus )
        wx.EVT_KILL_FOCUS( self.list_ctrl, self.OnKillFocus )  

        self.addToSizer( self.v_sizer )

        self.SetAutoLayout( True )
        self.SetSizer( self.v_sizer )
        self.v_sizer.Fit( self )
        self.Layout()

        view_prefs = self.app.prefs.getView()
        self.sort_data = ListSortData( view_prefs.sort_order, view_prefs.sort_field )

        self.list_handler = None

        self.last_char_timestamp = 0
        self.inc_search_string = ''

    def __repr__( self ):
        return '<WbListPanelCommon %r>' % self.list_handler

    def addToSizer( self, v_sizer ):
        pass

    def OnSetFocus( self, event ):
        self.frame.setEventHandler( self )

    def OnKillFocus( self, event ):
        #self.frame.clearEventHandler()
        pass

    def OnItemSelected( self, event ):
        self.frame.clearUpdateUiState()
        self.frame.setEventHandler( self )

    def OnItemDeselected( self, event ):
        self.frame.clearUpdateUiState()
        self.frame.setEventHandler( self )

    def OnDragBegin( self, event ):
        #print 'WbListPanel.OnDragBegin'

        all_filenames = [self.list_handler.getFilename( row ) for row in self.getSelectedRows()]

        # create our own data format and use it in a
        # custom data object
        svn_data = wx.CustomDataObject(wx.CustomDataFormat("WorkBench.svn_wc_path"))
        svn_data.SetData( cPickle.dumps( all_filenames ) )


        # Now make a data object for the text and also a composite
        # data object holding both of the others.
        text_data = wx.TextDataObject( 'the text of the list ctrl file name' )

        data = wx.DataObjectComposite()
        data.Add( svn_data )
        data.Add( text_data )

        # And finally, create the drop source and begin the drag
        # and drop opperation
        src = wx.DropSource( self )
        src.SetData( data )
        #print 'Begining DragDrop'
        src.DoDragDrop( wx.Drag_AllowMove )
        #print 'DragDrop completed: %d' % result

    def savePreferences( self ):
        view_prefs = self.app.prefs.getView()
        view_prefs.sort_order = self.sort_data.getOrder()
        view_prefs.sort_field = self.sort_data.getField()

    def getProjectInfo( self ):
        if self.list_handler:
            return self.list_handler.getProjectInfo()
        return None

    def clearHandler( self ):
        self.app.log.debug( 'WbListPanelCommon.clearHandler()' )
        self.list_handler = None
        # empty the list
        self.list_ctrl.DeleteAllItems()

    def updateHandler( self ):
        self.app.log.debug( 'WbListPanelCommon.updateHandler() %r' % self.list_handler )
        if self.list_handler:
            self.list_handler.setupColumns()
            self.list_handler.updateStatus()

        self.drawList()

    def setHandler( self, list_handler ):
        self.app.log.debug( 'WbListPanelCommon.setHandler( %r )' % list_handler )
        self.list_handler = list_handler
        self.list_handler.setupColumns()
        self.list_handler.updateStatus()

        self.filter_field = ''
        self.header_panel.clearFilterText()

        self.drawList()

    def drawList( self ):
        self.app.log.debug( 'WbListPanelCommon.drawList() %r' % self.list_handler )

        if self.list_handler:
            self.list_handler.initList( self.sort_data, self.filter_field, self.filter_text )

    def getHandler( self ):
        return self.list_handler

    def updateHeader( self, url_name, path_name ):
        self.header_panel.updateHeader( url_name, path_name )

    def OnFilterChanged( self, field, text ):
        self.filter_field = field
        self.filter_text = text
        self.drawList()

    def OnSize( self, event ):
        w, h = self.GetClientSizeTuple()
        self.v_sizer.SetDimension( 0, 0, w, h )

    def OnItemActivated(self, event):
        if not self.list_handler:
            return

        for row in self.getSelectedRows():
            filename = self.list_handler.getFilename( row )
            if self.list_handler.mayOpen( row ):
                self.app.selectTreeNode( filename )

            elif not os.path.isdir( filename ):
                wb_shell_commands.EditFile( self.app, self.list_handler.getProjectInfo(), filename )

    def isTreeHandler( self ):
        return False

    def isListHandler( self ):
        return True

    def getUpdateUiState( self ):
        if self.list_handler is None:
            return None
        return self.list_handler.getState( self.getSelectedRows() )

    def OnRightClick( self, event ):
        if not self.list_handler:
            return

        self.frame.getUpdateUiState()
        if True:
            menu = self.list_handler.getContextMenu()
            self.list_ctrl.PopupMenu( menu, event.GetPoint() )
            menu.Destroy()

    def OnColClick( self, event ):
        self.app.log.debug( 'OnColClick %r' % self.list_handler  )
        self.sort_data.setField( self.list_handler.getColumnId( event.m_col ) )
        if not self.list_handler:
            return

        self.list_handler.sortList( self.sort_data )


    # command handlers
    def OnFileEdit( self ):
        if not self.list_handler:
            return

        for row in self.getSelectedRows():
            filename = self.list_handler.getFilename( row )

            wb_shell_commands.EditFile( self.app, self.list_handler.getProjectInfo(), filename )

    def OnShellOpen( self ):
        if not self.list_handler:
            return

        for row in self.getSelectedRows():
            filename = self.list_handler.getFilename( row )

            wb_shell_commands.ShellOpen( self.app, self.list_handler.getProjectInfo(), filename )

    def OnSpEditCopy( self ):
        return self.Sp_Dispatch( 'Cmd_File_EditCopy' )

    def OnSpEditCut( self ):
        return self.Sp_Dispatch( 'Cmd_File_EditCut' )

    def OnSpEditPaste( self ):
        return self.Sp_Dispatch( 'Cmd_File_EditPaste' )

    def OnSpAdd( self ):
        return self.Sp_Dispatch( 'Cmd_File_Add' )

    def OnSpAnnotate( self ):
        return self.Sp_Dispatch( 'Cmd_File_Annotate' )

    def OnSpCheckin( self ):
        return self.Sp_Dispatch( 'Cmd_File_Checkin' )

    def OnSpCleanup( self ):
        return self.Sp_Dispatch( 'Cmd_File_Cleanup' )

    def OnSpDelete( self ):
        return self.Sp_Dispatch( 'Cmd_File_Delete' )

    def OnSpDiffWorkBase( self ):
        return self.Sp_Dispatch( 'Cmd_File_DiffWorkBase' )

    def OnSpDiffWorkHead( self ):
        return self.Sp_Dispatch( 'Cmd_File_DiffWorkHead' )
 
    def OnSpDiffWorkBranchOriginBase( self ):
        return self.Sp_Dispatch( 'Cmd_File_DiffWorkBranchOriginBase' )
 
    def OnSpDiffWorkBranchOriginHead( self ):
        return self.Sp_Dispatch( 'Cmd_File_DiffWorkBranchOriginHead' )

    def OnSpDiffMineNew( self ):
        return self.Sp_Dispatch( 'Cmd_File_DiffMineNew' )

    def OnSpDiffOldMine( self ):
        return self.Sp_Dispatch( 'Cmd_File_DiffOldMine' )

    def OnSpDiffOldNew( self ):
        return self.Sp_Dispatch( 'Cmd_File_DiffOldNew' )

    def OnSpHistory( self ):
        return self.Sp_Dispatch( 'Cmd_File_History' )

    def OnSpInfo( self ):
        return self.Sp_Dispatch( 'Cmd_File_Info' )

    def OnSpLock( self ):
        return self.Sp_Dispatch( 'Cmd_File_Lock' )

    def OnSpProperties( self ):
        return self.Sp_Dispatch( 'Cmd_File_Properties' )

    def OnSpRename( self ):
        return self.Sp_Dispatch( 'Cmd_File_Rename' )

    def OnSpRevert( self ):
        return self.Sp_Dispatch( 'Cmd_File_Revert' )

    def OnSpResolved( self ):
        return self.Sp_Dispatch( 'Cmd_File_Resolved' )

    def OnSpUnlock( self ):
        return self.Sp_Dispatch( 'Cmd_File_Unlock' )

    def OnSpUpdate( self ):
        return self.Sp_Dispatch( 'Cmd_File_Update' )

    def Sp_Dispatch( self, sp_func_name ):
        self.app.trace.info( 'WbListPanel.Sp_Dispatch( %s ) event' % sp_func_name )
        if not self.list_handler:
            return

        sp_func = getattr( self.list_handler, sp_func_name )

        self.app.trace.info( 'WbListPanel.Sp_Dispatch( %s ) calling' % sp_func_name )
        return sp_func( self.getSelectedRows() )

    def getSelectedRows( self ):
        all_rows = []
        item_index = -1
        while True:
            item_index = self.list_ctrl.GetNextItem( item_index, wx.LIST_NEXT_ALL, wx.LIST_STATE_SELECTED )
            if item_index < 0:
                break

            all_rows.append( item_index )

        all_rows.sort()

        #print 'getSelectedRows() %r' % all_rows
        return all_rows
        
class HeaderPanel(wx.Panel):
    def __init__( self, parent, app, filter_field ):
        wx.Panel.__init__(self, parent, -1)

        self.app = app


        self.background_colour = wx.SystemSettings.GetColour( wx.SYS_COLOUR_3DFACE )

        self.v_sizer = wx.BoxSizer( wx.VERTICAL )
        self.h_sizer1 = wx.BoxSizer( wx.HORIZONTAL )
        self.h_sizer2 = wx.BoxSizer( wx.HORIZONTAL )

        # Don't set the controls readonly as that prevents copy of the text
        self.url_text_ctrl = wx.TextCtrl( self, 0, '' )
        self.path_text_ctrl = wx.TextCtrl( self, 0, '' )

        self.filter_changed_handler = None

        self.filter_field_choices = ['Name','Author']
        self.filter_choice_ctrl = wx.Choice( self, wx.NewId(), choices=self.filter_field_choices )
        self.filter_choice_ctrl.SetSelection( self.filter_field_choices.index( filter_field ) )
        if wx.Platform == '__WXMAC__':
            self.filter_text_ctrl = wx.TextCtrl( self, wx.NewId(), '', size=(-1,-1) )
        else:
            self.filter_text_ctrl = wx.TextCtrl( self, wx.NewId(), '', size=(-1,10) )
        self.filter_clear_button = wx.Button( self, wx.NewId(), 'X', style=wx.BU_EXACTFIT, size=(30, -1) )

        # share the space 50/50
        border = 3
        self.h_sizer1.Add( self.url_text_ctrl, 50, wx.EXPAND|wx.ALL, border )
        self.h_sizer1.Add( self.path_text_ctrl, 50, wx.EXPAND|wx.TOP|wx.BOTTOM|wx.RIGHT, border )

        border = 1
        self.h_sizer2.Add( self.filter_choice_ctrl, 0, wx.EXPAND|wx.LEFT|wx.RIGHT, border )
        if wx.Platform == '__WXMAC__':
            self.h_sizer2.Add( self.filter_text_ctrl, 1, wx.EXPAND|wx.ALL, border )
        else:
            self.h_sizer2.Add( self.filter_text_ctrl, 1, wx.EXPAND|wx.ALL, border+2 )
        self.h_sizer2.Add( self.filter_clear_button, 0, wx.EXPAND|wx.LEFT|wx.RIGHT, border )

        border = 3
        self.v_sizer.Add( self.h_sizer1, 0, wx.EXPAND|wx.ALL, 0 )
        self.v_sizer.Add( self.h_sizer2, 1, wx.EXPAND|wx.ALL, 0 )

        wx.EVT_BUTTON( self, self.filter_clear_button.GetId(), self.OnClearFilterText )
        wx.EVT_TEXT( self, self.filter_text_ctrl.GetId(), self.OnFilterTextChanged )
        wx.EVT_CHOICE( self, self.filter_choice_ctrl.GetId(), self.OnFilterTypeChanged )

        self.SetAutoLayout( True )
        self.SetSizer( self.v_sizer )
        self.v_sizer.Fit( self )
        self.Layout()

    def setFilterChangedHandler( self, handler ):
        self.filter_changed_handler = handler

    def __callFilterChangedHandler( self ):
        self.filter_changed_handler(
            self.filter_field_choices[ self.filter_choice_ctrl.GetSelection() ],
            self.filter_text_ctrl.GetValue() )

    def updateHeader(self, url_name, path_name ):
        if url_name is None:
            url_name = ''
        if path_name is None:
            path_name = ''

        self.url_text_ctrl.SetValue( url_name )
        self.path_text_ctrl.SetValue( path_name )

        self.SetBackgroundColour( self.background_colour )
        self.Refresh()

    def clearFilterText( self ):
        self.filter_text_ctrl.Clear()
        self.__callFilterChangedHandler()

    def OnClearFilterText( self, event=None ):
        self.filter_text_ctrl.Clear()
        self.__callFilterChangedHandler()

    def OnFilterTypeChanged( self, event ):
        self.filter_text_ctrl.Clear()
        self.__callFilterChangedHandler()

    def OnFilterTextChanged( self, event ):
        self.__callFilterChangedHandler()

class ListHandler:
    def __init__( self, list_panel ):
        self.list_panel = list_panel

    def sortList( self, sort_data ):
        raise wb_exceptions.InternalError( 'sortList not implemented' )
