from lxml import etree
import logging

DEFAULT_NAMESPACE_KEY = 'ns'
DEFAULT_NAMESPACE = {DEFAULT_NAMESPACE_KEY:'http://autosar.org/schema/r4.0'}

class Arxml:

    def __init__(self, arxml_file_name: str) -> None:
        self.__file_name: str = arxml_file_name
        self.__root: etree.Element = self.get_arxml_root()


    @property
    def root(self) -> etree.Element:
        return self.__root

    def get_arxml_root(self):
        parser = etree.XMLParser(remove_blank_text=True)
        tree = etree.parse(self.__file_name, parser=parser)
        root = tree.getroot()

        return root

    '''
        XPath 通过前缀 ns:'xxxxx' 进行namespace解析
        find findall方法则是通过 {xxxxx}TAG-NAME 进行解析
        ETXpath 类似find findall方法的形式，通过{xxxx}TAG-NAME
    '''
    @staticmethod
    def get_arxml_namespace(element: etree.Element):
        ns = element.nsmap

        # add default namespace
        if None in ns:
            ns[DEFAULT_NAMESPACE_KEY] = ns[None]
            ns.pop(None)
        elif len(ns) == 0:
            ns = None

        return ns

    @staticmethod
    def get_formated_xpath_with_namespace_key(xpath_str: str, namespace_key: str = DEFAULT_NAMESPACE_KEY):
        '''

        :param xpath_str: src xpath string
        :param namespace_key: need to
        :return:
        '''
        namespace_prefix = '{}:'.format(namespace_key) if '' != namespace_key else ''

        result = xpath_str.replace('//', '**{}'.format(namespace_prefix))
        result = result.replace('/', '/{}'.format(namespace_prefix))
        result = result.replace('**', '//')

        return result

    @staticmethod
    def get_format_xpath_pattern(xpath_str, xpath_text='', xpath_rule=None, namespace_key=DEFAULT_NAMESPACE_KEY):
        '''

        :param xpath_str:
        :param xpath_text:
        :param xpath_rule:
            CONTAINS
            TEXT_EQ
            ATTR_EQ

        :param namespace_key:
        :return:
        '''
        xpath_with_ns = Arxml.get_formated_xpath_with_namespace_key(xpath_str, namespace_key)

        if xpath_rule == 'CONTAINS':
            xpath_suffix = '[contains(text(),"{}")]'.format(xpath_text)
        elif xpath_rule == 'TEXT_EQ':
            xpath_suffix = '[text()="{}"]'.format(xpath_text)
        elif (xpath_rule == 'ATTR_EQ' and isinstance(xpath_text, dict)):
            attr_name, attr_value = xpath_text.popitem()
            xpath_suffix = '[@{}={}]'.format(attr_name, attr_value)
        else:
            xpath_suffix = ''
            pass

        return xpath_with_ns + xpath_suffix


    @staticmethod
    def get_xpath_result(element, xpath_str, xpath_text='', xpath_rule=None):

        ns = Arxml.get_arxml_namespace(element)
        if ns is None:
            ns_key = ''
        else:
            ns_key = DEFAULT_NAMESPACE_KEY

        format_xpath = Arxml.get_format_xpath_pattern(xpath_str, xpath_text, xpath_rule, ns_key)

        # print(format_xpath)
        result = element.xpath(format_xpath, namespaces=ns)

        if len(result) == 0:
            result = None

        return result


    @staticmethod
    def get_all_match_element(root_element:etree.Element, xpath_str, xpath_text='', xpath_rule=None):

        result = Arxml.get_xpath_result(root_element, xpath_str, xpath_text, xpath_rule)
        return result

    @staticmethod
    def get_first_match_element(root_element:etree.Element, xpath_str, xpath_text='', xpath_rule=None):

        result = Arxml.get_all_match_element(root_element, xpath_str, xpath_text, xpath_rule)

        if result is not None:
            result = result[0]

        return result

    @staticmethod
    def get_brother_element(current_element:etree.Element, brother_tag, xpath_text='', xpath_rule=None):

        parent = current_element.getparent()
        if parent is not None:
            brother = Arxml.get_first_match_element(parent, brother_tag, xpath_text, xpath_rule)
        else:
            brother = None

        return brother

    @staticmethod
    def get_parent_element(current_element):

        return current_element.getparent()

    @staticmethod
    def get_container_text(current_element):
        if etree.iselement(current_element):
            return current_element.text
        else:
            return ''

    @staticmethod
    def xml_write_to_file(element, output_fileName):
        print(output_fileName)
        tree = etree.ElementTree(element)
        tree.write(output_fileName, pretty_print=True, xml_declaration=True, encoding='utf-8')
        logging.debug('Generate arxml file {}'.format(output_fileName))

    @staticmethod
    def creat_child_new_element(element, element_tag, text_str='', attribute_dict={}):
        new_element = etree.Element(element_tag, attribute_dict)
        if text_str != '':
            new_element.text = text_str
        element.append(new_element)

        return new_element

    @staticmethod
    def creat_brother_new_element(cur_element, element_tag, text_str='', attribute_dict={}):
        par_element = cur_element.getparent()
        if par_element is not None:
            bro_element = Arxml.creat_child_new_element(par_element, element_tag, text_str, attribute_dict)
        else:
            bro_element = None

        return bro_element

    @staticmethod
    def get_split_end_value(long_str_value):
        return long_str_value.split('/')[-1]

    @staticmethod
    def get_element_tag(element):
        tag_with_ns = element.tag
        if tag_with_ns != '':
            if '}' in tag_with_ns:
                position = tag_with_ns.find('}')
                tag = tag_with_ns[position+1:]
            else:
                tag = tag_with_ns

        else:
            tag = ''

        return tag

if __name__ == '__main__':
    arxml = Arxml('EH32_GW04_IpduM_ecuc.arxml')

    element = Arxml.get_first_match_element(arxml.root, './/DEFINITION-REF', 'IpduMRxDirectComInvocation', 'CONTAINS')
    par_ele = Arxml.get_parent_element(element)

    Arxml.creat_child_new_element(par_ele, 'aaaa', 'texttexttext', {'class':'1234'})
    Arxml.creat_child_new_element(par_ele, 'bbbb', '', {'class': '1234'})
    Arxml.creat_child_new_element(par_ele, 'bbbb1', 'dasfadfsad')
    cccc = Arxml.creat_child_new_element(par_ele, 'cccc')
    dddd = Arxml.creat_brother_new_element(cccc, 'dddd')
    Arxml.xml_write_to_file(arxml.root, 'test.arxml')

