'''
 ====================================================================
 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_subversion_list_handler.py

'''
import sys
import os

import pysvn
import wx

import wb_ids
import wb_subversion_list_handler_common
import wb_subversion_info_dialog
import wb_subversion_properties_dialog
import wb_subversion_utils
import wb_subversion_checkin
import wb_clipboard

class SubversionListHandler(wb_subversion_list_handler_common.SubversionListHandlerCommon):
    col_name = 'Name'
    col_state = 'State'
    col_date = 'Date'
    col_revision = 'Rev'
    col_author = 'Author'
    col_type = 'Type'
    col_size = 'Size'
    col_mime_type = 'Mimetype'
    col_eol_style = 'EOL'

    def __init__( self, app, list_panel, project_info ):
        wb_subversion_list_handler_common.SubversionListHandlerCommon.__init__( self, app, list_panel, project_info )

    def __repr__( self ):
        return '<SubversionListHandler %r>' % self.project_info

    def getContextMenu( self ):
        if self.project_info.need_checkout:
            menu_template = \
                [('', wb_ids.id_SP_Checkout, 'Checkout' )]
        else:
            menu_template = \
                [('', wb_ids.id_File_Edit, 'Edit' )]
            if wx.Platform in ['__WXMSW__','__WXMAC__']:
                menu_template += \
                    [('', wb_ids.id_Shell_Open, 'Open' )]
            menu_template += \
                [('-', 0, 0 )
                ,('', wb_ids.id_SP_DiffWorkBase, 'Diff WC vs. BASE...' )
                ,('', wb_ids.id_SP_DiffWorkHead, 'Diff WC vs. HEAD...' )
                ,('', wb_ids.id_SP_DiffWorkBranchOriginBase, 'Diff WC vs. branch origin BASE...' )
                ,('', wb_ids.id_SP_DiffWorkBranchOriginHead, 'Diff WC vs. branch origin HEAD...' )
                ,('>', wb_ids.id_SP_ConflictMenu, 'Conflict',
                    [('', wb_ids.id_SP_DiffOldMine, 'Diff Conflict Old vs. Mine...' )
                    ,('', wb_ids.id_SP_DiffMineNew, 'Diff Conflict Mine vs. New...' )
                    ,('', wb_ids.id_SP_DiffOldNew, 'Diff Conflict Old vs. New...' )
                    ,('-', 0, 0 )
                    ,('', wb_ids.id_SP_Resolved, 'Resolved Conflict' )
                    ])
                ,('-', 0, 0 )
                ,('', wb_ids.id_SP_Annotate, 'Annotate...' )
                ,('', wb_ids.id_SP_History, 'Log history...' )
                ,('', wb_ids.id_SP_Info, 'Information...' )
                ,('', wb_ids.id_SP_Properties, 'Properties...' )
                ,('-', 0, 0 )
                ,('', wb_ids.id_SP_Lock, 'Lock...' )
                ,('', wb_ids.id_SP_Unlock, 'Unlock...' )
                ,('-', 0, 0 )
                ,('', wb_ids.id_SP_Update, 'Update' )
                ,('-', 0, 0 )
                ,('', wb_ids.id_SP_Checkin, 'Checkin...' )
                ,('-', 0, 0 )
                ,('', wb_ids.id_SP_Add, 'Add' )
                ,('', wb_ids.id_SP_Rename, 'Rename...' )
                ,('-', 0, 0 )
                ,('', wb_ids.id_SP_Delete, 'Delete...' )
                ,('', wb_ids.id_SP_Revert, 'Revert...' )
                ,('-', 0, 0 )
                ,('', wb_ids.id_SP_Cleanup, 'Clean up' )
                ]

        return wb_subversion_utils.populateMenu( wx.Menu(), menu_template )

    def getAllGreyFilenames( self ):
        # show files on the clipboard in grey
        if self.app.hasPasteData():
            all_clipboard_filenames = self.app.getPasteData().getAllFilenames()
        else:
            all_clipboard_filenames = []
        return all_clipboard_filenames

    #------------------------------------------------------------
    def Cmd_File_EditCopy( self, all_rows ):
        self.app.setPasteData( wb_clipboard.Clipboard( [self.getFilename( row ) for row in all_rows], is_copy=True ) )
        print 'Copied %d files to the Clipboard' % len(all_rows)
        self.app.refreshFrame()
 
    def Cmd_File_EditCut( self, all_rows ):
        self.app.setPasteData( wb_clipboard.Clipboard( [self.getFilename( row ) for row in all_rows], is_copy=False ) )
        print 'Cut %d files to the Clipboard' % len(all_rows)
        self.app.refreshFrame()

    def Cmd_File_EditPaste( self, all_rows ):
        if not self.app.hasPasteData():
            return

        paste_data = self.app.getPasteData()
        self.app.clearPasteData()

        all_status = []
        try:
            for filename in paste_data.getAllFilenames():
                if os.path.isdir( filename ):
                    dir_status = self.project_info.client_bg.status( os.path.dirname( filename ), recurse=False )
                    all_status.extend( [s for s in dir_status if s.path == filename] )
                else:
                    all_status.extend( self.project_info.client_bg.status( filename, recurse=False ) )
        except pysvn.ClientError, e:
            self.app.log_client_error( e )
            return

        if paste_data.isCopy():
            title = 'Paste Copy'
        else:
            title = 'Paste Move'

        confirmed, force = self.app.confirmForceAction( title, self.getStatusAndFilenames( all_status ) )
        if not confirmed:
            return

        self.app.setProgress( title, 0 )

        for status in all_status:
            ok = False

            old_filename = status.path
            basename = os.path.basename( old_filename )
            new_filename = os.path.join( self.project_info.wc_path, basename )

            if paste_data.isCopy():
                rename_title = 'Save As'
            else:
                rename_title = 'Rename'

            while os.path.exists( new_filename ):
                new_name, force = self.app.renameFile( rename_title, os.path.basename( old_filename ), None )
                if new_name is None:
                    return
                new_filename = os.path.join( self.project_info.wc_path, new_name )

            self.app.log.info( '%s: From %s' % (title, old_filename) )
            self.app.log.info( '%s:   To %s' % (title, new_filename) )

            is_controlled = self.isControlled( status )

            yield self.app.backgroundProcess

            try:
                if paste_data.isCopy():
                    if os.path.isdir( old_filename ):
                        if is_controlled:
                            self.project_info.client_bg.copy( old_filename, new_filename )
                        else:
                            raise EnvironmentError( 'TBD - implement copy of folder' )
                            os.copydirtree( old_filename, new_filename )
                    else:
                        self.__copyFile( old_filename, new_filename, is_controlled, status.text_status, status.prop_status )

                    ok = True
                else:
                    if os.path.isdir( old_filename ):
                        if is_controlled:
                            self.project_info.client_bg.move( old_filename, new_filename, force=force )
                        else:
                            os.rename( old_filename, new_filename )
                    else:
                        text_status = self.getTextStatus( status )
                        prop_status = self.getPropStatus( status )
                        self.__moveFile( old_filename, new_filename, is_controlled, text_status, prop_status )

                    ok = True

            except EnvironmentError, e:
                self.app.log.error( str(e) )

            except pysvn.ClientError, e:
                self.app.log_client_error( e )

            yield self.app.foregroundProcess

            if not ok:
                break

        self.app.clearProgress()
        self.app.refreshFrame()


    #------------------------------------------------------------
    def Cmd_File_Add( self, all_rows ):
        try:
            for filename in [self.getFilename( row ) for row in all_rows]:
                self.project_info.client_fg.add( filename )
        except pysvn.ClientError, e:
            self.app.log_client_error( e )

        self.app.refreshFrame()

    # Cmd_File_Annotate - from SubversionListHandlerCommon

    def Cmd_File_Checkin( self, all_rows ):
        if len(all_rows) == 0:
            wx.MessageBox( "There are no changes to check in",
                "Warning", style=wx.OK|wx.ICON_EXCLAMATION )
            return

        all_files = [self.all_files[ row ] for row in all_rows]


        ci_frame = wb_subversion_checkin.CheckinFrame( self.app, self.project_info, all_files )
        ci_frame.Show( True )

    def Cmd_File_Cleanup( self, all_rows ):
        try:
            for filename in [self.getFilename( row ) for row in all_rows]:
                self.project_info.client_fg.cleanup( filename )
        except pysvn.ClientError, e:
            self.app.log_client_error( e )
        self.app.refreshFrame()

    def Cmd_File_Delete( self, all_rows ):
        if not self.app.confirmAction( 'Delete', self.getStatusAndFilenames( all_rows ) ):
            return

        for filename, is_controlled, text_status, prop_status in [
                (self.getFilename( row ), self.isControlled( row ), self.getTextStatus( row ), self.getPropStatus( row ))
                    for row in all_rows]:
            try:
                if is_controlled:
                    if text_status == pysvn.wc_status_kind.added:
                        self.project_info.client_fg.revert( filename )
                        os.remove( filename )

                    elif( text_status == pysvn.wc_status_kind.modified
                    or prop_status == pysvn.wc_status_kind.modified ):
                        self.project_info.client_fg.revert( filename )
                        self.project_info.client_fg.remove( filename )

                    else:
                        self.project_info.client_fg.remove( filename )

                else:
                    os.remove( filename )

            except pysvn.ClientError, e:
                self.app.log_client_error( e )

            except EnvironmentError, e:
                self.app.log.error( str(e) )

        self.app.refreshFrame()

    # Cmd_File_DiffWorkBase - from SubversionListHandlerCommon
    # Cmd_File_DiffWorkHead - from SubversionListHandlerCommon

    def Cmd_File_DiffOldNew( self, all_rows ):
        for row in all_rows:
            old_filename = self.getConflictOld( row )
            new_filename = self.getConflictNew( row )
            # qqq diff old_filename@None, new_filename@None
            self.app.Diff( old_filename, old_filename, new_filename, new_filename )

    def Cmd_File_DiffOldMine( self, all_rows ):
        for row in all_rows:
            old_filename = self.getConflictOld( row )
            mine_filename = self.getConflictMine( row )
            # qqq diff old_filename@None, mine_filename@None
            self.app.Diff( old_filename, old_filename, mine_filename, mine_filename )

    def Cmd_File_DiffMineNew( self, all_rows ):
        for row in all_rows:
            mine_filename = self.getConflictMine( row )
            new_filename = self.getConflictNew( row )
            # qqq diff mine_filename@None, new_filename@None
            self.app.Diff( mine_filename, mine_filename, new_filename, new_filename )

    # Cmd_File_History = from SubversionListHandlerCommon
    # Cmd_File_Info = from SubversionListHandlerCommon
    # Cmd_File_Lock= from SubversionListHandlerCommon
    # Cmd_File_Properties - from SubversionListHandlerCommon
    def __copyFile( self, old_filename, new_full_filename, is_controlled, text_status, prop_status ):
        if not is_controlled:
            raise EnvironmentError( 'TBD - copy file' )
            os.rename( old_filename, new_full_filename )
            return

        if( text_status == pysvn.wc_status_kind.normal
        and prop_status in [pysvn.wc_status_kind.normal, pysvn.wc_status_kind.none] ):
            self.project_info.client_fg.copy( old_filename, new_full_filename )
        else:
            raise EnvironmentError( 'Cannot move an added or modified file' )

    def __moveFile( self, old_filename, new_full_filename, is_controlled, text_status, prop_status ):
        if not is_controlled:
            os.rename( old_filename, new_full_filename )
            return

        if text_status == pysvn.wc_status_kind.added:
            # need to save and restore the props around the rename dance
            _, prop_dict = self.project_info.client_fg.proplist( old_filename,
                            revision=pysvn.Revision( pysvn.opt_revision_kind.working ) )[0]
            self.project_info.client_fg.revert( old_filename )
            print 'Rename %s %s' % (old_filename, new_full_filename)
            os.rename( old_filename, new_full_filename )
            self.project_info.client_fg.add( new_full_filename )
            for prop_name, prop_value in prop_dict.items():
                self.project_info.client_fg.propset( prop_name, prop_value, new_full_filename )

        elif( text_status == pysvn.wc_status_kind.modified
        or prop_status == pysvn.wc_status_kind.modified ):
            new_full_tmp_filename = None
            for tmp_name_index in range( 100 ):
                tmp_filename = os.path.join( os.path.dirname( old_filename ),
                    '%s.%d.tmp' % (new_full_filename, tmp_name_index) )
                if not os.path.exists( tmp_filename ):
                    new_full_tmp_filename = tmp_filename
                    break

            if new_full_tmp_filename is None:
                self.app.log.error( 'Failed to create tmp file for rename' )
            else:
                # need to save and restore the props around the rename dance
                all_props = self.project_info.client_fg.proplist( old_filename,
                                revision=pysvn.Revision( pysvn.opt_revision_kind.working ) )

                print 'Rename %s %s' % (old_filename, new_full_tmp_filename)
                os.rename( old_filename, new_full_tmp_filename )
                self.project_info.client_fg.revert( old_filename )
                self.project_info.client_fg.move( old_filename, new_full_filename )
                os.remove( new_full_filename )

                print 'Rename %s %s' % (new_full_tmp_filename, new_full_tmp_filename)
                os.rename( new_full_tmp_filename, new_full_filename )

                if len(all_props) > 0:
                    _, prop_dict = all_props[0]
                    for prop_name, prop_value in prop_dict.items():
                        self.project_info.client_fg.propset( prop_name, prop_value, new_full_filename )
        else:
            self.project_info.client_fg.move( old_filename, new_full_filename )

    def Cmd_File_Rename( self, all_rows ):
        for old_filename, is_controlled, text_status, prop_status in [
                (self.getFilename( row ), self.isControlled( row ), self.getTextStatus( row ), self.getPropStatus( row ))
                    for row in all_rows]:
            old_name = os.path.basename( old_filename )

            new_name, force = self.app.renameFile( "Rename", old_name, None )

            if new_name is None:
                break

            if new_name != old_name:
                new_full_filename = os.path.join( os.path.dirname( old_filename ), new_name )
                print 'Rename',old_filename, new_full_filename
                try:
                    self.__moveFile( old_filename, new_full_filename, is_controlled, text_status, prop_status )
                except pysvn.ClientError, e:
                    self.app.log_client_error( e )
                    break
                except EnvironmentError, e:
                    self.app.log.error( str(e) )
                    break

        self.app.refreshFrame()

    def Cmd_File_Revert( self, all_rows ):
        if not self.app.confirmAction( 'Revert', self.getStatusAndFilenames( all_rows ) ):
            return

        try:
            for filename in [self.getFilename( row ) for row in all_rows]:
                self.project_info.client_fg.revert( filename )
        except pysvn.ClientError, e:
            self.app.log_client_error( e )
        self.app.refreshFrame()

    def Cmd_File_Resolved( self, all_rows ):
        if not self.app.confirmAction( 'Resolved', self.getStatusAndFilenames( all_rows ) ):
            return

        try:
            for filename in [self.getFilename( row ) for row in all_rows]:
                self.project_info.client_fg.resolved( filename )
        except pysvn.ClientError, e:
            self.app.log_client_error( e )
        self.app.refreshFrame()

    # Cmd_File_Unlock= from SubversionListHandlerCommon
    def Cmd_File_Update( self, all_rows ):
        self.app.setProgress( 'Updated %(count)d', 0 )

        for filename in [self.getFilename( row ) for row in all_rows]:
            self.app.setAction( 'Update %s...' % filename )

            yield self.app.backgroundProcess

            ok = False
            try:
                rev_list = self.project_info.client_bg.update( filename, recurse=False )
                ok = True
            except pysvn.ClientError, e:
                self.app.log_client_error( e )

            yield self.app.foregroundProcess

            if not ok:
                break

            for rev in rev_list:
                if rev.number > 0:
                    basename = os.path.basename( filename )
                    count = self.app.getProgressValue( 'count' )
                    if count == 0:
                        self.app.log.info( 'Updated %s to revision %d, no new updates' % (basename, rev.number) )
                    elif count == 1:
                        self.app.log.info( 'Updated %s to revision %d, 1 new update' % (basename, rev.number) )
                    else:
                        self.app.log.info( 'Updated %s to revision %d, %d new updates' % (basename, rev.number, count) )

        self.app.clearProgress()
        self.app.setAction( 'Ready' )
        self.app.refreshFrame()
