
# File: CeParser.py ; This file is part of Twister.

# version: 3.017

# Copyright (C) 2012-2014 , Luxoft

# Authors:
#    Andreea Proca <aproca@luxoft.com>
#    Andrei Costachi <acostachi@luxoft.com>
#    Cristi Constantin <crconstantin@luxoft.com>

# 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.

'''
Module to parse the project file
'''

import os
import re
import copy
import ast
#import json
from binascii import hexlify

from collections import OrderedDict

from common.helpers import userHome
from common.tsclogging import logError, logDebug, logWarning
from lxml import etree

def cartesian(lists):
    """
    Helper function to compute cartesian multiply
    """
    if lists == []:
        return [()]
    return [x + (y,) for x in cartesian(lists[:-1]) for y in lists[-1]]


class CeXmlParser(object):
    """
    Parse and generate project.xml files.
    """

    def __init__(self, project):
        self.project = project


    def _edit_suite(self, ep_id, sut, suite):
        '''
        @param:
            ep_id: the id of the ep
            sut: the name of the sut
            suite: the suite to be edited
        @summary:
            Update suite props with sut name, ep_id
            Delete test cases having running = false.
            Delete the empty suite/subsuite
        '''

        for epname in suite.findall('.//EpId'):
            epname.text = ep_id

        for sut_name in suite.findall('.//SutName'):
            sut_name.text = sut

        # delete tc if running = false
        for prop in suite.findall(".//Property"):
            if prop.find('propName').text == 'Running' and\
            prop.find('propValue').text == 'false':
                grand_parent = (prop.getparent()).getparent()
                grand_parent.remove(prop.getparent())

        # delete empty sub-suites from this suite
        for suite in suite.xpath('//TestSuite'):
            if suite.find('TestSuite') is None and\
            suite.find('TestCase') is None:
                suite_parent = suite.getparent()
                suite_parent.remove(suite)

        return True


    def _resolve_dependencies(self, xml, dependency_dict):
        """
        @param:
            xml: the project/last_edited xml in etree format
            dependency_dict: a dict that stores the link between new and old ids. Old ids are keys.
        @summary:
            Replaces the ids in all Dependency tags with the new and updated ones.
        """
        dependencies = xml.findall('.//Dependency')
        for dependency in dependencies:
            if dependency.text:
                dep_list = dependency.text.split(';')
                new_dep_list = ''
                for dep in dep_list:
                    condition = dep.split(':')[-1]
                    dep_id = dep.split(':')[0]
                    if dep_id in dependency_dict:
                        new_dep_id = [id+':'+condition for id in dependency_dict[dep_id]]
                        new_dep_list += ';'.join(new_dep_id)+';'
                dependency.text = new_dep_list


    def _change_ids(self, xml, repeated_dict):
        '''
        @param:
            xml: the project/last_edited in etree format
            repeated_dict: the dict that storest the link between old and new IDs. Old ids are keys.
        @summary:
            Change IDs for suite/tc. Add them to repeated_dict dictionary to keep track of changes(to be able 
            to resolve dependencies later)
        '''
        suite_no = 100  # Base ID for suites
        file_no = 1000  # Base ID for tests
 
        # Fix long ugly IDs with short integer IDs
        for suite_id in xml.xpath('//TestSuite/ID'):
            suite_no += 1
            if suite_id.text in repeated_dict:
                repeated_dict[suite_id.text].append(str(suite_no))
            else:
                repeated_dict[suite_id.text] = [str(suite_no)]
            suite_id.text = str(suite_no)
  
        # Fix long ugly IDs with short integer IDs
        for file_id in xml.xpath('//TestCase/ID'):
            file_no += 1
            if file_id.text in repeated_dict:
                repeated_dict[file_id.text].append(str(file_no))
            else:
                repeated_dict[file_id.text] = [str(file_no)]
            file_id.text = str(file_no)


    def _get_iterator_values(self, config_file, iterator_default,\
    interval_values, user, part_interval_values=[], default_values_list=[]):
        """
        Calculates all the possible values for iterators for a test
        """
        data = self.project.configs.read_config_file(user, config_file)
        if data.startswith('*ERROR*'):
            logWarning(data)
            return

        # Try to parse the project file
        try:
            xml_config = etree.fromstring(data)
        except:
            msg = "Config file `{}` is invalid!".format(config_file)
            logWarning(msg)
            return

        config_file_st = etree.tostring(xml_config)
        config_file_fst = etree.fromstring(config_file_st)
        # find all entries having tag = iterator
        config_types = config_file_fst.xpath('//type')
        config_types = [x for x in config_types if x.text == 'iterator']

        # Iterators from config file
        for item in config_types:
            prop_iterator = item.getparent()

            # set the parent component(s) of the iterator
            comp_parent = prop_iterator.getparent()
            comp_parent_name = ''
            if comp_parent.find('fname') is not None:
                comp_parent_name = comp_parent.find('fname').text

            # if this is a sub-component, we need to go up to get all parents
            while True:
                comp_parent = comp_parent.getparent()
                if comp_parent is None:
                    # we reached the root; need to break out from this loop
                    break
                comp_fname = comp_parent.find('fname')
                if comp_fname is not None:
                    comp_parent_name = comp_parent.find('fname').text + '/' +\
                    comp_parent_name

            config_name = config_file + '#' + comp_parent_name +\
            '#' + prop_iterator.find('name').text
            values = prop_iterator.find('value').text
            if not values:
                continue

            values_list = values.replace(' ', '').split(',')

            # get the default value
            index_dot = values_list[0].find('..')
            if index_dot > -1:
                try:
                    default_value = ast.literal_eval(values_list[0][:index_dot])
                    defa
ult_value = int(default_value)
                except:
                    default_value = values_list[0][:index_dot]
            else:
                try:
                    default_value = ast.literal_eval(values_list[0])
                    default_value = int(default_value)
                except:
                    default_value = values_list[0]

            if iterator_default == 'true':
             
   part_interval_values.append(['{}={}'.\
                format(config_name, default_value)])
                default_values_list.append('{}={}'.\
                format(config_name, default_value))
            else:
                iter_interval_values = list()
                key_default_value = '{}={}'.format(config_name, default_value)

                for interv in values_list:
                    re_intervals = re.search('(\w*\d*\.?\d+)\.+(\w*\d*\.?\d+)',\
                    interv)
                    try:
                        x_val = ast.literal_eval(re_intervals.group(1))
                        y_val = ast.