#!/usr/bin/env python
# -*- coding: utf-8

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

import logging
import sys


# Collation: Sort by the length of xpathNode.
def order_by_xpath(elem):
    for key in elem:
        xpath_result = key.split("/")
    return len(xpath_result)

# remove the prefix from XPATH
# fun called before: [ { '/node1/prefix2:node2/prefix3:node3':[ text3,xmlns3 ] },... ]
# fun called after: [ { '/node1/node2/node3':[ text3,xmlns3,'/node1/prefix2:node2/prefix3:node3' ] },... ]
def xmlns_info_remove_prefix(xmlns_info_list):
    """
    :param xmlns_info_list: the xmlns info list
    """
    new_xmlns_info_list = []
    for item in xmlns_info_list:
        new_dict = {}
        for xapth_key in item:
            xapth_key_list = xapth_key.split("/")[1:]
            new_xapth_key = ''
            for each_node in xapth_key_list:
                new_xapth_key = new_xapth_key + "/" + each_node.split(":")[-1]
            temp_list = item[xapth_key]
            temp_list.append(xapth_key)
        new_dict[new_xapth_key] = temp_list
        new_xmlns_info_list.append(new_dict)
    return new_xmlns_info_list

# Recursive function,to generate xml with xmlns.
def get_all_xpath_from_new_xmlns_info(new_xmlns_info_list):
    """
    :param new_xmlns_info_list:  The xmlns info list generated by "_full.xml".Have been sorted and the key without prefix.
    :return xpath_list: [ '/node1','/node1/node2','/node1/node2/node3',... ]     ( without prefix )
    """
    xpath_list = []
    for item in new_xmlns_info_list:
        for xapth_key in item:
            xpath_list.append(xapth_key)
    return xpath_list

# Recursive function,to generate xml with xmlns.
def create_xml_with_xmlns(root, new_xmlns_info_list, all_xpath_without_prefix, dest_xpath, current_element):
    """
    :param root: The xml root which need to be rebuild.( always come from instance xml )
    :param new_xmlns_info_list: The xmlns info list generated by "_full.xml".Have been sorted and the key without prefix.
    :param all_xpath_without_prefix: get all xpath key from new_xmlns_info_list.Type: list
    :param dest_xpath: record the path of rebuild
    :param current_element: the current generate element root
    """
    if root.getchildren():
        # traversing the xml root which need to be rebuild.( always come from instance xml )
        for child_of_root in root.getchildren():
            dest_xpath = dest_xpath + "/" + child_of_root.tag

            # get value from instance xml
            instance_value = child_of_root.text
            instance_attributes = child_of_root.attrib  # instance_attributes: operation="XXX"

            # instance node'xpath must exist in "_full.xml"
            if dest_xpath in all_xpath_without_prefix:
                original_tag_name = ""
                xmlns = ""
                value = ""

                # find the same xpath from new_xmlns_info_list,get the original_tag_name
                for new_xmlns_info_list_item in new_xmlns_info_list:
                    for xapth_key in new_xmlns_info_list_item:
                        if xapth_key == dest_xpath:
                            tag_name_before = new_xmlns_info_list_item[xapth_key][2]
                            original_tag_name = tag_name_before.split("/")[-1]
                            value = instance_value
                            xmlns = new_xmlns_info_list_item[xapth_key][1]

                #  use the original_tag_name to rebuild sub element node.
                sub_element = ET.SubElement(current_element, original_tag_name)
                # set the element's value,the value come from instance xml
                if value != '':
                    sub_element.text = value
                # set the element's attributes,the attributes come from instance xml.Like: operation="XXX"
                if instance_attributes:
                    for instance_attribute in instance_attributes:
                        sub_element.set(instance_attribute, instance_attributes[instance_attribute])
                #  set the element's attributes,the attributes come from "_full.xml".Like: xmlns="XXX"
                if xmlns:
                    xmlns_list = xmlns.split("@")
                    for xmlns in xmlns_list[1:]:
                        xmlns_temp = xmlns.split("=")
                        xmlns_key = xmlns_temp[0]
                        xmlns_value = xmlns_temp[1].strip('"')
                        sub_element.set(xmlns_key, xmlns_value)
                # recursive
                create_xml_with_xmlns(child_of_root, new_xmlns_info_list, all_xpath_without_prefix, dest_xpath, sub_element)
            else:
                logging.info(dest_xpath+":This xpath not in the full.xml,please check.")
            dest_xpath = dest_xpath[:-(len(child_of_root.tag)+1)]

# join xmlns into fil_content
def xml_parser_join_xmlns(xml_content, xmlns_info_list, filter_or_config_type):
    """
    :param xml_content: the xml str which need to be joined xmlns.
    :param xmlns_info: xmlns info list generated by "_full.xml"
    :param filter_or_config_type: to distinguish filter or config message
    :return:xml_str: xml str which contain the xmlns
    """
    xmlns_info_list.sort(key=order_by_xpath)
    new_xmlns_info_list = xmlns_info_remove_prefix(xmlns_info_list)
    all_xpath_without_prefix = get_all_xpath_from_new_xmlns_info(new_xmlns_info_list)

    original_root = ET.fromstring(xml_content)
    dest_xpath = ""

    if filter_or_config_type == 'filter':
        generate_root = ET.Element('filter')
        generate_root.set("type", "subtree")
    elif filter_or_config_type == 'config':
        generate_root = ET.Element('config')
        generate_root.set("xmlns:nc", "urn:ietf:params:xml:ns:netconf:base:1.0")
    elif filter_or_config_type == 'rpc':
        generate_root = ET.Element('rpc')
    create_xml_with_xmlns(original_root, new_xmlns_info_list, all_xpath_without_prefix, dest_xpath, generate_root)
    if sys.version < "3":
        xml_str = ET.tostring(generate_root, method='xml')
    else:
        xml_str = ET.tostring(generate_root, method='xml', encoding="utf-8").decode('utf-8')
    return xml_str