import os, sys, re
import ConfigParser
from distutils.ccompiler import show_compilers
from distutils.core import Command, DEBUG
from distutils.errors import DistutilsOptionError, DistutilsPlatformError
from distutils import util, sysconfig

import Util

def get_python_lib(prefix):
    libdir = sysconfig.get_python_lib(prefix=prefix)
    if libdir not in sys.path:
        libdir = sysconfig.get_python_lib()
    return libdir

CONFIG_SCHEMES = {
    'fhs_local' : {
        'pythonlibdir'  : get_python_lib('/usr/local'),
        'bindir'        : '/usr/local/bin',
        'datadir'       : '/usr/local/share/$name',
        'sysconfdir'    : '/usr/local/etc/$name',
        'localstatedir' : '/var/local/lib/$name',
        'libdir'        : '/usr/local/lib/$name',
        'docdir'        : '/usr/local/share/doc/$fullname',
        'localedir'     : '/usr/local/share/locale',
    },
    'fhs_system' : {
        'pythonlibdir'  : get_python_lib('/usr'),
        'bindir'        : '/usr/bin',
        'datadir'       : '/usr/share/$name',
        'sysconfdir'    : '/etc/$name',
        'localstatedir' : '/var/lib/$name',
        'libdir'        : '/usr/lib/$name',
        'docdir'        : '/usr/share/doc/$fullname',
        'localedir'     : '/usr/share/locale',
        },
    'posix_autoconf' : {
        'pythonlibdir'  : sysconfig.get_python_lib(),
        'bindir'        : '$exec_prefix/bin',
        'datadir'       : '$prefix/share/$name',
        'sysconfdir'    : '$prefix/etc/$name',
        'localstatedir' : '$exec_prefix/var/$name',
        'libdir'        : '$exec_prefix/lib/$name',
        'docdir'        : '$prefix/share/doc/$fullname',
        'localedir'     : '$datadir/locale',
        },
    'posix_home' : {
        'pythonlibdir'  : '$prefix/lib/python$python_version',
        'bindir'        : '$prefix/bin',
        'datadir'       : '$prefix/share/$name',
        'sysconfdir'    : '$prefix/share/etc/$name',
        'localstatedir' : '$prefix/share/var/$name',
        'libdir'        : '$prefix/lib/$name',
        'docdir'        : '$prefix/share/doc/$fullname',
        'localedir'     : '$prefix/share/locale',
        },
    'nt' : {
        'pythonlibdir'  : '$prefix/Lib/site-packages',
        'bindir'        : '$prefix/Scripts',
        'datadir'       : '$prefix/Share/$name',
        'sysconfdir'    : '$prefix/Share/Settings/$name',
        'localstatedir' : '$prefix/Share/$name',
        'libdir'        : '$prefix/Share/$name',
        'docdir'        : '$prefix/Share/Doc/$fullname',
        'localedir'     : '$prefix/Share/Locale',
        },
    'other' : {
        'pythonlibdir'  : '$prefix/Lib/site-packages',
        'bindir'        : '$prefix/Scripts',
        'datadir'       : '$prefix/Share/$name',
        'sysconfdir'    : '$prefix/Share/Settings/$name',
        'localstatedir' : '$prefix/Share/$name',
        'libdir'        : '$prefix/Share/$name',
        'docdir'        : '$prefix/Share/Doc/$fullname',
        'localedir'     : '$prefix/Share/Locale',
        },
    }

if sys.version < '2.2':
    CONFIG_SCHEMES['nt']['pythonlibdir'] = '$prefix'

CACHE_FILENAME = 'config.cache'

CONFIG_KEYS = ('prefix', 'exec_prefix', 'pythonlibdir', 'bindir', 'datadir',
               'sysconfdir', 'localstatedir', 'libdir', 'docdir', 'localedir',
               'compiler', 'debug')

EXPORTED_CONFIG_KEYS = ('name', 'version', 'fullname', 'url',
                        'bindir', 'datadir', 'sysconfdir', 'localstatedir',
                        'libdir', 'docdir', 'localedir')
    
class Config(Command):

    command_name = 'config'

    description = 'select installation scheme and base directories'

    user_options = [
        # Select installation scheme and set base directories
        ('local', None,
         '(POSIX only) Use FHS /usr/local installation scheme'),
        ('system', None,
         '(POSIX only) Use FHS /usr installation scheme (e.g. for RPM builds)'),
        ('prefix=', None,
         'Use POSIX autoconf-style or Windows installation scheme with this prefix'),
        ('exec-prefix=', None,
         '(POSIX only) Prefix for platform-specific files when using autoconf-style scheme'),
        ('home=', None,
         '(POSIX only) Use home directory installation scheme with this prefix'),

        # Fine-tuning of directories
        ('pythonlibdir=', None,
         'Directory for 3rd-party Python libraries (site-packages)'),
        ('bindir=', None,
         'Directory for user executables'),
        ('datadir=', None,
         'Directory for read-only platform-independent data'),
        ('sysconfdir=', None,
         'Directory for read-only host-specific data'),
        ('localstatedir=', None,
         'Directory for modifiable host-specific data'),
        ('libdir=',None,
         'Directory for program & package libraries'),
        ('docdir=', None,
         'Directory for documentation files'),
        ('localedir=', None,
         'Directory for message catalogs'),

        # Compilation options
        ('compiler=', 'c',
         "specify the compiler type"),
        ('debug', 'g',
         "compile extensions and libraries with debugging information"),
        ]

    boolean_options = ['local', 'system', 'debug']

    help_options = [
        ('help-compiler', None,
         "list available compilers", show_compilers),
        ]

    def initialize_options(self):
        self.cache_filename = CACHE_FILENAME
        self.cache_section = Util.GetPlatformSpecifier()
        self.scheme = None
        self.config_vars = {}

        # User selectable Options
        self.local = None
        self.system = None
        self.home = None
        self.prefix = None
        self.exec_prefix = None

        self.pythonlibdir = None
        self.bindir = None
        self.datadir = None
        self.sysconfdir = None
        self.localstatedir = None
        self.libdir = None
        self.docdir = None
        self.localedir = None

        self.compiler = None
        self.debug = None
        return

    def finalize_options(self):
        if DEBUG: print "Config.finalize_options():"

        if os.name == 'posix':
            self.finalize_posix()
        else:
            self.finalize_other()

        # Read the configuration cache to fill in values not already set.
        parser = ConfigParser.ConfigParser()
        if os.path.exists(self.cache_filename):
            if DEBUG: print "  reading", self.cache_filename
            fp = open(self.cache_filename, 'rb')
            parser.readfp(fp)
            fp.close()

        # Use the cached configuration only if the platform specific section
        # already exists in the cache (duh!) and no options have been specified
        # by the user.
        changed = False
        if not (parser.has_section(self.cache_section) and
                not (self.local or self.system or self.home or
                     filter(self.__dict__.get, CONFIG_KEYS))):
            # Fill in the cache with values from the scheme and options.
            if not parser.has_section(self.cache_section):
                parser.add_section(self.cache_section)

            # string values
            for key in ('prefix', 'exec_prefix', 'compiler'):
                value = getattr(self, key) or ''
                parser.set(self.cache_section, key, value)

            # boolean values
            for key in ('debug',):
                value = str(getattr(self, key))
                parser.set(self.cache_section, key, value)

            # Set the scheme directories from the options, or the default
            # value if not specified.
            scheme = CONFIG_SCHEMES[self.scheme]
            for key in scheme.keys():
                value = getattr(self, key)
                if value is None:
                    value = scheme[key]
                parser.set(self.cache_section, key, value)
            changed = True
        elif DEBUG:
            print '  using cached options'

        # string values
        for key in ('prefix', 'exec_prefix', 'compiler'):
            if parser.has_option(self.cache_section, key):
                value = parser.get(self.cache_section, key) or None
                setattr(self, key, value)
            else:
                value = getattr(self, key) or ''
                parser.set(self.cache_section, key, value)
                changed = True

        # boolean values
        for key in ('debug',):
            if parser.has_option(self.cache_section, key):
                value = eval(parser.get(self.cache_section, key))
                setattr(self, key, value)
            else:
                value = str(getattr(self, key))
                parser.set(self.cache_section, key, value)
                changed = True

        # directory scheme
        scheme = CONFIG_SCHEMES[self.scheme]
        for key in scheme.keys():
            if parser.has_option(self.cache_section, key):
                value = parser.get(self.cache_section, key) or None
                setattr(self, key, value)
            else:
                value = CONFIG_SCHEMES[self.scheme][key]
                parser.set(self.cache_section, key, value)
                changed = True

        # Save this configuration for next run
        if DEBUG:
            print "  writing", self.cache_filename
            print '    [%s]' % self.cache_section
            for name in CONFIG_KEYS:
                print '   ', name, '=', parser.get(self.cache_section, name)

        if changed:
            fp = open(self.cache_filename, 'wb')
            parser.write(fp)
            fp.close()

        if self.prefix is None:
            self.prefix = sys.prefix

        return


    def finalize_posix(self):
        # Only one of local/system/home/[prefix|exec-prefix] may be given
        # The easiest determine this is to just count the Nones
        specified_count = ((self.local is not None) +
                           (self.system is not None) +
                           (self.home is not None) +
                           ((self.prefix or self.exec_prefix) is not None))
        if specified_count > 1:
            raise DistutilsOptionError("must use only one of --local, --system,"
                                       " --home or --prefix/exec-prefix")

        if self.local or not specified_count:
            self.scheme = 'fhs_local'
        elif self.system:
            self.scheme = 'fhs_system'
        elif self.home:
            self.prefix = self.exec_prefix = os.path.expanduser(self.home)
            self.scheme = 'posix_home'
        else:
            # Either self.prefix or self.exec_prefix is specified
            if self.exec_prefix is None:
                self.exec_prefix = self.prefix
            elif self.prefix is None:
                raise DistutilsOptionError, \
                      "must not supply exec-prefix without prefix"
            self.prefix = os.path.expanduser(self.prefix)
            self.exec_prefix = os.path.expanduser(self.exec_prefix)
            self.scheme = 'posix_autoconf'
        return

    def finalize_other(self):
        if self.local:
            self.warn("'--local' option ignored on this platform")
            self.local = None
        if self.system:
            self.warn("'--system' option ignored on this platform")
            self.system = None
        if self.exec_prefix:
            self.warn("'--exec-prefix' option ignored on this platform")
            self.exec_prefix = None
        if self.home:
            self.warn("'--home' option ignored on this platform")
            self.home = None

        if os.name == 'nt':
            self.scheme = 'nt'
        else:
            self.scheme = 'other'
        return

    def run(self):
        if DEBUG:
            print "Config.run():"
            for name in CONFIG_KEYS:
                print '   ', name, '=', getattr(self, name)
        return

    def get_config_vars(self):
        config_vars = {'name' : self.distribution.get_name(),
                       'version' : self.distribution.get_version(),
                       'fullname' : self.distribution.get_fullname(),
                       'url' : self.distribution.get_url(),
                       'python_version' : sys.version[:3],
                       }
        for key in CONFIG_KEYS:
            value = getattr(self, key)
            # Distutils prior to Python 2.3 cannot handle empty strings
            # in the util.* functions.
            if isinstance(value, str) and value:
                if os.name == 'posix':
                    value = os.path.expanduser(value)
                value = util.convert_path(value)
                value = util.subst_vars(value, config_vars)
            config_vars[key] = value
        return config_vars

    def write_config_vars(self, filename):
        # Create the configuration filename
        self.announce('creating ' + filename)
        config_vars = self.get_config_vars()

        f = open(filename, 'w')
        f.write('# Configuration variables\n')
        maxlen = max(map(len, EXPORTED_CONFIG_KEYS))
        for name in EXPORTED_CONFIG_KEYS:
            value = config_vars[name]
            f.write('%-*s = %r\n' % (maxlen, name.upper(), value))
        f.close()
        return
