#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
# Copyright 2005-2011 Zuza Software Foundation
#
# This file is part of the Translate Toolkit.
#
# 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, see <http://www.gnu.org/licenses/>.

"""Module for handling XLIFF files for translation.

The official recommendation is to use the extention .xlf for XLIFF files.
"""

from lxml import etree

from translate.misc.multistring import multistring
from translate.storage import base, lisa
from translate.storage.lisa import getXMLspace
from translate.storage.placeables.lisa import strelem_to_xml, xml_to_strelem
from translate.storage.workflow import StateEnum as state


# TODO: handle translation types

ID_SEPARATOR = u"\04"
# ID_SEPARATOR is commonly used through toolkit to generate compound
# unit ids (for instance to concatenate msgctxt and msgid in po), but
# \04 is an illegal char in XML 1.0, ID_SEPARATOR_SAFE will be used
# instead when converting between xliff and other toolkit supported
# formats
ID_SEPARATOR_SAFE = u"__%04__"


class xliffunit(lisa.LISAunit):
    """A single term in the xliff file."""

    rootNode = "trans-unit"
    languageNode = "source"
    textNode = ""
    namespace = 'urn:oasis:names:tc:xliff:document:1.1'

    _default_xml_space = "default"

    # TODO: id and all the trans-unit level stuff

    S_UNTRANSLATED = state.EMPTY
    S_NEEDS_TRANSLATION = state.NEEDS_WORK
    S_NEEDS_REVIEW = state.NEEDS_REVIEW
    S_TRANSLATED = state.UNREVIEWED
    S_SIGNED_OFF = state.FINAL

    statemap = {
                "new": S_UNTRANSLATED + 1,
                "needs-translation": S_NEEDS_TRANSLATION,
                "needs-adaptation": S_NEEDS_TRANSLATION + 1,
                "needs-l10n": S_NEEDS_TRANSLATION + 2,
                "needs-review-translation": S_NEEDS_REVIEW,
                "needs-review-adaptation": S_NEEDS_REVIEW + 1,
                "needs-review-l10n": S_NEEDS_REVIEW + 2,
                "translated": S_TRANSLATED,
                "signed-off": S_SIGNED_OFF,
                "final": S_SIGNED_OFF + 1,
                }

    statemap_r = dict((i[1], i[0]) for i in statemap.iteritems())

    STATE = {
        S_UNTRANSLATED: (state.EMPTY, state.NEEDS_WORK),
        S_NEEDS_TRANSLATION: (state.NEEDS_WORK, state.NEEDS_REVIEW),
        S_NEEDS_REVIEW: (state.NEEDS_REVIEW, state.UNREVIEWED),
        S_TRANSLATED: (state.UNREVIEWED, state.FINAL),
        S_SIGNED_OFF: (state.FINAL, state.MAX),
    }

    def __init__(self, source, empty=False, **kwargs):
        """Override the constructor to set xml:space="preserve"."""
        super(xliffunit, self).__init__(source, empty, **kwargs)
        if empty:
            return
        lisa.setXMLspace(self.xmlelement, "preserve")

    def createlanguageNode(self, lang, text, purpose):
        """Returns an xml Element setup with given parameters."""

        # TODO: for now we do source, but we have to test if it is target,
        # perhaps with parameter. Alternatively, we can use lang, if
        # supplied, since an xliff file has to conform to the bilingual
        # nature promised by the header.
        assert purpose
        langset = etree.Element(self.namespaced(purpose))
        # TODO: check language
        #lisa.setXMLlang(langset, lang)

        langset.text = text
        return langset

    def getlanguageNodes(self):
        """We override this to get source and target nodes."""
        source = None
        target = None
        nodes = []
        try:
            source = self.xmlelement.iterchildren(self.namespaced(self.languageNode)).next()
            target = self.xmlelement.iterchildren(self.namespaced('target')).next()
            nodes = [source, target]
        except StopIteration:
            if source is not None:
                nodes.append(source)
            if not target is None:
                nodes.append(target)
        return nodes

    def set_rich_source(self, value, sourcelang='en'):
        sourcelanguageNode = self.get_source_dom()
        if sourcelanguageNode is None:
            sourcelanguageNode = self.createlanguageNode(sourcelang, u'', "source")
            self.set_source_dom(sourcelanguageNode)

        # Clear sourcelanguageNode first
        for i in range(len(sourcelanguageNode)):
            del sourcelanguageNode[0]
        sourcelanguageNode.text = None

        strelem_to_xml(sourcelanguageNode, value[0])

    def get_rich_source(self):
        #rsrc = xml_to_strelem(self.source_dom)
        #logging.debug('rich source: %s' % (repr(rsrc)))
        #from dubulib.debug.misc import print_stack_funcs
        #print_stack_funcs()
        return [
            xml_to_strelem(self.source_dom,
                           getXMLspace(self.xmlelement,
                                       self._default_xml_space))
        ]
    rich_source = property(get_rich_source, set_rich_source)

    def set_rich_target(self, value, lang='xx', append=False):
        self._rich_target = None
        if value is None:
            self.set_target_dom(self.createlanguageNode(lang, u'', "target"))
            return

        languageNode = self.get_target_dom()
        if languageNode is None:
            languageNode = self.createlanguageNode(lang, u'', "target")
            self.set_target_dom(languageNode, append)

        # Clear languageNode first
        for i in range(len(languageNode)):
            del languageNode[0]
        languageNode.text = None

        strelem_to_xml(languageNode, value[0])
        ### currently giving some issues in Virtaal: self._rich_target = value

    def get_rich_target(self, lang=None):
        """retrieves the "target" text (second entry), or the entry in the
        specified language, if it exists"""
        if self._rich_target is None:
            self._rich_target = [
                xml_to_strelem(self.get_target_dom(lang),
                getXMLspace(self.xmlelement, self._default_xml_space))
            ]
        return self._rich_target
    rich_target = property(get_rich_target, set_rich_target)

    def addalttrans(self, txt, origin=None, lang=None, sourcetxt=None,
                    matchquality=None):
        """Adds an alt-trans tag and alt-trans components to the unit.

        :type txt: String
        :param txt: Alternative translation of the source text.
        """

        # TODO: support adding a source tag ad match quality attribute.  At the
        # source tag is needed to inject fuzzy matches from a TM.
        if isinstance(txt, str):
            txt = txt.decode("utf-8")
        alttrans = etree.SubElement(self.xmlelement, self.namespaced("alt-trans"))
        lisa.setXMLspace(alttrans, "preserve")
        if sourcetxt:
            if isinstance(sourcetxt, str):
                sourcetxt = sourcetxt.decode("utf-8")
            altsource = etree.SubElement(alttrans, self.namespaced("source"))
            altsource.text = sourcetxt
        alttarget = etree.SubElement(alttrans, self.namespaced("target"))
        alttarget.text = txt
        if matchquality:
            alttrans.set("match-quality", matchquality)
        if origin:
            alttrans.set("origin", origin)
        if lang:
            lisa.setXMLlang(alttrans, lang)

    def getalttrans(self, origin=None):
        """Returns <alt-trans> for the given origin as a list of units. No
        origin means
 all alternatives."""
        translist = []
        for node in self.xmlelement.iterdescendants(self.namespaced("alt-trans")):
            if self.correctorigin(node, origin):
                # We build some mini un
its that keep the xmlelement. This
                