#!/usr/bin/python

import os
import sys
from distutils import log
from distutils.command.build import build as _build
from distutils.command.sdist import sdist
from distutils.command.build_py import build_py
from distutils.core import setup
from distutils.dep_util import newer_group
from distutils import dir_util
from distutils.spawn import spawn

# can't import telepathy._version because that imports telepathy,
# which needs telepathy._generated, which we haven't yet built...
loc = {}
execfile(os.path.join(os.curdir, 'src', '_version.py'),
         globals(), loc)
__version__ = loc['__version__']


def ls_spec_xml():
    names = os.listdir(os.path.join(os.curdir, 'spec'))
    return [os.path.join(os.curdir, 'spec', name) for name in names]

def ls_spec_basenames():
    names = os.listdir(os.path.join(os.curdir, 'spec'))
    for name in names:
        assert name.endswith('.xml')
    # we only want the interfaces, which start with a capital letter
    return [name[:-4] for name in names if name[0].upper() == name[0]]


XSLTPROC = ['xsltproc', '--nonet', '--novalid', '--xinclude']
ALL_SPEC_XML = ls_spec_xml()
SPEC_BASENAMES = ls_spec_basenames()

class build_empty_py(build_py):
    def finalize_options(self):
        build_py.finalize_options(self)
        self.packages = []
        self.py_modules = ['telepathy._generated']
        self.package_data = []
        self.data_files = []

    def find_modules(self):
        return [('telepathy._generated', '__init__', '')]

    def build_module(self, module, module_file, package):
        # "module_file" is actually the empty string
        package = package.split('.')
        outfile = self.get_module_outfile(self.build_lib, package, module)
        dir = os.path.dirname(outfile)
        log.info('Generating empty %s' % outfile)
        self.mkpath(dir)
        if not self.dry_run:
            f = file(outfile, 'w')
            f.write('# Placeholder for package')
            f.close()

class build_gen_py_ifaces(build_py):
    def finalize_options(self):
        build_py.finalize_options(self)
        self.packages = []
        self.py_modules = ['telepathy._generated.%s' % name
                           for name in SPEC_BASENAMES]
        self.package_data = []
        self.data_files = []
        self.stylesheet = os.path.join(os.curdir, 'tools',
                                       'spec-to-python.xsl')

    def find_modules(self):
        return [('telepathy._generated', name,
                 os.path.join(os.curdir, 'spec', '%s.xml' % name))
                for name in SPEC_BASENAMES]

    def build_module(self, module, module_file, package):
        # "module_file" is actually the XML
        package = package.split('.')
        outfile = self.get_module_outfile(self.build_lib, package, module)
        dir = os.path.dirname(outfile)
        self.mkpath(dir)
        return self.run_xsl(module_file, outfile)

    def run_xsl(self, xml, outfile):
        if newer_group([xml, self.stylesheet], outfile):
            command = XSLTPROC + ['-o', outfile,
                                  self.stylesheet,
                                  xml]
            log.info('Generating %s from %s using %s' % (outfile, xml,
                                                         self.stylesheet))
            spawn(command, dry_run=self.dry_run)


class build_gen_py(build_py):
    def finalize_options(self):
        build_py.finalize_options(self)
        self.packages = []
        self.py_modules = ['telepathy._generated.interfaces',
                           'telepathy._generated.errors',
                           'telepathy._generated.constants']
        self.package_data = []
        self.data_files = []

    def find_modules(self):
        return [('telepathy._generated', 'interfaces',
                 'tools/python-interfaces-generator.xsl'),
                ('telepathy._generated', 'constants',
                 'tools/python-constants-generator.xsl'),
                ('telepathy._generated', 'errors',
                 'tools/python-errors-generator.xsl')]

    def build_module(self, module, module_file, package):
        # "module_file" is actually the stylesheet
        package = package.split('.')
        outfile = self.get_module_outfile(self.build_lib, package, module)
        dir = os.path.dirname(outfile)
        self.mkpath(dir)
        return self.run_xsl(module_file, outfile)

    def run_xsl(self, stylesheet, outfile):
        if newer_group(ALL_SPEC_XML + [stylesheet], outfile):
            command = XSLTPROC + ['-o', outfile,
                                  stylesheet,
                                  os.path.join(os.curdir, 'spec', 'all.xml')]
            log.info('Generating %s using %s' % (outfile, stylesheet))
            spawn(command, dry_run=self.dry_run)


class build(_build):
    sub_commands = (_build.sub_commands
                    + [('build_gen_py', (lambda self: True)),
                       ('build_gen_py_ifaces', (lambda self: True)),
                       ('build_empty_py', (lambda self: True))])

class distcheck(sdist):

    def run(self):
        sdist.run(self)

        if self.dry_run:
            return

        base_dir = self.distribution.get_fullname()
        distcheck_dir = os.path.join(self.dist_dir, 'distcheck')
        self.mkpath(distcheck_dir)
        self.mkpath(os.path.join(distcheck_dir, 'again'))

        cwd = os.getcwd()
        os.chdir(distcheck_dir)

        if os.path.isdir(base_dir):
            dir_util.remove_tree(base_dir)

        for archive in self.archive_files:
            if archive.endswith('.tar.gz'):
                cmd = ['tar', '-xzf',
                       os.path.join(os.pardir, os.pardir, archive), base_dir]
                spawn(cmd)
                break
        else:
            raise ValueError('No supported archives were created')

        # make sdist again, then make clean
        os.chdir(cwd)
        os.chdir(os.path.join(distcheck_dir, base_dir))
        cmd = [sys.executable, 'setup.py', 'sdist', '--formats', 'gztar']
        spawn(cmd)

        # make sure the two tarballs have the same contents
        os.chdir(cwd)
        os.chdir(os.path.join(distcheck_dir, 'again'))
        cmd = ['tar', '-xzf',
               os.path.join(os.pardir, base_dir, 'dist', base_dir + '.tar.gz'),
               base_dir]
        spawn(cmd)

        os.chdir(cwd)
        os.chdir(os.path.join(distcheck_dir, base_dir))
        dir_util.remove_tree('dist')
        os.remove('MANIFEST')

        os.chdir(cwd)
        cmd = ['diff', '-ru',
               os.path.join(distcheck_dir, base_dir),
               os.path.join(distcheck_dir, 'again', base_dir)]
        spawn(cmd)

        # make sure the whole spec was included
        from xml.dom.minidom import parse
        dom = parse(os.path.join(distcheck_dir, base_dir, 'spec', 'all.xml'))
        inclusions = dom.getElementsByTagName('xi:include')
        for inclusion in inclusions:
            filename = inclusion.getAttribute('href')
            if not os.path.isfile(os.path.join(distcheck_dir, base_dir, 'spec',
                                               filename)):
                if not inclusion.getElementsByTagName('xi:fallback'):
                    raise AssertionError('%s was not packaged' % filename)
        dom.unlink()


setup(
    cmdclass={'build': build,
              'build_gen_py': build_gen_py,
              'build_empty_py': build_empty_py,
              'distcheck': distcheck,
              'build_gen_py_ifaces': build_gen_py_ifaces},
    name='telepathy-python',
    version=__version__,
    package_dir={'telepathy': 'src'},
    packages=[
        'telepathy',
        'telepathy.client',
        'telepathy.server'
        ],
    )

