'''

 ====================================================================
 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_preferences.py

'''
import os
import wx
import ConfigParser
import wb_platform_specific
import wb_source_control_providers
import string

class Preferences:
    def __init__( self, app ):
        self.app = app
        self.pref_filename = wb_platform_specific.getPreferencesFilename()

        self.app.log.info( 'Reading preferences from %s' % self.pref_filename )

        # all the preference section handles get created here
        self.pref_handlers = {}
        self.pref_handlers['Projects'] = ProjectsPreferences( self.app )
        self.pref_handlers['Bookmarks'] = BookmarksPreferences( self.app )
        self.pref_handlers['Window'] = WindowPreferences( self.app )
        self.pref_handlers['DiffWindow'] = DiffWindowPreferences( self.app )
        self.pref_handlers['View'] = ViewPreferences( self.app )
        self.pref_handlers['Editor'] = EditorPreferences( self.app )
        self.pref_handlers['Shell'] = ShellPreferences( self.app )
        self.pref_handlers['DiffTool'] = DiffToolPreferences( self.app )

        # read preferences into the handlers
        self.readPreferences()

    def readPreferences( self ):
        self.pref_data = ConfigParser.RawConfigParser()
        self.pref_data.read( self.pref_filename )

        for handler in self.pref_handlers.values():
            if self.pref_data.has_section( handler.section_name ):
                try:
                    handler.readPreferences( self.pref_data )
                except ConfigParser.Error:
                    self.app.log.error( 'Preferences.readPreferences()', exc_info=1 )

    def __getattr__( self, name ):
        # support getProjects(), getFoobars() etc.
        if name[0:3] == 'get':
            section_name = name[3:]
            if self.pref_handlers.has_key( section_name ):
                return self.pref_handlers[ section_name ]

        raise AttributeError, '%s has no attribute %s' % (self.__class__.__name__, name )


    def writePreferences( self ):
        try:
            for handler in self.pref_handlers.values():
                self.pref_data.remove_section( handler.section_name )
                self.pref_data.add_section( handler.section_name )
                handler.writePreferences( self.pref_data )

            self.pref_data.write( open( self.pref_filename, 'w' ) )
            self.app.log.info( 'Wrote preferences to %s' % self.pref_filename )

        except IOError, e:
            self.app.log.error( 'write preferences: %s' % e )

class PreferenceSection:
    def __init__( self, section_name ):
        self.section_name = section_name

    def readPreferences( self, pref_data ):
        pass

    def writePreferences( self, pref_data ):
        pass

    # support being returned by the __getattr__ above
    def __call__( self ):
        return self

class GetOption:
    def __init__( self, pref_data, section_name ):
        self.pref_data = pref_data
        self.section_name = section_name

    def has( self, name ):
        return self.pref_data.has_option( self.section_name, name )

    def getstr( self, name ):
        return self.pref_data.get( self.section_name, name ).strip()

    def getint( self, name ):
        return self.pref_data.getint( self.section_name, name )

    def getfloat( self, name ):
        return self.pref_data.getfloat( self.section_name, name )

    def getbool( self, name ):
        return self.pref_data.getboolean( self.section_name, name )

    def getstrlist( self, name, sep ):
        s = self.getstr( name )
        if len(s) == 0:
            return []
        return [p.strip() for p in s.split( sep )]

class SetOption:
    def __init__( self, pref_data, section_name ):
        self.pref_data = pref_data
        self.section_name = section_name

    def set( self, name, value, sep='' ):
        if type(value) == type([]):
            value = string.join( value, sep )

        self.pref_data.set( self.section_name, name, value )

class GetIndexedOption(GetOption):
    def __init__( self, pref_data, section_name, index ):
        GetOption.__init__( self, pref_data, section_name )
        self.index = index + 1

    def has( self, name ):
        return self.pref_data.has_option( self.section_name, '%s_%d' % (name, self.index) )

    def getstr( self, name ):
        return self.pref_data.get( self.section_name, '%s_%d' % (name, self.index) ).strip()

    def getint( self, name ):
        return self.pref_data.getint( self.section_name, '%s_%d' % (name, self.index) )

    def getfloat( self, name ):
        return self.pref_data.getfloat( self.section_name, '%s_%d' % (name, self.index) )

    def getbool( self, name ):
        return self.pref_data.getboolean( self.section_name, '%s_%d' % (name, self.index) )

class SetIndexedOption:
    def __init__( self, pref_data, section_name, index ):
        self.pref_data = pref_data
        self.section_name = section_name
        self.index = index + 1

    def set( self, name, value ):
        self.pref_data.set( self.section_name, '%s_%d' % (name, self.index), value )

class ProjectsPreferences(PreferenceSection):
    def __init__( self, app ):
        PreferenceSection.__init__( self, 'Projects' )
        self.app = app

        self.all_projects = {}

    def readPreferences( self, pref_data ):
        if not pref_data.has_section( self.section_name ):
            return

        num_projects = pref_data.getint( self.section_name, 'num_projects' )
        for index in range( num_projects ):
            get_option = GetIndexedOption( pref_data, self.section_name, index )
            provider = get_option.getstr( 'provider' )

            if wb_source_control_providers.hasProvider( provider ):
                provider = wb_source_control_providers.getProvider( provider )
                pi = provider.getProjectInfo( self.app )
                pi.readPreferences( get_option )
                self.all_projects[ pi.project_name ] = pi

    def writePreferences( self, pref_data ):
        pref_data.remove_section( self.section_name )
        pref_data.add_section( self.section_name )
        pref_data.set( self.section_name, 'num_projects', len( self.all_projects ) )
        for index, pi in enumerate( self.all_projects.values() ):
            pi.writePreferences( SetIndexedOption( pref_data, self.section_name, index ) )

    def _by_project_name( self, a, b ):
        return cmp( a.project_name, b.project_name )

    def getProjectList( self ):
        pl = self.all_projects.values()
        pl.sort( self._by_project_name )
        return pl

    def addProject( self, pi ):
        self.all_projects[ pi.project_name ] = pi

    def delProject( self, pi ):
        del self.all_projects[ pi.project_name ]

class BookmarksPreferences(PreferenceSection):
    def __init__( self, app ):
        PreferenceSection.__init__( self, 'Bookmarks' )
        self.app = app

        self.all_bookmarks = {}
        # no longer support menu style -
        # self.menu_style = 'leaf_only'
        # self.leaf_names_to_ignore = ['main','source','src', 'inc','include']
        self.menu_style = None
        self.leaf_names_to_ignore = []

    def readPreferences( self, pref_data ):
        if not pref_data.has_section( self.section_name ):
            return

        get_option = GetOption( pref_data, self.section_name )
        # look for menu_style and leaf_names_to_ignore to
        # allow for preference file update
        if get_option.has( 'menu_style' ):
            self.menu_style = get_option.getstr( 'menu_style' )

        if get_option.has( 'leaf_names_to_ignore' ):
            self.leaf_names_to_ignore = get_option.getstrlist( 'leaf_names_to_ignore', ',' )

        num_bookmarks = get_option.getint( 'num_bookmarks' )
        for index in range( num_bookmarks ):
            get_option = GetIndexedOption( pref_data, self.section_name, index )
            bookmark_name = get_option.getstr( 'bookmark_name' )

            provider = get_option.getstr( 'provider' )
            if wb_source_control_providers.hasProvider( provider ):
                provider = wb_source_control_providers.getProvider( provider )
                pi = provider.getProjectInfo( self.app )
                pi.readPreferences( get_option )
                # default the menu name if required
                if pi.menu_name is None:
                    # see if update required
                    if self.menu_style is not None:
                        # default the name as if used to be done
                        pi.menu_name = self.__getMenuName( pi.wc_path )
                    else:
                        self.__defaultMenuName( pi )

                self.all_bookmarks[ bookmark_name ] = pi

    def writePreferences( self, pref_data ):
        pref_data.remove_section( self.section_name )
        pref_data.add_section( self.section_name )

        set_option = SetOption( pref_data, self.section_name )
        #set_option.set( 'menu_style', self.menu_style )
        #set_option.set( 'leaf_names_to_ignore', self.leaf_names_to_ignore, ',' )

        set_option.set( 'num_bookmarks', len( self.all_bookmarks ) )
        for index, bookmark_name in enumerate( self.all_bookmarks.keys() ):
            set_option = SetIndexedOption( pref_data, self.section_name, index )
            set_option.set( 'bookmark_name', bookmark_name )
            pi = self.all_bookmarks[ bookmark_name ]
            pi.writePreferences( set_option )

    def addBookmark( self, pi, name=None ):
        if name is None:
            name = pi.wc_path
        self.all_bookmarks[ name ] = pi
        self.__defaultMenuName( pi )

    def delBookmark( self, bookmark_name ):
        del self.all_bookmarks[ bookmark_name ]

    def delAllBookmarks( self, name ):
        self.all_bookmarks = {}

    def getBookmarkNames( self ):
        names = self.all_bookmarks.keys()
        names.sort()
        return names

    def __defaultMenuName( self, pi ):
        if os.environ.has_key( 'HOME' ):
            home_dir = os.environ[ 'HOME' ] + '/'
            if pi.wc_path.startswith( home_dir ):
                pi.menu_name = pi.wc_path[len(home_dir):]
            else:
                pi.menu_name = pi.wc_path
        else:
            pi.menu_name = pi.wc_path

    # only used to update pref file now
    def __getMenuName( self, wc_path, menu_style=None, leaf_names_to_ignore=None ):
        if menu_style is None:
            menu_style = self.menu_style
        if leaf_names_to_ignore is None:
            leaf_names_to_ignore = self.leaf_names_to_ignore

        if menu_style in ['leaf_in_parent', 'leaf_only']:
            path_parts = wc_path.split( os.path.sep )
            for leaf_index in range( len(path_parts)-1, 1, -1 ):
                if path_parts[ leaf_index ].lower() not in leaf_names_to_ignore:
                    if menu_style == 'leaf_in_parent':
                        return '%s   in %s' % (os.path.sep.join( path_parts[leaf_index:] )
                                              ,os.path.sep.join( path_parts[:leaf_index] ))
                    else:
                        return os.path.sep.join( path_parts[leaf_index:] )

            # cannot reach here?
            return wc_path

        else:
            return wc_path

    def hasBookmark( self, name ):
        return self.all_bookmarks.has_key( name )

    def getBookmark( self, name ):
        return self.all_bookmarks[ name ]


class WindowPreferences(PreferenceSection):
    def __init__( self, app ):
        PreferenceSection.__init__( self, 'Window' )
        self.app = app

        self.h_sash_ratio = 0.7
        self.v_sash_ratio = 0.2

        self.frame_size = wx.Size( 700, 500 )
        self.frame_position = wx.DefaultPosition
        self.maximized = False

    def readPreferences( self, pref_data ):
        get_option = GetOption( pref_data, self.section_name )
        x = get_option.getint( 'pos_x' )
        if x < 0:
            x = 0
        y = get_option.getint( 'pos_y' )
        if y < 0:
            y = 0
        self.frame_position = wx.Point( x, y )

        w = get_option.getint( 'width' )
        h = get_option.getint( 'height' )
        self.frame_size = wx.Size( w, h )

        self.maximized = get_option.getbool( 'maximized' )

    def writePreferences( self, pref_data ):
        set_option = SetOption( pref_data, self.section_name )

        set_option.set( 'pos_x', self.frame_position.x )
        set_option.set( 'pos_y', self.frame_position.y )
        set_option.set( 'width', self.frame_size.GetWidth() )
        set_option.set( 'height', self.frame_size.GetHeight() )
        set_option.set( 'maximized', self.maximized )

class DiffWindowPreferences(PreferenceSection):
    def __init__( self, app ):
        PreferenceSection.__init__( self, 'DiffWindow' )
        self.app = app

        self.frame_size = wx.Size( 700, 500 )
        self.frame_position = wx.DefaultPosition
        self.maximized = False

    def readPreferences( self, pref_data ):
        get_option = GetOption( pref_data, self.section_name )
        x = get_option.getint( 'pos_x' )
        if x < 0:
            x = 0
        y = get_option.getint( 'pos_y' )
        if y < 0:
            y = 0
        self.frame_position = wx.Point( x, y )

        w = get_option.getint( 'width' )
        h = get_option.getint( 'height' )
        self.frame_size = wx.Size( w, h )

        self.maximized = get_option.getbool( 'maximized' )


    def writePreferences( self, pref_data ):
        set_option = SetOption( pref_data, self.section_name )

        set_option.set( 'pos_x', self.frame_position.x )
        set_option.set( 'pos_y', self.frame_position.y )
        set_option.set( 'width', self.frame_size.GetWidth() )
        set_option.set( 'height', self.frame_size.GetHeight() )
        set_option.set( 'maximized', self.maximized )

class ViewPreferences(PreferenceSection):
    def __init__( self, app ):
        PreferenceSection.__init__( self, 'View' )
        self.app = app

        self.auto_refresh = True
        self.sort_order = 1
        self.sort_field = 'Name'
        self.view_ignored = False
        self.view_controlled = True
        self.view_uncontrolled = True
        self.view_recursive = False
        self.column_order = ['Name','State','Date','Rev','Author','Type']
        self.column_widths = ['25','4','14','4','10','4']

    def readPreferences( self, pref_data ):
        get_option = GetOption( pref_data, self.section_name )

        if get_option.has( 'auto_refresh' ):
            self.auto_refresh = get_option.getbool( 'auto_refresh' )
        if get_option.has( 'sort_order' ):
            self.sort_order = get_option.getint( 'sort_order' )
        if get_option.has( 'sort_field' ):
            self.sort_field = get_option.getstr( 'sort_field' )
        if get_option.has( 'view_ignored' ):
            self.view_ignored = get_option.getbool( 'view_ignored' )
        # always view controlled on startup
        self.view_controlled = True
        self.view_uncontrolled = True
        if get_option.has( 'column_order' ):
            self.column_order = get_option.getstrlist( 'column_order', ',' )
        if get_option.has( 'column_widths' ):
            self.column_widths = get_option.getstrlist( 'column_widths', ',' )
        if self.sort_field not in self.column_order:
            self.sort_field = 'Name'

    def writePreferences( self, pref_data ):
        set_option = SetOption( pref_data, self.section_name )

        set_option.set( 'auto_refresh', self.auto_refresh )
        set_option.set( 'sort_order', self.sort_order )
        set_option.set( 'sort_field', self.sort_field )
        set_option.set( 'view_ignored', self.view_ignored )
        # don't need to save view_controlled or view_uncontrolled as we force it on at reload
        set_option.set( 'column_order', self.column_order, ',' )
        set_option.set( 'column_widths', self.column_widths, ',' )

class EditorPreferences(PreferenceSection):
    def __init__( self, app ):
        PreferenceSection.__init__( self, 'Editor' )
        self.app = app

        self.editor_image = ''
        self.editor_options = ''

    def readPreferences( self, pref_data ):
        get_option = GetOption( pref_data, self.section_name )

        if get_option.has( 'editor' ):
            self.editor_image = get_option.getstr( 'editor' )
        if get_option.has( 'editor_options' ):
            self.editor_options = get_option.getstr( 'editor_options' )

    def writePreferences( self, pref_data ):
        set_option = SetOption( pref_data, self.section_name )

        if self.editor_image:
            set_option.set( 'editor', self.editor_image )
        if self.editor_options:
            set_option.set( 'editor_options', self.editor_options )

class ShellPreferences(PreferenceSection):
    def __init__( self, app ):
        PreferenceSection.__init__( self, 'Shell' )
        self.app = app

        self.shell_init_command = ''
        self.shell_terminal = ''
        self.shell_file_browser = ''

    def readPreferences( self, pref_data ):
        get_option = GetOption( pref_data, self.section_name )

        if get_option.has( 'init_command' ):
            self.shell_init_command = get_option.getstr( 'init_command' )
        if get_option.has( 'terminal' ):
            self.shell_terminal = get_option.getstr( 'terminal' )
        if get_option.has( 'file_browser' ):
            self.shell_file_browser = get_option.getstr( 'file_browser' )

    def writePreferences( self, pref_data ):
        set_option = SetOption( pref_data, self.section_name )

        if self.shell_init_command:
            set_option.set( 'init_command', self.shell_init_command )
        if self.shell_terminal:
            set_option.set( 'terminal', self.shell_terminal )
        if self.shell_file_browser:
            set_option.set( 'file_browser', self.shell_file_browser )

class DiffToolPreferences(PreferenceSection):
    def __init__( self, app ):
        PreferenceSection.__init__( self, 'DiffTool' )
        self.app = app

        self.diff_tool_mode = 'built-in'
        self.gui_diff_tool = ''
        self.shell_diff_tool = ''
        self.gui_diff_tool_options = ''
        self.shell_diff_tool_options = ''

    def readPreferences( self, pref_data ):
        get_option = GetOption( pref_data, self.section_name )

        if get_option.has( 'diff_tool_mode' ):
            self.diff_tool_mode = get_option.getstr( 'diff_tool_mode' )
        if get_option.has( 'diff_tool' ):
            self.gui_diff_tool = get_option.getstr( 'diff_tool' )
        if get_option.has( 'shell_diff_tool' ):
            self.shell_diff_tool = get_option.getstr( 'shell_diff_tool' )
        if get_option.has( 'diff_tool_options' ):
            self.gui_diff_tool_options = get_option.getstr( 'diff_tool_options' )
        if get_option.has( 'shell_diff_tool_options' ):
            self.shell_diff_tool_options = get_option.getstr( 'shell_diff_tool_options' )

    def writePreferences( self, pref_data ):
        set_option = SetOption( pref_data, self.section_name )

        if self.diff_tool_mode != 'built-in':
            set_option.set( 'diff_tool_mode', self.diff_tool_mode )
        if self.gui_diff_tool != '':
            set_option.set( 'diff_tool', self.gui_diff_tool )
        if self.shell_diff_tool != '':
            set_option.set( 'shell_diff_tool', self.shell_diff_tool )
        if self.gui_diff_tool_options != '':
            set_option.set( 'diff_tool_options', self.gui_diff_tool_options )
        if self.shell_diff_tool_options != '':
            set_option.set( 'shell_diff_tool_options', self.shell_diff_tool_options )
