import xml.etree.ElementTree as ET
import xml.dom.minidom as MD

from .testSuite import TestSuite
from .testCase import TestCase
from .utils import forceUnicode, cleanIllegalXmlChars


class TestReport(object):


    class XmlDecodingFailure(Exception):
        pass
    class MergeFailure(Exception):
        pass


    def __init__(self, testSuites=None, **kwargs):
        self.params = {
            'time': None,
            'name': None,
            'tests': None,
            'failures': None,
            'errors': None,
            'disabled': None,
            'testSuites': [],
            'timeAggregate': sum,
        }
        self.attributeNames = [
            'time',
            'name',
            'tests',
            'failures',
            'errors',
            'disabled',
        ]

        if 'timeAggregate' in kwargs and kwargs['timeAggregate'] is not None:
            self.params['timeAggregate'] = kwargs['timeAggregate']

        if testSuites is not None and not isinstance(testSuites, list):
            testSuites = [testSuites]

        if testSuites is not None:
            self.params['testSuites'] = testSuites
            self._recalculateParams()

        self.params.update(kwargs)


    def toRawData(self):
        testReportData = {
            'testSuites': [],
        }

        for testSuite in self.params['testSuites']:
            testSuiteData = {
                'testCases': [],
            }

            for testCase in testSuite.params['testCases']:
                testSuiteData['testCases'].append(testCase.params)

            testSuiteData.update(dict([(k, v) for k, v in testSuite.params.items() if
                                       k in testSuite.attributeNames]))
            testReportData['testSuites'].append(testSuiteData)

        testReportData.update(dict([(k, v) for k, v in self.params.items() if k in self.attributeNames]))

        return testReportData


    def toXml(self, prettyPrint=False, encoding=None):
        testsuitesAttrib = dict([(key, forceUnicode(val, encoding)) for key, val in self.params.items() if
                                 key in self.attributeNames and
                                 val is not None])
        testsuitesNode = ET.Element('testsuites', attrib=testsuitesAttrib)
        for testSuite in self.params['testSuites']:
            testsuiteAttrib = dict([(key, forceUnicode(val, encoding)) for key, val in testSuite.params.items() if
                                    key in testSuite.attributeNames and
                                    val is not None])
            testsuiteNode = ET.SubElement(testsuitesNode, 'testsuite', attrib=testsuiteAttrib)
            for testCase in testSuite.params['testCases']:
                testcaseAttrib = dict([(key, forceUnicode(val, encoding)) for key, val in testCase.params.items() if
                                       key in testCase.attributeNames and
                                       val is not None])
                testcaseNode = ET.SubElement(testsuiteNode, 'testcase', attrib=testcaseAttrib)
                for childName in testCase.childNames.keys():
                    childAttrib = dict([(key.split('_')[1], forceUnicode(val, encoding)) for key, val in
                                        testCase.params.items() if
                                        key.startswith('%s_' % childName) and
                                        val is not None])
                    if testCase.params[childName] is not None or len(childAttrib.items()) > 0:
                        childNode = ET.SubElement(testcaseNode, testCase.childNames[childName], attrib=childAttrib)
                        childNode.text = forceUnicode((testCase.params[childName]), encoding)

        uglyXml = ET.tostring(testsuitesNode, encoding=encoding)
        uglyXml = uglyXml.decode(encoding or 'utf-8')
        uglyXml = cleanIllegalXmlChars(uglyXml)

        if prettyPrint:
            uglyXml = uglyXml.encode(encoding or 'utf-8')
            xml = MD.parseString(uglyXml)
            xml = xml.toprettyxml(encoding=encoding)
            if encoding:
                xml = xml.decode(encoding or 'utf-8')
            return xml

        return uglyXml


    def fromXml(self, xmlStr, encoding=None):
        self._clearAttributes()

        xmlStr = xmlStr.encode(encoding or 'utf-8')
        root = ET.fromstring(xmlStr)
        if root.tag != 'testsuites':
            raise self.XmlDecodingFailure

        self._fillAttributes(root.attrib)
        self.params['testSuites'] = []

        for child in root:
            if child.tag == 'testsuite':
                testSuite = TestSuite()
                testSuite._fillAttributes(child.attrib)

                for subchild in child:
                    if subchild.tag == 'testcase':
                        testCase = TestCase()
                        testCase._fillAttributes(subchild.attrib)

                        for subsubchild in subchild:
                            if subsubchild.tag in testCase.childNames.values():
                                childNamesToParamNames = dict([(v, k) for k, v in testCase.childNames.items()])
                                paramName = childNamesToParamNames[subsubchild.tag]
                                testCase.params[paramName] = subsubchild.text
                                for attributeName, attributeValue in subsubchild.attrib.items():