/*
+----------------------------------------------------------------------+
|                    Class: CLauncher                                  |
|                                                                      |
| Developper:  Antoine Fouille (mbdsys)                                |
|              Eric Gavaldo (XQual)                                    |
| Version:     1.3                                                     |
| License:     LGPL (http://www.gnu.org/licenses/lgpl.html)            |
+----------------------------------------------------------------------+
*/
package com.xqual.xlauncher.bat;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStreamReader;
import java.util.Vector;

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.IConstantsResults;
import com.xqual.xcommon.type.CParam;
import com.xqual.xcommon.type.CParamParsingException;
import com.xqual.xlauncher.CTimeoutListener;

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

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

	static final String TRACE_HEADER = "{bat           }  ";

	// parameters impacting executing at run time set by the test operator
	private String testRootPath;
	private boolean synchronous;
	private int timeout = 600;

	private File workingDir;

	// +==============================================================+
	// | 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");
			synchronous  = getBooleanParamValue("General", "Synchronous executable");
			timeout      = getIntegerParamValue("General", "Asynchronous timeout (in seconds)");
		} 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 scriptParentFolderPath = testRootPath + "/" + testPath + "/";
		workingDir = new File(scriptParentFolderPath);

		// +------------------------------------+
		// | Interpret the script
		// +------------------------------------+
		CRunner exeRunner = new CRunner("[" + testId + "] "+ testPath + ":" + testName + "." + testcaseIndex,
                                        testRootPath + "/" + testPath + "/" + testName + ".bat " +
                                        "/debug " +
                                        "/testcaseIndex=" + testcaseIndex,
                                        workingDir);

		short result = RESULT_FAILURE;
		if (synchronous) {
			result = exeRunner.requestAction(IRunner.START_PROCESS, IRunner.WAIT_END_OF_EXECUTION);
			if (result == RESULT_FAILURE) {
				executionSteps.add(new CExecutionStep(RESULT_FAILURE, "execution failed"));
				return new CReturnStatus(RESULT_FAILURE, executionSteps);
			} else {
				executionSteps.add(new CExecutionStep(RESULT_SUCCESS, "execution succeeded"));
				return new CReturnStatus(RESULT_SUCCESS, executionSteps);
			}
		} else {
			result = exeRunner.requestAction(IRunner.START_PROCESS, IRunner.DO_NOT_WAIT_END_OF_EXECUTION);
			if (result == RESULT_FAILURE) {
				executionSteps.add(new CExecutionStep(RESULT_FAILURE, "execution failed"));
				return new CReturnStatus(RESULT_FAILURE, executionSteps);
			}

			// to check if the execution completed correctly, we need to check if the "test_completed.txt" has been created
		    File testCompletedFile = new File(workingDir + "/test_completed.txt");
		    short resultTimeout = CTimeoutListener.waitForFile(testCompletedFile, timeout);
			if (resultTimeout != RESULT_SUCCESS) {
				executionSteps.add(new CExecutionStep(RESULT_FAILURE, "timeout of " + timeout + " seconds to execute the test case expired"));
				return new CReturnStatus(RESULT_FAILURE, executionSteps);
			}

		    // file exists, remove it to complete asyncronous handshake
		    if (!testCompletedFile.delete())
		       executionSteps.add(new CExecutionStep(RESULT_UNKNOWN, "Failed to cleanup test_completed.txt"));

			return parseResultFile(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(Vector<CExecutionStep> executionSteps) {
		// parse the result file to get the result and the execution steps
		File resultFile = new File(workingDir + "/log.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, message="";
		boolean errorDetected = false;

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

			while ((line = bufferedReader.readLine()) != null) {
				line = line.trim();
				System.out.println(">" + line);
				if (line.indexOf("[Success]")>=0) {
					try {message = line.substring(10, line.length());} catch (Exception e) {} // [Success] length = 9
					executionSteps.add(new CExecutionStep(RESULT_SUCCESS, message));

				} else if (line.indexOf("[Failure]")>=0) {
					try {message = line.substring(10, line.length());} catch (Exception e) {}
					executionSteps.add(new CExecutionStep(RESULT_FAILURE, message));
					errorDetected = true;

				} else if (line.indexOf("[Log]")>=0) {
					try {message = line.substring(6, line.length());} catch (Exception e) {}
					executionSteps.add(new CExecutionStep(RESULT_UNKNOWN, message));

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

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

