# libgo - script to build library.gnome.org
# Copyright (C) 2007-2009  Frederic Peters
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
# 02110-1301  USA

import glob
import logging
import os
import re
import stat
import subprocess
import tarfile
import tempfile

try:
    import elementtree.ElementTree as ET
except ImportError:
    import xml.etree.ElementTree as ET

from base import DocModule


class GtkDocModule(DocModule):
    '''Class for documentation shipped in a tarball and using gtk-doc'''

    gtkdoc_xsl_file = os.path.join(data_dir, 'xslt', 'gtk-doc.xsl')
    html2html_xsl_file = os.path.join(data_dir, 'xslt', 'html2html.xsl')

    related_xsl_files = ['gtk-doc.xsl', 'heading.xsl']

    def setup_channel(self):
        self.channel = app.overlay.get_channel_overlay(self.modulename, 'devel')

    def __str__(self):
        return 'gtk-doc module at %s' % self.dirname

    def process(self):
        doc_module = self.modulename
        ext_dirname = os.path.join(app.config.private_dir, 'extracts')

        main_sgml_file = re.findall(r'DOC_MAIN_SGML_FILE\s?=\s?(.*)',
                self.makefile_am)[0].strip()
        main_sgml_file = main_sgml_file.replace('$(DOC_MODULE)', doc_module)

        try:
            html_images = re.findall('HTML_IMAGES\s+=\s+(.*)', self.makefile_am)[0].split()
        except IndexError:
            html_images = []
        html_images = [x.replace('$(srcdir)/', '') for x in html_images]

        web_output_dir = app.get_module_web_output_dir(self)
        if not os.path.exists(web_output_dir):
            os.makedirs(web_output_dir)

        if not app.rebuild_all and os.path.exists(
                os.path.join(web_output_dir, '%s.devhelp2' % doc_module)):
            mtime = os.stat(os.path.join(web_output_dir, '%s.devhelp2' % doc_module))[stat.ST_MTIME]
        else:
            mtime = 0

        if mtime > max(self.mtime_tarball, self.mtime_xslt_files):
            logging.debug('using already generated doc')
        else:
            logging.info('generating doc in %s' % web_output_dir[len(app.config.output_dir):])
            cmd = ['xsltproc', '--output', web_output_dir + '/',
                    '--nonet', '--xinclude',
                    '--stringparam', 'libgo.lang', 'en',
                    '--stringparam', 'gtkdoc.bookname', doc_module,
                    '--stringparam', 'gtkdoc.version', '"(~lgo)"',
                    '--stringparam', 'libgo.channel', self.channel,
                    self.gtkdoc_xsl_file,
                    os.path.join(ext_dirname, self.dirname, main_sgml_file)]

            if app.config.symbols_dbm_filepath:
                cmd.insert(-2, '--param')
                cmd.insert(-2, 'libgo.dbm_support')
                cmd.insert(-2, 'true()')

            logging.debug('executing %s' % ' '.join(cmd))
            xsltproc = subprocess.Popen(cmd, stdin = subprocess.PIPE, stderr = subprocess.PIPE)
            stdout, stderr = xsltproc.communicate()
            if re.findall('XInclude error : could not load.*and no fallback was found', stderr):
                logging.warn('XInclude error, creating fake xsltproc return code')
                xsltproc.returncode = 6

            if xsltproc.returncode != 0:
                logging.warn('%s failed with error %d' % (' '.join(cmd), xsltproc.returncode))
                if xsltproc.returncode == 6:
                    # build failed, probably because it has inline references in
                    # documentation and would require a full module build to get
                    # them properly.  (happens with GTK+)

                    htmlfiles_dir = os.path.join(ext_dirname, self.dirname, 'html')
                    if not os.path.exists(htmlfiles_dir):
                        # tarball shipped without an HTML version of the
                        # documentation, can't do anything here :/
                        logging.error('skipped %s as it is missing an html/ dir' % doc_module)
                        return
                    try:
                        import html5lib
                    except ImportError:
                        html5lib = None
                    if html5lib:
                        # convert files to XML, then process them with xsltproc
                        # to get library.gnome.org look

                        logging.debug('transforming files shipped with tarball')
                        parser = html5lib.HTMLParser()

                        for filename in os.listdir(htmlfiles_dir):
                            src = os.path.join(htmlfiles_dir,filename)
                            dst = os.path.join(web_output_dir, filename)
                            if not filename.endswith('.html'):
                                open(dst, 'w').write(open(src, 'r').read())
                                continue
                            doc = parser.parse(open(src))
                            doc.childNodes[-1].attributes['xmlns'] = 'http://www.w3.org/1999/xhtml'
                            temporary = tempfile.NamedTemporaryFile()
                            temporary.write(doc.childNodes[-1].toxml().encode('utf-8'))
                            temporary.flush()

                            cmd = ['xsltproc', '--output', dst,
                                    '--nonet', '--xinclude',
                                    '--stringparam', 'libgo.h2hmode', 'gtk-doc',
                                    self.html2html_xsl_file,
                                    os.path.join(ext_dirname,
                                            self.dirname, temporary.name)]

                            if app.config.symbols_dbm_filepath:
                                cmd.insert(-2, '--param')
                                cmd.insert(-2, 'libgo.dbm_support')
                                cmd.insert(-2, 'true()')

                            rc = subprocess.call(cmd)
                    else:
                        # simply copy files shipped in tarball
                        logging.debug('copying files shipped with tarball')
                        for filename in os.listdir(htmlfiles_dir):
                            src = os.path.join(htmlfiles_dir, filename)
                            dst = os.path.join(web_output_dir, filename)
                            if not os.path.exists(os.path.split(dst)[0]):
                                os.makedirs(os.path.split(dst)[0])
                            if not os.path.exists(dst) or \
                                    os.stat(src)[stat.ST_MTIME] > os.stat(dst)[stat.ST_MTIME]:
                                open(dst, 'w').write(open(src, 'r').read())

            if html_images:
                # and copy images/
                logging.debug('copying images')
                for html_image in html_images:
                    src = os.path.join(ext_dirname, self.dirname, html_image)
                    if not os.path.exists(src):
                        continue
                    dst = os.path.join(web_output_dir, os.path.basename(html_image))
                    if not os.path.exists(os.path.split(dst)[0]):
                        os.makedirs(os.path.split(dst)[0])
                    if not os.path.exists(dst) or \
                            os.stat(src)[stat.ST_MTIME] > os.stat(dst)[stat.ST_MTIME]:
                        open(dst, 'w').write(open(src, 'r').read())

            # in any case, copy png files from gtk-doc
            for src in glob.glob('/usr/share/gtk-doc/data/*.png'):
                dst = os.path.join(web_output_dir, os.path.basename(src))
                if not os.path.exists(dst) or \
                        os.stat(src)[stat.ST_MTIME] > os.stat(dst)[stat.ST_MTIME]:
                    open(dst, 'w').write(open(src, 'r').read())

        doc = self.get_libgo_document(['en'])
        if not doc:
            return

        doc.keywords.append('gtk-doc')
        doc.category = 'api'
        doc.toc_id = 'api'

        devhelp2_file = [x for x in os.listdir(web_output_dir) if x.endswith('.devhelp2')]
        if os.path.exists(os.path.join(web_output_dir, 'index.xml.en')):
            tree = ET.parse(os.path.join(web_output_dir, 'index.xml.en'))
            if tree.find('title') is not None:
                doc.title['en'] = tree.find('title').text
            elif tree.find('{http://www.w3.org/1999/xhtml}title') is not None:
                doc.title['en'] = tree.find('{http://www.w3.org/1999/xhtml}title').text
        elif devhelp2_file:
            tree = ET.parse(os.path.join(web_output_dir, devhelp2_file[0]))
            doc.title['en'] = tree.getroot().attrib['title']

        if app.config.create_tarballs:
            self.create_tarball(doc)
            self.create_tarball_symlink(doc)

        self.install_version_symlinks(doc)

    def create_tarball(self, doc):
        web_output_dir = app.get_module_web_output_dir(self, versioned=False)
        tarball_name = '%s-html-%s.tar.gz' % (self.modulename, self.version)
        tarball_filepath = os.path.join(web_output_dir, tarball_name)
        if os.path.exists(tarball_filepath):
            mtime = os.stat(tarball_filepath)[stat.ST_MTIME]
        else:
            mtime = 0

        if mtime > self.mtime_tarball:
            logging.debug('using already generated tarball')
        else:
            logging.info('generating doc tarball in %s' % tarball_filepath[
                    len(app.config.output_dir):])
            shipped_html = os.path.join(app.config.private_dir, 'extracts', self.dirname, 'html')
            if not os.path.exists(shipped_html) or len(os.listdir(shipped_html)) == 0:
                logging.warning('tarball shipped without html/, too bad')
                return
            tar = tarfile.open(tarball_filepath, 'w:gz')
            base_tarball_dir = '%s-html-%s' % (self.modulename, self.version)
            for base, dirs, files in os.walk(shipped_html):
                base_dir = '%s/%s' % (base_tarball_dir, base[len(shipped_html):])
                for file in files:
                    orig_file = os.path.join(base, file)
                    tarinfo = tar.gettarinfo(orig_file, '%s/%s' % (base_dir, file))
                    tar.addfile(tarinfo, open(orig_file))
            tar.close()

        doc.tarballs[self.one_dot_version] = tarball_name



