# -*- coding: utf-8 -*-

#-------------------------------------------------------------------------
# drawElements Quality Program utilities
# --------------------------------------
#
# Copyright 2015 The Android Open Source Project
#
# 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 os
import sys
import codecs
import xml.dom.minidom
import xml.sax
import xml.sax.handler
from log_parser import BatchResultParser, StatusCode

STYLESHEET_FILENAME = "testlog.xsl"
LOG_VERSION			= '0.3.2'

class BuildXMLLogHandler(xml.sax.handler.ContentHandler):
	def __init__ (self, doc):
		self.doc			= doc
		self.elementStack	= []
		self.rootElements	= []

	def getRootElements (self):
		return self.rootElements

	def pushElement (self, elem):
		if len(self.elementStack) == 0:
			self.rootElements.append(elem)
		else:
			self.getCurElement().appendChild(elem)
		self.elementStack.append(elem)

	def popElement (self):
		self.elementStack.pop()

	def getCurElement (self):
		if len(self.elementStack) > 0:
			return self.elementStack[-1]
		else:
			return None

	def startDocument (self):
		pass

	def endDocument (self):
		pass

	def startElement (self, name, attrs):
		elem = self.doc.createElement(name)
		for name in attrs.getNames():
			value = attrs.getValue(name)
			elem.setAttribute(name, value)
		self.pushElement(elem)

	def endElement (self, name):
		self.popElement()

	def characters (self, content):
		# Discard completely empty content
		if len(content.strip()) == 0:
			return

		# Append as text node (not pushed to stack)
		if self.getCurElement() != None:
			txt = self.doc.createTextNode(content)
			self.getCurElement().appendChild(txt)

class LogErrorHandler(xml.sax.handler.ErrorHandler):
	def __init__ (self):
		pass

	def error (self, err):
		#print "error(%s)" % str(err)
		pass

	def fatalError (self, err):
		#print "fatalError(%s)" % str(err)
		pass

	def warning (self, warn):
		#print "warning(%s)" % str(warn)
		pass

def findFirstElementByName (nodes, name):
	for node in nodes:
		if node.nodeName == name:
			return node
		chFound = findFirstElementByName(node.childNodes, name)
		if chFound != None:
			return chFound
	return None

# Normalizes potentially broken (due to crash for example) log data to XML element tree
def normalizeToXml (result, doc):
	handler		= BuildXMLLogHandler(doc)
	errHandler	= LogErrorHandler()

	xml.sax.parseString(result.log, handler, errHandler)

	rootNodes = handler.getRootElements()

	# Check if we have TestCaseResult
	testCaseResult = findFirstElementByName(rootNodes, 'TestCaseResult')
	if testCaseResult == None:
		# Create TestCaseResult element
		testCaseResult = doc.createElement('TestCaseResult')
		testCaseResult.setAttribute('CasePath', result.name)
		testCaseResult.setAttribute('CaseType', 'SelfValidate') # \todo [pyry] Not recoverable..
		testCaseResult.setAttribute('Version', LOG_VERSION)
		rootNodes.append(testCaseResult)

	# Check if we have Result element
	resultElem = findFirstElementByName(rootNodes, 'Result')
	if resultElem == None:
		# Create result element
		resultElem = doc.createElement('Result')
		resultElem.setAttribute('StatusCode', result.statusCode)
		resultElem.appendChild(doc.createTextNode(result.statusDetails))
		testCaseResult.appendChild(resultElem)

	return rootNodes

def logToXml (inFile, outFile):
	parser	= BatchResultParser()
	results	= parser.parseFile(inFile)

	dstDoc			= xml.dom.minidom.Document()
	batchResultNode	= dstDoc.createElement('BatchResult')
	batchResultNode.setAttribute("FileName", os.path.basename(inFile))

	dstDoc.appendChild(batchResultNode)

	for result in results:
		# Normalize log to valid XML
		rootNodes = normalizeToXml(result, dstDoc)
		for node in rootNodes:
			batchResultNode.appendChild(node)

	# Summary
	countByStatusCode = {}
	for code in StatusCode.STATUS_CODES:
		countByStatusCode[code] = 0

	for result in results:
		countByStatusCode[result.statusCode] += 1

	summaryElem = dstDoc.createElement('ResultTotals')
	for code in StatusCode.STATUS_CODES:
		summaryElem.setAttribute(code, "%d" % countByStatusCode[code])
	summaryElem.setAttribute('All', "%d" % len(results))
	batchResultNode.appendChild(summaryElem)

	text = dstDoc.toprettyxml()

	out = codecs.open(outFile, "wb", encoding="utf-8")

	# Write custom headers
	out.write("<?xml version=\"1.0\"?>\n")
	out.write("<?xml-stylesheet href=\"%s\" type=\"text/xsl\"?>\n" % STYLESHEET_FILENAME)

	for line in text.splitlines()[1:]:
		out.write(line)
		out.write("\n")

	out.close()

if __name__ == "__main__":
	if len(sys.argv) != 3:
		print "%s: [test log] [dst file]" % sys.argv[0]
		sys.exit(-1)

	logToXml(sys.argv[1], sys.argv[2])