# 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

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

import logging
import urlparse

from document import RemoteDocument
from utils import version_cmp, is_version_number


class SubIndex:
    def __init__(self, node):
        self.id = node.attrib.get('id')
        self.weight = node.attrib.get('weight')
        self.sections = node.find('sections').text.split()
        self.title = {}
        self.abstract = {}

        for title in node.findall('title'):
            lang = title.attrib.get(
                    '{http://www.w3.org/XML/1998/namespace}lang', 'en')
            self.title[lang] = title.text
        for abstract in node.findall('abstract'):
            lang = abstract.attrib.get(
                    '{http://www.w3.org/XML/1998/namespace}lang', 'en')
            self.abstract[lang] = abstract.text

    def create_element(self, parent, channel, language):
        index = ET.SubElement(parent, 'index')
        if language == 'C':
            language = 'en'
        index.set('id', self.id)
        index.set('lang', language)
        index.set('channel', channel)
        index.set('weigth', self.weight)

        title = self.title.get(language)
        if not title:
            title = self.title.get('en')
            if not title:
                title = self.id
        ET.SubElement(index, 'title').text = title

        abstract = self.abstract.get(language)
        if not abstract:
            abstract = self.abstract.get('en')
        if abstract:
            ET.SubElement(index, 'abstract').text = abstract

        return index


class Overlay:
    def __init__(self, overlay_file):
        self.tree = ET.parse(overlay_file)
        self.modified_docs = {}
        self.new_docs = []
        self.more_tarball_docs = {}
        self.quirks = {}
        self.extra_devel_releases = {}

        self.create_extra_wiki_docs()

        for doc in self.tree.findall('/documents/document'):
            if 'doc_module' in doc.attrib:
                # modifying an existing document
                self.modified_docs[(
                        doc.attrib['doc_module'], doc.attrib['channel'])] = doc

            if not 'doc_module' in doc.attrib or (
                    doc.find('new') is not None or doc.find('local') is not None):
                # new document
                self.new_docs.append(doc)

            if 'matching_tarball' in doc.attrib:
                tarball = doc.attrib['matching_tarball']
                if not tarball in self.more_tarball_docs:
                    self.more_tarball_docs[tarball] = []
                tarball_docs = self.more_tarball_docs[tarball]
                tarball_docs.append(doc)

            if 'doc_module' in doc.attrib:
                doc_module = doc.attrib.get('doc_module')
                for extra in [x.text for x in doc.findall('extra-devel-releases')]:
                    self.extra_devel_releases[(doc_module, extra)] = True

        self.toc_mapping = {}
        for mapping in self.tree.findall('/subsections/map'):
            channel = mapping.attrib.get('channel')
            sectionid = mapping.attrib.get('id')
            subsection = mapping.attrib.get('subsection')
            self.toc_mapping[(channel, sectionid)] = subsection

        for quirks in self.tree.findall('/quirks'):
            self.quirks[(quirks.attrib['doc_module'], quirks.attrib['channel'])] = quirks

    def create_extra_wiki_docs(self):
        try:
            import html5lib
        except ImportError:
            logging.error('failed to import html5lib, skipping extra wiki docs')
            return
        documents_node = self.tree.find('documents')
        for extra in self.tree.findall('/documents/extrawikidocs'):
            href = extra.find('href').text
            content = app.download(href + '?action=print', use_cache=False)

            # parse the wiki page and get all links in content
            parser = html5lib.HTMLParser()
            doc = parser.parse(file(content))
            del doc.childNodes[:-1]
            html = ET.fromstring(doc.toxml())
            links = ET.ElementTree(html).findall('//a')

            channel = extra.find('channel').text
            category = extra.find('category').text
            attributes = extra.find('attributes')

            for link in links:
                if link.attrib.get('title'):
                    # heuristic to eliminate generated links
                    continue
                doc_href = urlparse.urljoin(href, link.attrib.get('href'))
                logging.info('adding extra document from wiki: %s' % doc_href)
                title = link.text
                doc_node = ET.SubElement(documents_node, 'document')
                doc_node.attrib['doc_module'] = doc_href.split('/')[-1]
                doc_node.attrib['channel'] = channel
                doc_node.attrib['category'] = category
                ET.SubElement(doc_node, 'new')
                ET.SubElement(doc_node, 'title').text = title
                ET.SubElement(doc_node, 'href').text = doc_href + '?action=print'
                local_node = ET.SubElement(doc_node, 'local')
                for key, value in attributes.attrib.items():
                    local_node.attrib[key] = value
                local_node.attrib['nocache'] = 'true'

    def apply(self, document):
        if (document.channel, document.toc_id) in self.toc_mapping:
            document.subsection = self.toc_mapping[(document.channel, document.toc_id)]

        key = (document.module, document.channel)
        overlay = self.modified_docs.get(key)
        if overlay is None:
            return
        if overlay.find('title') is not None:
            for title in overlay.findall('title'):
                lang = title.attrib.get(
                        '{http://www.w3.org/XML/1998/namespace}lang', 'en')
                document.title[lang] = title.text
            for lang in document.languages:
                if not document.title.get(lang):
                    document.title[lang] = document.title.get('en')
        if overlay.find('abstract') is not None:
            for abstract in overlay.findall('abstract'):
                lang = abstract.attrib.get(
                        '{http://www.w3.org/XML/1998/namespace}lang', 'en')
                document.abstract[lang] = abstract.text
            for lang in document.languages:
                if not document.abstract.get(lang):
                    document.abstract[lang] = document.abstract.get('en')
        if overlay.find('subsection') is not None:
            document.subsection = overlay.find('subsection').text
        if overlay.find('category') is not None:
            document.toc_id = overlay.find('category').text
        if overlay.attrib.get('weight'):
            document.weight = overlay.attrib.get('weight')

        if overlay.find('keywords') is not None:
            for keyword in overlay.findall('keywords/keyword'):
                document.keywords.append(keyword.text)

    def get_channel_overlay(self, module, current_channel):
        for doc in self.tree.findall('/documents/document'):
            if doc.attrib.get('doc_module') != module:
                continue
            if doc.attrib.get('old-channel') == current_channel:
                return doc.attrib.get('channel')
        return current_channel

    def get_new_docs(self):
        l = []
        for overlay in self.new_docs:
            doc = RemoteDocument(overlay)
            if app.config.channels is None or doc.channel in app.config.channels:
                doc.retrieve_if_necessary(overlay)
                self.apply(doc)
                l.append(doc)
        return l

    def get_section_weight(self, section_id):
        for section in self.tree.findall('subsections/subsection'):
            if section.attrib.get('id') == section_id:
                return float(section.attrib.get('weight', 0.5))
        return 0.5

    def get_subindexes(self, channel):
        for subindexes in self.tree.findall('subsections/subindexes'):
            if subindexes.attrib.get('channel') != channel:
                return

            return [SubIndex(x) for x in subindexes.findall('subindex')]

    def get_quirks(self, doc):
        key = (doc.modulename, doc.channel)
        quirks = self.quirks.get(key)
        if quirks is None:
            return []
        q = []
        for quirk in quirks.findall('quirk'):
            min_version = quirk.attrib.get('appears-in')
            max_version = quirk.attrib.get('fixed-in')
            if min_version and version_cmp(min_version, doc.version) > 0:
                continue
            if max_version and version_cmp(max_version, doc.version) <= 0:
                continue
            q.append(quirk.text)
        return q

    def is_development_release(self, modulename, version):
        if self.extra_devel_releases.get((modulename, version)):
            return True
        return False

