/*
+----------------------------------------------------------------------+
|                    Class: CLauncher                                  |
|                                                                      |
| Developper:  Yossi Kimron                                            |
| Version:     1.3                                                     |
| License:     LGPL (http://www.gnu.org/licenses/lgpl.html)            |
+----------------------------------------------------------------------+
 */
package com.xqual.xlauncher.java;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStreamReader;
import java.lang.reflect.Method;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
import java.text.SimpleDateFormat;
import java.util.Date;
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.xcommon.CAttribute;
import com.xqual.xcommon.IConstantsResults;
import com.xqual.xcommon.type.CParam;
import com.xqual.xcommon.type.CParamParsingException;

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

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

	static final String TRACE_HEADER = "{java          }  ";

	// parameters impacting executing at run time set by the test operator
	private String testRootPath;
	private URLClassLoader classLoader;

	Vector<CAttribute> attributes = new Vector<CAttribute>();

	// +==============================================================+
	// | 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
		} 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);
		}

		// add the test root path to the classpath
		URL[] classPathUrls = new URL[1];
        try {
			classPathUrls[0] = new File(testRootPath).toURI().toURL();
			classLoader = new URLClassLoader(classPathUrls);
		} catch (MalformedURLException e) {
			traceln(LOG_PRIORITY_SEVERE, "couldn't add the test root path to the classpath");
			executionSteps.add(new CExecutionStep(RESULT_FAILURE, "Exception during classpath modification: " + 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 + "]...");
		this.attributes = attributes;

		printAttributes(attributes);

		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, "preparing to instanciate class " + testName + "...");

		// +-------------------------------------------------------------------+
		// | Load and invoke the requested class using the adhoc class loader  |
		// +-------------------------------------------------------------------+
		Vector<CExecutionStep> executionSteps = new Vector<CExecutionStep>();

		try {
			traceln(LOG_PRIORITY_INFO, "looking for class " + testPath + "." + testName + "...");
			Class testClass = Class.forName(testPath + "." + testName, true, classLoader);

			Method methodlist[] = testClass.getDeclaredMethods();
	        for (int i = 0; i < methodlist.length; i++) {
	            Method method = methodlist[i];
	            traceln(LOG_PRIORITY_INFO, "methodlist[" + i + "]: " + method.getName());
	            /*
	            Class parameterList[] = method.getParameterTypes();
	            for (int j = 0; j < parameterList.length; j++)
	            	traceln(LOG_PRIORITY_INFO, " - parameter[" + j + "] " + parameterList[j]);

	            Class exceptionList[] = method.getExceptionTypes();
	            for (int j = 0; j < exceptionList.length; j++)
	            	traceln(LOG_PRIORITY_INFO, " - exception[" + j + "]: " + exceptionList[j]);

	            traceln(LOG_PRIORITY_INFO, " - return type: " + method.getReturnType());
	            */
	        }

	        // WE SHOULD PROBABLY CHECK THAT THE TESTCASE EXIST BY COMPARING WITH THIS INSTEAD OF TRYING TO EXECUTE IT

			// Sorry for this below line
			@SuppressWarnings("unchecked")

			Method mainMethod = testClass.getMethod("main", new Class[] {Object[].class});
			Object[] mainArguments = new Object[] {testId, testRootPath, testPath, testName, testcaseIndex, testcaseName, attributes, additionalInfo};

			// execute the main() method
			mainMethod.invoke(null, new Object[] {mainArguments});
			traceln(LOG_PRIORITY_INFO, "main() invokation returned");

		} catch (ClassNotFoundException e) {
			traceln(LOG_PRIORITY_SEVERE, "ERROR loading class file: " + e.toString());
			executionSteps.add(new CExecutionStep(RESULT_FAILURE, "Class Not Found Exception"));

		} catch (NoSuchMethodException e) {
			traceln(LOG_PRIORITY_SEVERE, "main method doesn't exists: " + e.toString());
			executionSteps.add(new CExecutionStep(RESULT_FAILURE, "No such Method Exception"));

		} catch (IllegalAccessException e) {
			traceln(LOG_PRIORITY_SEVERE, "Illegal access exception: " + e.toString());
			executionSteps.add(new CExecutionStep(RESULT_FAILURE, "Illegal access Exception"));

		} catch (Exception e) {
			traceln(LOG_PRIORITY_SEVERE, "Exception: " + e.toString());
			executionSteps.add(new CExecutionStep(RESULT_FAILURE, "Exception: " + e.toString()));
			e.printStackTrace();
		}

		// at this point, the method invokation returned so it should be completed and teh result file should be present
		String resultFilename = String.format("%s_%s.txt", testName, testcaseIndex); // format generated by the test itself
		File testParentfolder = new File(testRootPath + "/" + testPath.replace('.', '/') + "/");
		File resultFile = new File(testParentfolder + "/" + resultFilename);

		return parseResultFile(executionSteps, resultFile);
	}

	@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, File resultFile) {

		String line, message, timestamp;

		SimpleDateFormat SDF = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
		timestamp = SDF.format(new Date());

		if (!resultFile.exists()) {
			traceln(LOG_PRIORITY_SEVERE, "Result file not found!");
			executionSteps.add(new CExecutionStep(timestamp, RESULT_FAILURE, "run: result file not found!"));
			return new CReturnStatus(RESULT_FAILURE, executionSteps);
		} else {
			executionSteps.add(new CExecutionStep(timestamp, RESULT_SUCCESS, "run: result file found"));
		}

		boolean errorDetected = 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);

				// Requested format: 2010-01-23 19:00:09 [Success] Any message...
				if (line.indexOf("[Success]") >= 0) {
					timestamp = line.substring(0, line.indexOf("[Success]"));
					message = line.substring(line.indexOf("[Success]") + 10, line.length());
					executionSteps.add(new CExecutionStep(timestamp, RESULT_SUCCESS, message));

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

				} else if (line.indexOf("[Log]") >= 0) {
					timestamp = line.substring(0, line.indexOf("[Log]"));
					message = line.substring(line.indexOf("[Log]") + 6, line.length());
					executionSteps.add(new CExecutionStep(timestamp, 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);
		}
	}
}
