#-*- coding: utf-8 -*-

# Copyright 2012-2013 Calculate Ltd. http://www.calculate-linux.org
#
#  Licensed under the Apache License, Version 2.0 (the "License");
#  you may not use this file except in compliance with the License.
#  You may obtain a copy of the License at
#
#      http://www.apache.org/licenses/LICENSE-2.0
#
#  Unless required by applicable law or agreed to in writing, software
#  distributed under the License is distributed on an "AS IS" BASIS,
#  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
#  See the License for the specific language governing permissions and
#  limitations under the License.

import sys
import time
import re
from xml import xpath
import xml.dom.minidom
from calculate.lib.format.xml_gconf import xml_gconf
from calculate.lib.cl_lang import setLocalTranslate
setLocalTranslate('cl_lib3',sys.modules[__name__])

class xml_gconf_tree(xml_gconf):
    """Класс для объединения gconf_tree-xml файлов"""

    def _join(self, xmlNewNode, xmlOldNode, flagRootNode=True):
        """Объединение корневой ноды шаблона и корневой ноды файла"""
        xmlNode = xmlNewNode
        childNodes = xmlNode.childNodes
        nextOldNode = xmlOldNode
        flagError = False
        if xmlNode.nodeType == xmlNode.ELEMENT_NODE:
            n = xmlNode
            tagName = n.tagName
            tagNameStr = tagName.encode("UTF-8")
            nAction = u''
            nName = u''
            nType = u''
            nValue = u''
            nSchema = u''
            attrName = ''
            if flagRootNode:
                if not tagName == "gconf":
                    self.setError(_("The text is not a valid gconf-XML format \
(not found '<gconf>...</gconf>')"))
                    return False
            else:
                if not tagName in ("dir", "entry"):
                    self.setError(_("The text is not a valid gconf-XML format \
(found '<%(tag)s>..</%(tag)s>')")%{'tag':tagNameStr})
                    return False
                if not n.hasAttribute("name"):
                    self.setError(\
                        _('Not found attribute "name" in %(tag)s')\
                        %{'tag':tagNameStr})
                    return False
                nName = n.getAttribute("name")
                attrName = u"attribute::name='%s'"%nName
            if n.hasAttribute("action"):
                nAction = n.getAttribute("action")
                if not nAction in ("join","replace","drop"):
                    textError = _("In the text of the XML template, "
                      "reserved attribute 'action' comes with an "
                      "incorrect value.\n"
                      "Valid values of the 'action' attribute are: "
                      '(action="join", action="replace", action="drop")')
                    self.setError(textError)
                    return False
            if xmlOldNode.parentNode:
                findAttrStr = ""
                if attrName:
                    findAttrStr = "[%s]"%attrName
                findPath = u"child::%s%s"%(tagName,findAttrStr)
                # Рабочая нода
                if flagRootNode:
                    workNode = xmlOldNode.parentNode
                else:
                    workNode = xmlOldNode
                oldNodes = xpath.Evaluate(findPath, workNode)
                # По умолчанию - объединение аттрибутов
                flagJoin = True
                flagReplace = False
                flagDrop = False
                if nAction=="replace":
                    flagReplace = True
                    flagJoin = False
                # Удаляем ноду
                if nAction == "drop":
                    flagDrop = True
                    flagJoin = False
                    if flagRootNode:
                        textError = \
                            _('Incorrect action="drop" in the root node')
                        self.setError(textError)
                        return False
                if oldNodes:
                    if len(oldNodes)>1:
                        textError = _("Ambiguity in this template: the "
                                      "same nodes are on a same level")
                        self.setError(textError)
                        return False
                    nextOldNode = oldNodes[0]
                    # Объединение аттрибутов нод
                    if flagJoin:
                        listOldAttributes = nextOldNode.attributes.items()
                        if listOldAttributes:
                            listOldAttributes = filter(lambda x:\
                                x[0] != "mtime", listOldAttributes)
                        # Замена содержимого
                        if xmlNode.tagName == "entry":
                            replaceXmlNode = xmlNode.cloneNode(True)
                            # Сравнение содержимого нод
                            if not self.cmpListsNodesEntry([replaceXmlNode],
                                                            [nextOldNode]):
                                replaceXmlNode.setAttribute("mtime",
                                                            self.currentTime)
                                if not\
                                    self._removeDropNodesAndAttrAction(\
                                                                replaceXmlNode):
                                    return False
                                workNode.replaceChild(replaceXmlNode,
                                                    nextOldNode)
                            childNodes = False
                        listNewAttributes = xmlNode.attributes.items()
                        if listNewAttributes:
                            listNewAttributes = filter(lambda x:\
                                not x[0] in ("action", "mtime"),
                                listNewAttributes)
                        if set(listNewAttributes)!= set(listOldAttributes):
                            # Объединение аттрибутов
                            for attrName, attrValue in listNewAttributes:
                                nextOldNode.setAttribute(attrName, attrValue)
                            if nextOldNode.tagName == "entry":
                                nextOldNode.setAttribute("mtime",
                                                         self.currentTime)
                    # Замещение ноды
                    elif flagReplace:
                        replaceXmlNode = xmlNode.cloneNode(True)
                        # Сравнение содержимого нод
                        if not self.cmpListsNodesEntry([replaceXmlNode],
                                                       [nextOldNode]):
                            if replaceXmlNode.tagName == "entry":
                                replaceXmlNode.setAttribute("mtime",
                                                        self.currentTime)
                            if not\
                                self._removeDropNodesAndAttrAction(\
                                                                replaceXmlNode):
                                return False
                            workNode.replaceChild(replaceXmlNode,
                                                nextOldNode)
                        childNodes = False
                    # Удаление ноды
                    elif flagDrop:
                        workNode.removeChild(nextOldNode)
                        childNodes = False
                else:
                    # Добавление ноды
                    childNodes = False
                    if not flagDrop:
                        appendXmlNode = xmlNode.cloneNode(True)
                        if appendXmlNode.tagName == "entry":
                            appendXmlNode.setAttribute("mtime",
                                                        self.currentTime)
                        if not\
                            self._removeDropNodesAndAttrAction(appendXmlNode):
                                return False
                        workNode.appendChild(appendXmlNode)
            if childNodes:
                for node in childNodes:
                    if not self._join(node, nextOldNode, False):
                        flagError = True
                        break
        if flagError:
            return False
        return True

    def join(self, xml_gconfObj):
        """Объединяем конфигурации"""
        # Получаем текущее время
        self.currentTime = self.getCurrentTime()
        if isinstance(xml_gconfObj, xml_gconf_tree):
            try:
                self.joinDoc(xml_gconfObj.doc)
            except:
                self.setError(_("Failed to join the template"))
                return False
        return True
