/*
+----------------------------------------------------------------------+
|                    Class: CLauncher                                  |
|                                                                      |
| Developper:  Eric Gavaldo (eric.gavaldo@xqual.com)                   |
|              Michel                                                  |
| Version:     1.0                                                     |
| License:     LGPL (http://www.gnu.org/licenses/lgpl.html)            |
+----------------------------------------------------------------------+
*/
package com.xqual.xlauncher.xunit_dotnet;

import java.io.File;
import java.io.IOException;
import java.util.Vector;

import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

import com.xqual.xagent.launcher.CExecutionStep;
import com.xqual.xagent.launcher.CLauncher;
import com.xqual.xagent.launcher.CReturnStatus;
import com.xqual.xagent.launcher.runner.CRunner;
import com.xqual.xagent.launcher.runner.IRunner;
import com.xqual.xcommon.CAttribute;
import com.xqual.xcommon.CXmlDocumentFactory;
import com.xqual.xcommon.IConstantsResults;
import com.xqual.xcommon.type.CParam;
import com.xqual.xcommon.type.CParamParsingException;
import com.xqual.xcommon.utils.CFileUtils;
import com.xqual.xcommon.utils.CUtils;

/**
 * The <code>CLauncherImpl</code> implementation of <code>ILauncher</code> for NUnit.
 * @author egavaldo
 */
public class CLauncherImpl extends CLauncher implements IConstantsResults {

	// +==============================================================+
	// | Attributes                                                   |
	// +==============================================================+

	static final String TRACE_HEADER = "{xunit.net     }  ";

	// parameters impacting executing at run time set by the test operator
	private String testRootPath;
	private String xunitConsolePath;
	private String platformType;
	private String clrVersion;

	private String xunitInterpreter;

	private File workingDir;

	private static final String XUNIT_CONSOLE_32_RUNNER   = CUtils.getExecutableName("xunit.console.x86"); // useful only when running 32bits tests on a 64bits computer
	private static final String XUNIT_CONSOLE_64_RUNNER   = CUtils.getExecutableName("xunit.console");
	private static final String XUNIT_CONSOLE_CLR4_32_RUNNER   = CUtils.getExecutableName("xunit.console.clr4.x86");
	private static final String XUNIT_CONSOLE_CLR4_64_RUNNER   = CUtils.getExecutableName("xunit.console.clr4");
	
	private static final String XUNIT_TRACE_FILENAME      = "xunit_traces.txt";
	private static final String XUNIT_REPORT_XML_FILENAME = "xunit_report.xml";
	
	// +==============================================================+
	// | Constructors                                                 |
	// +==============================================================+

	public CLauncherImpl() {
		super(TRACE_HEADER);
	}

	// +==============================================================+
	// | Methods                                                      |
	// +==============================================================+

	@Override
	public CReturnStatus initialize(int sutId, String sutName, String sutVersion) {
		setSutDetails(sutId, sutName, sutVersion);

		// check the configuration sent by the manager
		printConfiguration();

		Vector<CExecutionStep> executionSteps = new Vector<CExecutionStep>();
		try {
			// retrieve the parameters we need

			// section related to the SUT, independent from xUnit
			testRootPath      = getStringParamValue("General", "Test root path");        // i.e. C:/test_repository/tests/xunit

			// section related to xUnit
			xunitConsolePath  = getStringParamValue("xUnit.net", "xUnit console path");  // i.e. Y:/Externals/xunit-1.7
			platformType      = getStringParamValue("xUnit.net", "Platform type");       // i.e. 32 bits
			clrVersion        = getStringParamValue("xUnit.net", "CLR version");           // i.e. CLR 2

			// select the proper xUnit console executable 
			xunitInterpreter = xunitConsolePath + "/";
			if (platformType.equalsIgnoreCase("32 bits") && clrVersion.equalsIgnoreCase("CLR 2")) {
				xunitInterpreter += XUNIT_CONSOLE_32_RUNNER;
			} else if (platformType.equalsIgnoreCase("64 bits") && clrVersion.equalsIgnoreCase("CLR 2")) {
				xunitInterpreter += XUNIT_CONSOLE_64_RUNNER;
			} else if (platformType.equalsIgnoreCase("32 bits") && clrVersion.equalsIgnoreCase("CLR 4")) {
				xunitInterpreter += XUNIT_CONSOLE_CLR4_32_RUNNER;
			} else if (platformType.equalsIgnoreCase("64 bits") && clrVersion.equalsIgnoreCase("CLR 4")) {
				xunitInterpreter += XUNIT_CONSOLE_CLR4_64_RUNNER;
			}
			
		} catch (CParamParsingException e) {
			traceln(LOG_PRIORITY_SEVERE, "parsing error during initialization");
			executionSteps.add(new CExecutionStep(RESULT_FAILURE, "Exception during initialize: " + e.getMessage()));
			return new CReturnStatus(RESULT_FAILURE, executionSteps);
		}
		return new CReturnStatus(RESULT_SUCCESS, executionSteps);
	}

	@Override
	public CReturnStatus preRun(int testId, String testPath, String testName, Vector<CAttribute> attributes, String additionalInfo) {
		traceln(LOG_PRIORITY_INFO, "preRun testId=" + testId + " testPath=" + testPath + " [" + testName + "]...");
		Vector<CExecutionStep> executionSteps = new Vector<CExecutionStep>();
		return new CReturnStatus(RESULT_SUCCESS, executionSteps);
	}

	@Override
	public CReturnStatus run(int testId, String testPath, String testName, int testcaseId, int testcaseIndex, String testcaseName, Vector<CParam> params, String additionalInfo) {
		traceln(LOG_PRIORITY_INFO, "run testId=" + testId + " testPath=" + testRootPath + "/" + testPath + "/" + testName + " testcaseIndex=" + testcaseIndex + "...");
		Vector<CExecutionStep> executionSteps = new Vector<CExecutionStep>();

		workingDir = new File(xunitConsolePath);

		// +------------------------------------+
		// | Interpret the script
		// +------------------------------------+
		File xUnitTraceFile = new File(testRootPath + "/" + testPath + "/" + XUNIT_TRACE_FILENAME);
		if (xUnitTraceFile.exists())
			xUnitTraceFile.delete();
		try {
			xUnitTraceFile.createNewFile();
		} catch (IOException e) {
			traceln(LOG_PRIORITY_SEVERE, "Could not create file \"" + XUNIT_TRACE_FILENAME + "\"...");
		}

		File xmlReportFile = new File(testRootPath + "/" + testPath + "/" + XUNIT_REPORT_XML_FILENAME);
		if (xmlReportFile.exists())
			xmlReportFile.delete();

		
		// +------------------------------------+
		// | Run the xUnit test
		// +------------------------------------+
		// "Y:/Externals/xunit-1.7-beta/xunit.console" "C:/test_repository/tests/xunit/xUnitTests/bin/Debug/xUnitTests.dll"
		//  <------------- xunitInterpreter ---------> <------- testRootPath --------> <------ path ------> <- name ->
		// TODO: use the xml report instead of parsing the console
		CRunner xunitRunner = new CRunner("[" + testId + "] "+ testPath + "." + testName,
		                                   CFileUtils.quoteFilePath(xunitInterpreter) + " " +
		                                   CFileUtils.quoteFilePath(testRootPath + "/" + testPath + "/" + testName + ".dll") + " " +
		                                   "/nunit " + xmlReportFile.getAbsolutePath(),
										   workingDir);
		// here we redirect the output to a file for later parsing
		/*short result = */xunitRunner.requestAction(IRunner.START_PROCESS, IRunner.WAIT_END_OF_EXECUTION, xUnitTraceFile);

		// +------------------------------------+
		// | Add the output console to the test
		// +------------------------------------+
		addAttachment(xUnitTraceFile);
		addAttachment(xmlReportFile);

		/*if (result == RESULT_FAILURE) {
			executionSteps.add(new CExecutionStep(RESULT_FAILURE, "execution process failed"));
			parseResultFile(xmlReportFile, executionSteps); // to have the details anyway
			return new CReturnStatus(RESULT_FAILURE, executionSteps);
		} else {
			executionSteps.add(new CExecutionStep(RESULT_SUCCESS, "execution process succeeded"));
			return parseResultFile(xmlReportFile, executionSteps);
		}*/
		return parseResultFile(xmlReportFile, executionSteps);
	}

	@Override
	public CReturnStatus postRun(int testId, String testPath, String testName) {
		traceln(LOG_PRIORITY_INFO, "postRun testId=" + testId + " testPath=" + testPath + ":" + testName + "...");
		Vector<CExecutionStep> executionSteps = new Vector<CExecutionStep>();
		executionSteps.add(new CExecutionStep(RESULT_SUCCESS, "postRun: succeeded"));
		return new CReturnStatus(RESULT_SUCCESS, executionSteps);
	}

	@Override
	public CReturnStatus terminate() {
		Vector<CExecutionStep> executionSteps = new Vector<CExecutionStep>();
		executionSteps.add(new CExecutionStep(RESULT_SUCCESS, "Terminate"));
		return new CReturnStatus(RESULT_SUCCESS, executionSteps);
	}

	// +--------------------------+
	// ¦        Utilities         ¦
	// +--------------------------+

	private CReturnStatus parseResultFile(File xmlReportFile, Vector<CExecutionStep> executionSteps) {
		Document xmlDocument = CXmlDocumentFactory.createXMLDoc(xmlReportFile);
		
		boolean errorDetected = false;
		
		NodeList testSuiteNodes = CXmlDocumentFactory.getNodeObjListFromXPath(xmlDocument, "//results/test-suite");
		System.err.println("- - - - - nb testsuites: " + testSuiteNodes.getLength());
		for (int i = 0; i < testSuiteNodes.getLength(); i++) {
            Node testSuiteNode = testSuiteNodes.item(i);
            
            String testSuiteName   = testSuiteNode.getAttributes().getNamedItem("name").getNodeValue();    // i.e. "xUnitTests.Test.TestSuite1"
            String testSuiteStatus = testSuiteNode.getAttributes().getNamedItem("success").getNodeValue(); // i.e. "True"
            if (!testSuiteStatus.equalsIgnoreCase("true")) errorDetected = true;
            
            executionSteps.add(new CExecutionStep(testSuiteStatus.equalsIgnoreCase("true") ? RESULT_SUCCESS : RESULT_FAILURE, "[Testsuite: " + testSuiteName + "]"));
            
            NodeList testcaseNodes = CXmlDocumentFactory.getNodeObjListFromXPath(testSuiteNode, "results/test-case");
            System.err.println("- - - - - nb testcases: " + testcaseNodes.getLength());
            for (int j = 0; j < testcaseNodes.getLength(); j++) {
            	Node testcaseNode = testcaseNodes.item(j);

            	String testcaseName     = testcaseNode.getAttributes().getNamedItem("name").getNodeValue();      // i.e. "xUnitTests.Test.TestSuite1.Test1Addition"
            	String testcaseExecuted = testcaseNode.getAttributes().getNamedItem("executed").getNodeValue();  // i.e. "True"
                String testcaseStatus   = testcaseNode.getAttributes().getNamedItem("success").getNodeValue();   // i.e. "True"
            	String testcaseTime     = testcaseNode.getAttributes().getNamedItem("time").getNodeValue();      // i.e. "0.004"
            	if (!testcaseStatus.equalsIgnoreCase("true")) errorDetected = true;
            	
                executionSteps.add(new CExecutionStep(testcaseStatus.equalsIgnoreCase("true") ? RESULT_SUCCESS : RESULT_FAILURE, " - '" + testcaseName + "' executed: " + testcaseExecuted + " time: " + testcaseTime));  
            }
		}

		return new CReturnStatus(errorDetected ? RESULT_FAILURE : RESULT_SUCCESS, executionSteps);
	}
	
	
	
	/*
	private CReturnStatus parseResultFile(File xmlReportFile, Vector<CExecutionStep> executionSteps) {
		// parse the result file to get the result and the execution steps
		File resultFile = new File("xunit_traces.txt");
		if (!resultFile.exists()) {
			traceln(LOG_PRIORITY_SEVERE, "Result file not found!");
			executionSteps.add(new CExecutionStep(RESULT_FAILURE, "run: result file not found!"));
			return new CReturnStatus(RESULT_FAILURE, executionSteps);
		} else {
			executionSteps.add(new CExecutionStep(RESULT_SUCCESS, "run: result file found"));
		}

		String line;
		boolean errorDetected = false;
		boolean notRunDetected = false;

		try {
			FileInputStream fileInputStream = new FileInputStream(resultFile);
			BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(fileInputStream));

			while ((line = bufferedReader.readLine()) != null) {
				line = line.trim();
				System.out.println(">" + line);
				if (line.startsWith("Total tests:")) {
					String[] array = line.split(",");

					int totalCount = getCountFromBuffer(array[0]);
					int failureCount = getCountFromBuffer(array[1]);
					int skippedCount = getCountFromBuffer(array[2]);

					executionSteps.add(new CExecutionStep(RESULT_UNKNOWN, "Total tests: " + totalCount));
					executionSteps.add(new CExecutionStep(failureCount > 0 ? RESULT_FAILURE: RESULT_SUCCESS, "Failures: " + failureCount));
					executionSteps.add(new CExecutionStep(RESULT_UNKNOWN, "Skipped: " + skippedCount));

					if (failureCount+skippedCount > 0) {
						executionSteps.add(new CExecutionStep(RESULT_FAILURE, line));
						errorDetected = true;
					} else {
						executionSteps.add(new CExecutionStep(RESULT_SUCCESS, line));
					}

				} else {
					//traceln(LOG_PRIORITY_SEVERE, "unknown tag!");
				}
			}

			bufferedReader.close();
			fileInputStream.close();
		} catch (Exception e) {
			traceln(LOG_PRIORITY_SEVERE, "exception whle parsing the result file: " + e);
			executionSteps.add(new CExecutionStep(RESULT_FAILURE, "Exception while parsing the result file: " + e));
			errorDetected = true;
		}

		if (errorDetected) {
			return new CReturnStatus(RESULT_FAILURE, executionSteps);
		} else if (notRunDetected) {
			return new CReturnStatus(RESULT_UNKNOWN, executionSteps);
		} else{
			return new CReturnStatus(RESULT_SUCCESS, executionSteps);
		}
	}

	private int getCountFromBuffer(String input) {
		String[] array = input.split(":");
		return Integer.parseInt(array[1].trim());
	}
	*/

}

