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

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.Writer;
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;

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

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

	static final String TRACE_HEADER = "{qtp           }  ";

	// parameters impacting executing at run time set by the test operator
	private String testRootPath;
	private String wscriptExePath;
	
	private String resultPrefix;
	private boolean qtpVisible;
	private String qtpWindowState;
	private String qtpView;
	private boolean qtpQuit;

	// +==============================================================+
	// | 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
			testRootPath   = getStringParamValue("General", "Test root path");              // i.e. Y:/XStudio/build/tmp/eclipse_classes
			wscriptExePath = getStringParamValue("General", "Windows cscript.exe path");    // i.e. C:/WINDOWS/system32/cscript.exe

			resultPrefix   = getStringParamValue("QTP", "Results folders name prefix");     // i.e. Res
			qtpVisible     = getBooleanParamValue("QTP", "QTP Visible");                     // i.e. true
			qtpWindowState = getStringParamValue("QTP", "QTP window state");                // i.e. Maximized
			qtpView        = getStringParamValue("QTP", "QTP View");                        // i.e. ExpertView
			qtpQuit        = getBooleanParamValue("QTP", "Quit QTP after test");             // i.e. true
		} 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>();

    	String vbScriptPath = testRootPath + "/" + testPath + "/runTest_" + testName  + ".vbs";
		
		
		// +------------------------------------+
		// | Generate the VB script
		// +------------------------------------+
		StringBuffer outputBuffer = new StringBuffer();
		
		outputBuffer.append("Dim qtApp 'As QuickTest.Application\n");
		outputBuffer.append("Dim qtTest 'As QuickTest.Test\n");
		outputBuffer.append("Set qtApp = CreateObject(\"QuickTest.Application\")\n"); 
		outputBuffer.append("qtApp.Launch\n");
		outputBuffer.append("qtApp.Visible = " + (qtpVisible ? "True" : "False") + "\n");	
		outputBuffer.append("qtApp.WindowState = \"" + qtpWindowState + "\"\n");   
		outputBuffer.append("qtApp.ActivateView \"" + qtpView + "\"\n"); 
		outputBuffer.append("qtApp.Open \"" + testRootPath + "/" + testPath + "/" + testName + "\", True\n"); 
		outputBuffer.append("Set qtTest = qtApp.Test\n");
		outputBuffer.append("qtTest.Run\n"); 
		outputBuffer.append("WScript.StdOut.Write \"Status is: \" & qtTest.LastRunResults.Status\n"); 
		outputBuffer.append("qtTest.Close\n");
		if (qtpQuit) outputBuffer.append("qtApp.quit\n"); 
		outputBuffer.append("Set qtResultsOpt = Nothing\n"); 
		outputBuffer.append("Set qtTest = Nothing\n");
		outputBuffer.append("Set qtApp = Nothing\n");
	
		
		File vbScriptFile = new File(vbScriptPath);
		if (vbScriptFile.exists()) vbScriptFile.delete();
		try {
			vbScriptFile.createNewFile();
		    Writer output = new BufferedWriter(new FileWriter(vbScriptFile));
		    output.write(outputBuffer.toString());
		    output.close();
		} catch (IOException e) {
			traceln(LOG_PRIORITY_SEVERE, "Could not create file \"" + vbScriptFile + "\"");
			executionSteps.add(new CExecutionStep(RESULT_FAILURE, "Could not create file \"" + vbScriptFile + "\""));
			return new CReturnStatus(RESULT_FAILURE, executionSteps);
		}
		traceln(LOG_PRIORITY_INFO, vbScriptFile + " has been generated");

		
		// +------------------------------------+
		// | Run it with test name as argument
		// +------------------------------------+
		File qtpExecutionTraces = new File("qtp_traces.txt");
		if (qtpExecutionTraces.exists())
			qtpExecutionTraces.delete();
		try {
			qtpExecutionTraces.createNewFile();
		} catch (IOException e) {
			traceln(LOG_PRIORITY_SEVERE, "Could not create file \"qtp_traces.txt\"...");
		}
		
		CRunner qtpRunner = new CRunner("[" + testId + "] "+ testPath + "." + testName,
		                                wscriptExePath.toString() + " \"" + vbScriptPath + "\"");

		// here we redirect the output to a file for later parsing
		short result = qtpRunner.requestAction(IRunner.START_PROCESS, IRunner.WAIT_END_OF_EXECUTION, qtpExecutionTraces);

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

		if (result == RESULT_FAILURE) {
			executionSteps.add(new CExecutionStep(RESULT_FAILURE, "execution process failed"));
		} else {
			executionSteps.add(new CExecutionStep(RESULT_SUCCESS, "execution process succeeded"));
		}
		
		return parseResultFile(new File(testRootPath + "/" + testPath + "/" + testName), executionSteps); // to have the details anyway
	}

	@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 testFolder, Vector<CExecutionStep> executionSteps) {
		traceln(LOG_PRIORITY_INFO, "parsing result folder " + testFolder + "...");

		short result = RESULT_FAILURE;

	    int nbWarning = 0;
		int nbFails   = 0;
		int nbSuccess = 0;
		
		if (testFolder == null || !testFolder.exists() || !testFolder.isDirectory()) {
			traceln(LOG_PRIORITY_INFO, "couldn't find the folder " + testFolder + "...");
			executionSteps.add(new CExecutionStep(RESULT_FAILURE, "couldn't find the folder"));
			return new CReturnStatus(RESULT_FAILURE, executionSteps);
		}
		
		File[] childFolders = testFolder.listFiles();
		if (childFolders == null) {
			traceln(LOG_PRIORITY_INFO, "empty test folder " + testFolder);
			executionSteps.add(new CExecutionStep(RESULT_FAILURE, "empty test folder"));
			return new CReturnStatus(RESULT_FAILURE, executionSteps);			
		}
		int index = -1;
		for (File folder: childFolders) {
			String folderName = folder.getName();
			if (folder.isDirectory() && folderName.startsWith(resultPrefix)) {
				traceln(LOG_PRIORITY_INFO, "found a results folder candidate: " + folder);
				try {
					int currentIndex = Integer.parseInt(folderName.substring(resultPrefix.length()));
					
					// check anyway if there is a results.xml file in this folder
					File resultsFile = new File(testFolder.getAbsolutePath() + "/" + resultPrefix + currentIndex + "/Report/Results.xml");
					if (resultsFile.exists()) {
						index = Math.max(index, currentIndex);
					}
				} catch (NumberFormatException e) {
					traceln(LOG_PRIORITY_INFO, "the rest of the name is not an integer: " + folderName);
				}
			}
		}
		if (index == -1) {
			traceln(LOG_PRIORITY_INFO, "couldn't find appropriate result folder");
			executionSteps.add(new CExecutionStep(RESULT_FAILURE, "couldn't find appropriate result folder"));
			return new CReturnStatus(RESULT_FAILURE, executionSteps);				
		}
		
		File latestResultsFolder = new File(testFolder.getAbsolutePath() + "/" + resultPrefix + index);
		traceln(LOG_PRIORITY_INFO, "latest results folder found: " + latestResultsFolder);
		
		// search potential HTML reports un resx
		File[] htmlFiles = latestResultsFolder.listFiles();
		if (htmlFiles != null) {
			for (File htmlFile: htmlFiles) {
				//System.err.println(" extension = " + CFileUtils.getExtension(htmlFile));
				String extension = CFileUtils.getExtension(htmlFile);
				if (extension != null && extension.equalsIgnoreCase("html")) {
			    	traceln(LOG_PRIORITY_INFO, "attaching html report " + htmlFile + "...");
			    	addAttachment(htmlFile);				
				}
			}
		}
		
		
		// parse the result file to get the result and the execution steps
		File resultsFile = new File(testFolder.getAbsolutePath() + "/" + resultPrefix + index + "/Report/Results.xml");
		addAttachment(resultsFile);
		traceln(LOG_PRIORITY_INFO, "reading " + resultsFile + "...");
	    Document xmlDocument = CXmlDocumentFactory.createXMLDoc(resultsFile);
	    
	    // attach all the excel sheets used for this test
	    NodeList tableNodeList = CXmlDocumentFactory.getNodeObjListFromXPath(xmlDocument, "//NodeArgs[@eType='Table']/BtmPane[@vType='Table']/Path");
	    for (int i=0; i<tableNodeList.getLength(); i++) {
	    	Node resultNode = tableNodeList.item(i);
	    	String tableName = resultNode.getFirstChild().getNodeValue();
	    	File tableFile = new File(testFolder.getAbsolutePath() + "/" + resultPrefix + index + "/Report/" + tableName);
	    	traceln(LOG_PRIORITY_INFO, "attaching excel sheet " + tableFile + "...");
	    	addAttachment(tableFile);
	    }
	    
	    
	    
	    NodeList iterationNodeList = CXmlDocumentFactory.getNodeObjListFromXPath(xmlDocument, "//DIter");
	    for (int j=0; j<iterationNodeList.getLength(); j++) {
	    	Node iterationNode = iterationNodeList.item(j);
	    	
	    	String iterationIndex = iterationNode.getAttributes().getNamedItem("iterID").getNodeValue();
	    	//System.err.println("============ Iteration " + iterationIndex + " =============");
	    	
			NodeList resultNodeList = CXmlDocumentFactory.getNodeObjListFromXPath(iterationNode, "Action/Step/NodeArgs[@eType='Replay' or @eType='Context' or @eType='User']");
		    for (int k=0; k<resultNodeList.getLength(); k++) {
		    	String type   = "";
		    	String status = "";
		    	String time   = "";
		    	String desc   = "";
		    	
		    	Node resultNode = resultNodeList.item(k);
		    	
		    	try {type = CXmlDocumentFactory.getNodeStringValueFromXPath(resultNode, "@eType");} catch (Exception e) {}
		    	
		    	try {status = CXmlDocumentFactory.getNodeStringValueFromXPath(resultNode, "@status");} catch (Exception e) {}
		    	
		    	Node timeNode = CXmlDocumentFactory.getNodeFromXPath(resultNode, "preceding-sibling::Time[1]");
		    	try {time   = timeNode.getFirstChild().getNodeValue();} catch (Exception e) {}
		    		
		    	Node descNode = CXmlDocumentFactory.getNodeFromXPath(resultNode, "Disp");
		    	try {desc   = descNode.getFirstChild().getNodeValue();} catch (Exception e) {}
		    	
		    	//System.err.println("- Result: " + status + " : " + type + " : " + desc);
		    	
		    	if (status.equals("Passed")) {
		        	executionSteps.add(new CExecutionStep(time, RESULT_SUCCESS, "#" + iterationIndex + " " + type + " " + desc));
		        	nbSuccess++;

		    	} else if (status.equals("Failed")) {
		        	executionSteps.add(new CExecutionStep(time, RESULT_FAILURE, "#" + iterationIndex + " " + type + " " + desc));
		        	nbFails++;

		    	} else if (status.equals("Warning")) {
		        	executionSteps.add(new CExecutionStep(time, RESULT_RELATIVE, "#" + iterationIndex + " " + type + " " + desc));
		        	nbWarning++;

		    	} else { // status.equals("Done") or something else
		    		executionSteps.add(new CExecutionStep(time, RESULT_NOT_EXECUTED, "#" + iterationIndex + " " + type + " " + desc));
		    	}
		    }	    	
	    }
	    
      result = RESULT_RELATIVE;	
	    if (nbFails == 0 && nbWarning == 0 && nbSuccess > 0) {
	    	result = RESULT_SUCCESS;
	    } else if (nbFails > 0) {
	    	result = RESULT_FAILURE;
	    } 
	    
		
		return new CReturnStatus(result, executionSteps);
	}

	
	public static void main(String[] args) {
		CLauncherImpl launcher = new CLauncherImpl();
		launcher.resultPrefix = "Res";
		Vector<CExecutionStep> executionSteps = new Vector<CExecutionStep>();
		
		launcher.parseResultFile(new File("C:/test_repository/tests/qtp/test2"), executionSteps);
		
		System.out.println(executionSteps);
	}
	
}

