import os
from distutils.core import Command
from distutils.file_util import write_file

import string


class bdist_inno(Command):

    """Command to create a windows installer with Inno Setup"""

    description = "Create a windows installer with Inno Setup"

    user_options = [
        ('skip-build', None, "skip the build steps"),
        ('bdist-dir=', None,
         "temporary directory for creating the distribution"),
        ('run-inno', None,
         "Run inno-setup to create the installer. On by default on nt"),
        ('iss-name', None,
         "The name of the iss file to generate. "
         "Shouldn't contain directories"),

        # Parameters for the Inno Setup script
        ('copyright', None, "Copyright notice for the Inno Setup file"),
        ('default-dir-name', None,
         "Default installation directory. Defaults to '{pf}\\<name>'"),
        ('default-group-name', None,
         "Default program group name. Defaults to <name>'"),
        ("license-file", None, "File containing the license."),
        ("output-basename", None,
         "Base filename for the Inno Setup output "
         "(defaults to <name>-<version>-<issrevision>)."),
        ("iss-revision", None,
         "revision of the generated installer of the package version"),

        ("icons-entries", None,
         "List if InnoIconItems "
         "(this can only be set from inside the setup.py script)"),
        ]

    boolean_options = ["do-symlink"]

    def initialize_options(self):
        self.skip_build = 0
        self.bdist_dir = None
        self.run_inno = None
        self.iss_name = None
        self.copyright = ""
        self.default_dir_name = None
        self.default_group_name = None
        self.license_file = None
        self.output_basename = None
        self.iss_revision = None
        self.icons_entries = []

    def finalize_options(self):
        self.set_undefined_options("install",
                                   ('skip_build', 'skip_build'))
        if self.bdist_dir is None:
            bdist_base = self.get_finalized_command('bdist').bdist_base
            self.bdist_dir = os.path.join(bdist_base, 'inno')

        if self.run_inno is None:
            self.run_inno = os.name == "nt"

        name = self.distribution.get_name()
        if self.iss_name is None:
            self.iss_name = name + '.iss'

        if self.default_dir_name is None:
            self.default_dir_name = "{pf}\\" + name
        if self.default_group_name is None:
            self.default_group_name = name

        if self.iss_revision is None:
            self.iss_revision = 0
        if self.output_basename is None:
            self.output_basename = "%s-%s-%d" \
                                   % (name, self.distribution.get_version(),
                                      self.iss_revision)

    def run(self, install_options = None):
        """Execute the command. install_options if given, should be a
        directory of additional options to set in the install step"""
        # Obviously have to build before we can install
        if not self.skip_build:
            self.run_command('build')

        # Install in a temporary directory
        install = self.reinitialize_command('install')
        install.root = self.bdist_dir
        if install_options is not None:
            for key, value in install_options.items():
                setattr(install, key, value)
        if os.name != 'nt':
            # Must force install to use the 'nt' scheme;
            install.select_scheme('nt')

        self.announce("installing to %s" % self.bdist_dir)
        install.ensure_finalized()
        install.run()

        # Create the iss file
        iss_file = os.path.join(self.bdist_dir, self.iss_name)
        self.execute(write_file, (iss_file, self.generate_iss()),
                     "Create Inno Setup script file %s" % iss_file)

        # and invoke 
        if self.run_inno:
            self.spawn(["iscc", iss_file])

    def generate_iss(self):
        """Return the contents of the iss file as list of strings, one
        string per line"""

        # first, turn the icons entries into a more usable form
        icons = {}
        for item in self.icons_entries:
            icons[item.filename] = item

        iss = []

        name = self.distribution.get_name()
        iss.extend(["[Setup]",
                    "AppName=" + name,
                    "AppVerName=" + name + " "+self.distribution.get_version(),
                    "DefaultDirName=" + self.default_dir_name,
                    "DefaultGroupName=" + self.default_group_name,
                    ])
        if self.copyright:
            iss.append("AppCopyright=" + self.copyright)
        if self.license_file:
            iss.append("LicenseFile=" + self.license_file)

        iss.append("OutputBasefilename=" + self.output_basename)

        iss.append("")
        iss.append("[Files]")

        install = self.get_finalized_command("install")
        install_scripts = self.get_finalized_command("install_scripts")
        script_files = install_scripts.get_outputs()
        prefixlen = len(self.bdist_dir) + len(os.sep)
        for filename in install.get_outputs():
            filename = filename[prefixlen:]
            icon = icons.get(filename)
            dirname = os.path.dirname(filename)
            if os.name != "nt":
                # change the separators to \ on non-windos systems
                filename = string.join(string.split(filename, os.sep), "\\")
                dirname =  string.join(string.split(dirname, os.sep), "\\")
            line = 'Source: "%s"' % filename
            if icon is not None:
                # install it as defined in the icon object
                backslash = string.rfind(icon.install_name, "\\")
                if backslash >= 0:
                    dirname = icon.install_name[:backslash]
                    basename = icon.install_name[backslash + 1:]
                else:
                    dirname = ""
                    basename = icon.install_name
                line = '%s; DestDir: "%s"; DestName: "%s"' % (line, dirname,
                                                              basename)
            else:
                line = line + '; DestDir: "{app}\\%s"' % (dirname)
            iss.append(line)

        iss.append("")
        iss.append("[Icons]")
        for icon in self.icons_entries:
            line = 'Name: "{group}\\%s"; Filename: "%s";' \
                   % (icon.title, icon.install_name)
            iss.append(line)
            
        return iss


class InnoIconItem:

    """Describe one item for the start menu for the Inno Setup installer"""

    def __init__(self, filename, title, install_name = None):
        self.filename = filename
        self.title = title
        if install_name is not None:
            self.install_name = install_name
        else:
            self.install_name = filename

