# Copyright (c) 2016 Dell Inc. or its subsidiaries.
# All Rights Reserved.
#
#    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 io
import re

from lxml import etree


class XMLAPIParser(object):
    def __init__(self):
        # The following Boolean acts as the flag for the common sub-element.
        # For instance:
        #     <CifsServers>
        #         <li> server_1 </li>
        #     </CifsServers>
        #     <Alias>
        #         <li> interface_1 </li>
        #     </Alias>
        self.is_QueryStatus = False
        self.is_CifsServers = False
        self.is_Aliases = False
        self.is_MoverStatus = False
        self.is_TaskResponse = False
        self.is_Vdm = False
        self.is_Interfaces = False

        self.elt = {}

    def _remove_ns(self, tag):
        i = tag.find('}')
        if i >= 0:
            tag = tag[i + 1:]
        return tag

    def parse(self, xml):
        result = {
            'type': None,
            'taskId': None,
            'maxSeverity': None,
            'objects': [],
            'problems': [],
        }

        events = ("start", "end")

        context = etree.iterparse(io.BytesIO(xml),
                                  events=events)
        for action, elem in context:
            self.tag = self._remove_ns(elem.tag)

            func = self._get_func(action, self.tag)
            if func in vars(XMLAPIParser):
                if action == 'start':
                    eval('self.' + func)(elem, result)  # nosec B307
                elif action == 'end':
                    eval('self.' + func)()  # nosec B307

        return result

    def _get_func(self, action, tag):
        if tag == 'W2KServerData':
            return action + '_' + 'w2k_server_data'

        temp_list = re.sub(r"([A-Z])", r" \1", tag).split()
        if temp_list:
            func_name = action + '_' + '_'.join(temp_list)
        else:
            func_name = action + '_' + tag
        return func_name.lower()

    def _copy_property(self, source, target, property, list_property=None):
        for key in property:
            if key in source:
                target[key] = source[key]

        if list_property:
            for key in list_property:
                if key in source:
                    target[key] = source[key].split()

    def _append_elm_property(self, elm, result, property, identifier):
        for obj in result['objects']:
            if (identifier in obj and identifier in elm.attrib and
                    elm.attrib[identifier] == obj[identifier]):
                for key, value in elm.attrib.items():
                    if key in property:
                        obj[key] = value

    def _append_element(self, elm, result, property, list_property,
                        identifier):
        sub_elm = {}
        self._copy_property(elm.attrib, sub_elm, property, list_property)

        for obj in result['objects']:
            if (identifier in obj and identifier in elm.attrib and
                    elm.attrib[identifier] == obj[identifier]):
                if self.tag in obj:
                    obj[self.tag].append(sub_elm)
                else:
                    obj[self.tag] = [sub_elm]

    def start_task_response(self, elm, result):
        self.is_TaskResponse = True
        result['type'] = 'TaskResponse'
        self._copy_property(elm.attrib, result, ['taskId'])

    def end_task_response(self):
        self.is_TaskResponse = False

    def start_fault(self, elm, result):
        result['type'] = 'Fault'

    def start_status(self, elm, result):
        if self.is_TaskResponse:
            result['maxSeverity'] = elm.attrib['maxSeverity']
        elif self.is_MoverStatus or self.is_Vdm:
            self.elt['maxSeverity'] = elm.attrib['maxSeverity']

    def start_query_status(self, elm, result):
        self.is_QueryStatus = True
        result['type'] = 'QueryStatus'
        self._copy_property(elm.attrib, result, ['maxSeverity'])

    def end_query_status(self):
        self.is_QueryStatus = False

    def start_problem(self, elm, result):
        self.elt = {}
        properties = ('message', 'messageCode')

        self._copy_property(elm.attrib, self.elt, properties)
        result['problems'].append(self.elt)

    def start_description(self, elm, result):
        self.elt['Description'] = elm.text

    def start_action(self, elm, result):
        self.elt['Action'] = elm.text

    def start_diagnostics(self, elm, result):
        self.elt['Diagnostics'] = elm.text

    def start_file_system(self, elm, result):
        self.elt = {}
        property = (
            'fileSystem',
            'name',
            'type',
            'storages',
            'volume',
            'dataServicePolicies',
            'internalUse',
        )
        list_property = ('storagePools',)

        self._copy_property(elm.attrib, self.elt, property, list_property)
        result['objects'].append(self.elt)

    def start_file_system_capacity_info(self, elm, result):
        property = ('volumeSize',)

        identifier = 'fileSystem'

        self._append_elm_property(elm, result, property, identifier)

    def start_storage_pool(self, elm, result):
        self.elt = {}
        property = ('name', 'autoSize', 'usedSize', 'diskType', 'pool',
                    'dataServicePolicies', 'virtualProvisioning')
        list_property = ('movers',)

        self._copy_property(elm.attrib, self.elt, property, list_property)
        result['objects'].append(self.elt)

    def start_system_storage_pool_data(self, elm, result):
        property = ('greedy', 'isBackendPool')

        self._copy_property(elm.attrib, self.elt, property)

    def start_mover(self, elm, result):
        self.elt = {}
        property = ('name', 'host', 'mover', 'role')
        list_property = ('ntpServers', 'standbyFors', 'standbys')

        self._copy_property(elm.attrib, self.elt, property, list_property)
        result['objects'].append(self.elt)

    def start_mover_status(self, elm, result):
        self.is_MoverStatus = True

        property = ('version', 'csTime', 'clock', 'timezone', 'uptime')

        identifier = 'mover'

        self._append_elm_property(elm, result, property, identifier)

    def end_mover_status(self):
        self.is_MoverStatus = False

    def start_mover_dns_domain(self, elm, result):
        property = ('name', 'protocol')
        list_property = ('servers',)

        identifier = 'mover'

        self._append_element(elm, result, property, list_property, identifier)

    def start_mover_interface(self, elm, result):
        property = (
            'name',
            'device',
            'up',
            'ipVersion',
            'netMask',
            'ipAddress',
            'vlanid',
        )

        identifier = 'mover'

        self._append_element(elm, result, property, None, identifier)

    def start_logical_network_device(self, elm, result):
        property = ('name', 'type', 'speed')
        list_property = ('interfaces',)
        identifier = 'mover'

        self._append_element(elm, result, property, list_property, identifier)

    def start_vdm(self, elm, result):
        self.is_Vdm = True

        self.elt = {}
        property = ('name', 'state', 'mover', 'vdm')

        self._copy_property(elm.attrib, self.elt, property)
        result['objects'].append(self.elt)

    def end_vdm(self):
        self.is_Vdm = False

    def start_interfaces(self, elm, result):
        self.is_Interfaces = True
        self.elt['Interfaces'] = []

    def end_interfaces(self):
        self.is_Interfaces = False

    def start_li(self, elm, result):
        if self.is_CifsServers:
            self.elt['CifsServers'].append(elm.text)
        elif self.is_Aliases:
            self.elt['Aliases'].append(elm.text)
        elif self.is_Interfaces:
            self.elt['Interfaces'].append(elm.text)

    def start_cifs_server(self, elm, result):
        self.elt = {}
        property = ('type', 'localUsers', 'name', 'mover', 'moverIdIsVdm')

        list_property = ('interfaces',)

        self._copy_property(elm.attrib, self.elt, property, list_property)
        result['objects'].append(self.elt)

    def start_aliases(self, elm, result):
        self.is_Aliases = True
        self.elt['Aliases'] = []

    def end_aliases(self):
        self.is_Aliases = False

    def start_w2k_server_data(self, elm, result):
        property = ('domain', 'compName', 'domainJoined')

        self._copy_property(elm.attrib, self.elt, property)

    def start_cifs_share(self, elm, result):
        self.elt = {}
        property = ('path', 'fileSystem', 'name', 'mover', 'moverIdIsVdm')

        self._copy_property(elm.attrib, self.elt, property)
        result['objects'].append(self.elt)

    def start_cifs_servers(self, elm, result):
        self.is_CifsServers = True
        self.elt['CifsServers'] = []

    def end_cifs_servers(self):
        self.is_CifsServers = False

    def start_checkpoint(self, elm, result):
        self.elt = {}
        property = ('checkpointOf', 'name', 'checkpoint', 'state')

        self._copy_property(elm.attrib, self.elt, property)
        result['objects'].append(self.elt)

    def start_mount(self, elm, result):
        self.elt = {}
        property = ('fileSystem', 'path', 'mover', 'moverIdIsVdm')

        self._copy_property(elm.attrib, self.elt, property)
        result['objects'].append(self.elt)
