/*
* Licensed Materials - Property of IBM Corp.
* IBM UrbanCode Build
* (c) Copyright IBM Corporation 2012, 2014. All Rights Reserved.
*
* U.S. Government Users Restricted Rights - Use, duplication or disclosure restricted by
* GSA ADP Schedule Contract with IBM Corp.
*/
package com.urbancode.air.plugin.test.mstest
import org.xml.sax.Attributes;
import org.xml.sax.InputSource
import org.xml.sax.helpers.DefaultHandler
import java.text.SimpleDateFormat
import javax.xml.parsers.SAXParserFactory;

class MSTestHandler extends DefaultHandler {
    final SUCCESS = "success"
    final FAILURE = "failure"
    final ERROR = "error"

    final millisPerSecond = 1000
    final millisPerMinute = 60 * millisPerSecond
    final millisPerHour = 60 * millisPerMinute

    final SimpleDateFormat vs2010Date = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss.SSS");

    final resultMap = buildResultMap()

    def filename
    def onNewTest
    def testTable = [:]

    def inUnitTest
    def inUnitTestResult
    def inMessage

    def testName
    def testResult
    def testDuration
    def testId

    def unitTestId
    def unitTestClassName
    def unitTestName

    def messages = []
    def currentText = new StringBuilder()

    MSTestHandler(def filename) {
        this.filename = filename
    }

    void startElement(String ns, String localName, String qName, Attributes attrs) {
        switch (qName) {
            case 'UnitTest':
                inUnitTest = true
                unitTestId = attrs.getValue('id')
                break;

            case 'UnitTestResult':
                inUnitTestResult = true
                testId = attrs.getValue('testId')
                testName = attrs.getValue('testName')
                testResult = toTestResult(attrs.getValue('outcome'))
                String durationStr;
                durationStr = attrs.getValue('duration')
                if (!durationStr) {
                    def startValue = attrs.getValue('startTime');
                    if (startValue) {
                        String startTime = attrs.getValue('startTime').replace('T',' ').substring(0,23)
                        String endTime = attrs.getValue('endTime').replace('T', ' ').substring(0,23)
                        def startDate = vs2010Date.parse(startTime)
                        def endDate = vs2010Date.parse(endTime)
                        testDuration = endDate.getTime() - startDate.getTime()
                    }
                    else {
                        // aborted / not executed
                        testDuration = 0;
                    }
                }
                else {
                    testDuration = toTestDuration(durationStr)
                }
                break;

            case 'Message':
                if (inUnitTestResult) {
                    inMessage = true
                }
                break;

            case 'TestMethod':
                if (inUnitTest) {
                    unitTestClassName = getClassName(attrs.getValue('className'))
                    unitTestName = attrs.getValue('name')
                }
                break;
        }
    }

    void endElement(String ns, String localName, String qName) {
        switch (qName) {
            case 'UnitTest':
                testTable[unitTestId] = [className: unitTestClassName, name: unitTestName]
                inUnitTest = false
                break;

            case 'UnitTestResult':
                def className = testTable[testId].className
                onNewTest(testName, className, testResult, testDuration, messages)
                inUnitTestResult = false
                messages = []
                break;

            case 'Message':
                if (inUnitTestResult) {
                    messages << currentText.toString()
                    currentText = new StringBuilder()
                    inMessage = false;
                }
                break;
        }
    }

    void characters(char[] ch, int start, int length) {
        if (inMessage) {
            currentText.append(ch, start, length)
        }
    }

    def toTestResult(msTestOutcome) {
        return resultMap[msTestOutcome]
    }

    def toTestDuration(msTestDuration) {
        def matcher = msTestDuration =~ /(\d+):(\d+):(\d+(?:.\d+)?)/
        def hours = matcher[0][1]
        def minutes = matcher[0][2]
        def seconds = matcher[0][3]
        def duration = hours.toInteger() * millisPerHour +
                minutes.toInteger() * millisPerMinute +
                seconds.toBigDecimal() * millisPerSecond
        return duration.setScale(0, java.math.RoundingMode.HALF_UP)
    }

    def buildResultMap() {
        def map = [
                'Error': ERROR,
                'Failed': FAILURE,
                'Timeout': FAILURE,
                'Aborted': FAILURE,
                'Inconclusive': FAILURE,
                'PassedButRunAborted': SUCCESS,
                'NotRunnable': ERROR,
                'NotExecuted': FAILURE,
                'Disconnected': ERROR,
                'Warning': SUCCESS,
                'Passed': SUCCESS,
                'Completed': SUCCESS,
                'InProgress': ERROR,
                'Pending': ERROR]
        return map.asImmutable()
    }

    def getClassName(classStrongName) {
        def i = classStrongName.indexOf(',')
        def className = classStrongName[0 ..< i].trim()
        className = className.replaceAll(/\+/, '.')
        className = className.replaceAll('\\\\\\\\', '\\\\')
        return className
    }

    def readTests(Closure closure) {
        onNewTest = closure
        def reader = SAXParserFactory.newInstance().newSAXParser().XMLReader
        def suiteFile = (filename instanceof File) ? filename : new File(filename)
        suiteFile.withInputStream { stream ->
            reader.setContentHandler(this)
            reader.parse(new InputSource(stream))
        }
    }
}
