/* Copyright (c) 2009 Peter Troshin
 *  
 *  JAva Bioinformatics Analysis Web Services (JABAWS) @version: 1.0     
 * 
 *  This library is free software; you can redistribute it and/or modify it under the terms of the
 *  Apache License version 2 as published by the Apache Software Foundation
 * 
 *  This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without
 *  even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the Apache 
 *  License for more details.
 * 
 *  A copy of the license is in apache_license.txt. It is also available here:
 * @see: http://www.apache.org/licenses/LICENSE-2.0.txt
 * 
 * Any republication or derived work distributed in source code form
 * must include this copyright and license notice.
 */

package compbio.ws.client;

import java.io.ByteArrayInputStream;
import java.io.Closeable;
import java.io.IOException;
import java.util.Arrays;
import java.util.List;

import javax.xml.ws.WebServiceException;

import compbio.data.msa.MsaWS;
import compbio.data.sequence.Alignment;
import compbio.data.sequence.FastaSequence;
import compbio.data.sequence.SequenceUtil;
import compbio.metadata.JobStatus;
import compbio.metadata.JobSubmissionException;
import compbio.metadata.Limit;
import compbio.metadata.LimitsManager;
import compbio.metadata.Preset;
import compbio.metadata.PresetManager;
import compbio.metadata.ResultNotAvailableException;
import compbio.metadata.RunnerConfig;
import compbio.metadata.UnsupportedRuntimeException;
import compbio.metadata.WrongParameterException;

/**
 * Class for testing web services
 * 
 * @author pvtroshin
 * 
 * @version 1.0 February 2010
 */
public class WSTester {

	/**
	 * Sequences to be used as input for all WS
	 */
	static final String fastaInput = ">Foobar\n"
			+ "MTADGPRELLQLRAAVRHRPQDFVAWLMLADAELGMGDTTAGEMAVQRGLALHPGHPEAVARLGRVRWTQQRHAEAAVLLQQASDAAPEHPGIALWLGHALEDAGQAEAAAAAYTRAHQLLPEEPYITAQLLNWRRRLCDWRALDVLSAQVRAAVAQGVGAVEPFAFLSEDASAAEQLACARTRAQAIAASVRPLAPTRVRSKGPLRVGFVSNGFGAHPTGLLTVALFEALQRRQPDLQMHLFATSGDDGSTLRTRLAQASTLHDVTALGHLATAKHIRHHGIDLLFDLRGWGGGGRPEVFALRPAPVQVNWLAYPGTSGAPWMDYVLGDAFALPPALEPFYSEHVLRLQGAFQPSDTSRVVAEPPSRTQCGLPEQGVVLCCFNNSYKLNPQSMARMLAVLREVPDSVLWLLSGPGEADARLRAFAHAQGVDAQRLVFMPKLPHPQYLARYRHADLFLDTHPYNAHTTASDALWTGCPVLTTPGETFAARVAGSLNHHLGLDEMNVADDAAFVAKA"
			+ "\n>Barbar\n"
			+ "ASDAAPEHPGIALWLHALEDAGQAEAAAAYTRAHQLLPEEPYITAQLLNAVAQGVGAVEPFAFLSEDASAAESVRPLAPTRVRSKGPLRVGFVSNGFGAHPTGLLTVALFEALQRRQPDLQMHLFATSGDDGSTLRTRLAQASTLHDVTALGHLATAKHIRHHGIDLLFDLRGWGGGGRPEVFALRPAPVQVNWLAYPGTSGAPWMDYVLGDAFALPPALEPFYSEHVLRLQGAFQPSDTSRVVAEPPSRTQCGLPEQGVVLCCFNNSYKLNPQSMARMLAVLREVPDSVLWLLSGPGEADARLRAFAHAQGVDAQRLVFMPKLPHPQYLARYRHADLFLDTHPYNAHTTASDALWTGCPVLTTPGETFAARVAGSLNHHLGLDEMNVADDAAFVAKAVALASDPAALTALHARVDVLRRESGVFEMDGFADDFGALLQALARRHGWLGI"
			+ "\n>Foofriend\n"
			+ "MTADGPRELLQLRAAVRHRPQDVAWLMLADAELGMGDTTAGEMAVQRGLALHPGHPEAVARLGRVRWTQQRHAEAAVLLQQASDAAPEHPGIALWLGHALEDHQLLPEEPYITAQLDVLSAQVRAAVAQGVGAVEPFAFLSEDASAAEQLACARTRAQAIAASVRPLAPTRVRSKGPLRVGFVSNGFGAHPTGLLTVALFEALQRRQPDLQMHLFATSGDDGSTLRTRLAQASTLHDVTALGHLATAKHIRHHGIDLLFDLRGWGGGGRPEVFALRPAPVQVNWLAYPGTSGAPWMDYVLGDAFALPPALEPFYSEHVLRLQGAFQPSDTSRVVAEPPSRTQCGLPEQGVVLCCFNNSYKLNPQSMARMLAVLREVPDSVLWLLSGPGEADARLRAFAHAQGVDAQRLVFMPKLPHPQYLARYRHADLFLDTHPYNAHTTASDALWTGCPVLTTPGETFAARVAGSLNHHLGLDEMNVADDAAFVAKAVALASDPAALTALHARVDVLRRESI\n";

	static final List<FastaSequence> seqs = loadSeqs();

	private static final String FAILED = "FAILED";
	private static final String OK = "OK";

	/**
	 * Converting input to a form accepted by WS
	 * 
	 * @return List of FastaSequence records
	 */
	static List<FastaSequence> loadSeqs() {
		try {
			return SequenceUtil.readFasta(new ByteArrayInputStream(fastaInput
					.getBytes()));
		} catch (IOException ignored) {
			// Should not happen as a source is not a external stream
			ignored.printStackTrace();
		}
		return null;
	}

	/**
	 * Prints usage
	 */
	static void printUsage() {
		System.out.println("Usage: <Class or Jar file name> "
				+ Jws2Client.hostkey + Jws2Client.pseparator
				+ "host_and_context " + "<" + Jws2Client.servicekey
				+ Jws2Client.pseparator + "serviceName>");
		System.out.println();
		System.out
				.println(Jws2Client.hostkey
						+ Jws2Client.pseparator
						+ "<host_and_context> - a full URL to the JABAWS web server including context path e.g. http://10.31.1.159:8080/ws");
		System.out
				.println(Jws2Client.servicekey
						+ Jws2Client.pseparator
						+ "<ServiceName> - optional if unspecified all services are tested otherwise one of "
						+ Arrays.toString(Services.values()));
		System.out.println();

	}

	/**
	 * Calls alignment with preset
	 * 
	 * @param <T>
	 * @param msaws
	 * @param presets
	 *            list of the Preset
	 * @throws UnsupportedRuntimeException
	 */
	static <T> boolean presetAlign(MsaWS<T> msaws, List<Preset<T>> presets)
			throws UnsupportedRuntimeException {
		boolean succeed = false;
		for (Preset<T> preset : presets) {
			System.out.print("Aligning with preset '" + preset.getName()
					+ "'... ");
			Alignment al = null;
			try {
				String taskId = msaws.presetAlign(seqs, preset);
				al = msaws.getResult(taskId);
				if (al != null) {
					System.out.println(OK);
				}
				succeed = true;
			} catch (UnsupportedRuntimeException e) {
				System.out.println(FAILED);
				// If executable is not supported than none of the presets are
				// going to work
				throw new UnsupportedRuntimeException(e);
			} catch (JobSubmissionException e) {
				// TODO custom message
				System.out.println(FAILED);
				System.out.println();
				e.printStackTrace();
				continue;
			} catch (WrongParameterException e) {
				// TODO custom message
				System.out.println(FAILED);
				System.out.println();
				e.printStackTrace();
				continue;
			} catch (ResultNotAvailableException e) {
				// TODO custom message
				System.out.println(FAILED);
				System.out.println();
				e.printStackTrace();
				continue;
			}
		}
		return succeed;
	}

	/**
	 * Call most of web services functions and check the output
	 * 
	 * @param <T>
	 *            web service type
	 * @param msaws
	 * @throws UnsupportedRuntimeException
	 *             is thrown if the connection to a web service was made, but
	 *             the web service is not functional. e.g. when native
	 *             executable does not exists for a server platform
	 */
	static <T> boolean checkService(MsaWS<T> msaws)
			throws UnsupportedRuntimeException {

		boolean succeed = testDefaultAlignment(msaws);

		// If exception above is thrown than the tests below is not run

		PresetManager<T> pmanager = msaws.getPresets();
		if (pmanager != null && pmanager.getPresets().size() > 0) {
			System.out.println("Testing alignment with presets:");
			List<Preset<T>> plist = pmanager.getPresets();
			succeed = !succeed ? presetAlign(msaws, plist) : succeed;
		}

		System.out.print("Querying presets...");

		if (pmanager != null && pmanager.getPresets().size() > 0) {
			System.out.println(OK);
		} else {
			System.out.println("UNSUPPORTED");
		}

		System.out.print("Querying Parameters...");
		RunnerConfig<T> options = msaws.getRunnerOptions();
		if (options != null && options.getArguments().size() > 0) {
			System.out.println(OK);
		} else {
			System.out.println(FAILED);
			succeed = false;
		}

		System.out.print("Querying Limits...");
		LimitsManager<T> limits = msaws.getLimits();
		if (limits != null && limits.getLimits().size() > 0) {
			System.out.println(OK);
		} else {
			System.out.println("UNSUPPORTED");
		}

		System.out.print("Querying Local Engine Limits...");
		Limit<T> localLimit = msaws
				.getLimit(PresetManager.LOCAL_ENGINE_LIMIT_PRESET);
		if (localLimit != null) {
			System.out.println(OK);
		} else {
			System.out.println("UNSUPPORTED");
		}
		return succeed;
	}

	/**
	 * Align using default settings
	 * 
	 * @param <T>
	 * @param msaws
	 * @throws UnsupportedRuntimeException
	 */
	static <T> boolean testDefaultAlignment(MsaWS<T> msaws)
			throws UnsupportedRuntimeException {
		System.out.print("Testing alignment with default parameters:");
		Alignment al = null;
		boolean succeed = false;
		try {
			String taskId = msaws.align(seqs);
			System.out.print("\nQuerying job status...");
			JobStatus status = msaws.getJobStatus(taskId);
			while (status != JobStatus.FINISHED) {
				Thread.sleep(1000);
				status = msaws.getJobStatus(taskId);
			}
			System.out.println(OK);
			System.out.print("Retrieving results...");
			al = msaws.getResult(taskId);
			succeed = true;
		} catch (ResultNotAvailableException e) {
			System.out.println(FAILED);
			e.printStackTrace();
		} catch (JobSubmissionException e) {
			System.out.println(FAILED);
			System.out.println();
			e.printStackTrace();
		} catch (InterruptedException e) {
			System.out.println(FAILED);
			System.out.println();
			e.printStackTrace();
		}
		if (al != null) {
			System.out.println(OK);
		}
		return succeed;
	}

	/**
	 * Connect to a WS using the host and the service name
	 * 
	 * @param <T>
	 * @param host
	 * @param service
	 * @return
	 */
	static <T> MsaWS<T> connect(String host, Services service) {
		MsaWS<T> msaws = null;
		try {
			System.out.print("Connecting to service " + service + " on " + host
					+ " ... ");
			msaws = Jws2Client.connect(host, service);
			System.out.println(OK);
		} catch (WebServiceException e) {
			System.out.println(FAILED);
		}
		return msaws;
	}

	/**
	 * Test JWS2 web services
	 * 
	 * @param <T>
	 *            web service type
	 * @param args
	 *            -h=<Your web application server host name, port and JWS2
	 *            context path>
	 * 
	 *            -s=<ServiceName> which is optional. If service name is not
	 *            provided then all known JWS2 web services are tested
	 * @throws IOException
	 */
	public static <T> void main(String[] args) throws IOException {

		if (args == null || args.length < 1) {
			printUsage();
			System.exit(0);
		}
		String host = Jws2Client.getHost(args);
		String serviceName = Jws2Client.getServiceName(args);
		if (!Jws2Client.validURL(host)) {
			System.out
					.println("<host_and_context> parameter is not provided or is incorrect!");
			System.exit(1);
		}
		boolean succeed = false;
		MsaWS<T> msaws = null;
		if (serviceName != null) {
			Services service = Services.getService(serviceName);
			if (service == null) {
				System.out.println("Service '" + serviceName
						+ "' is not supported. Valid values are: "
						+ Arrays.toString(Services.values()));
				System.out.println();
				printUsage();
				System.exit(1);
			}

			msaws = connect(host, service);
			if (msaws == null) {
				System.exit(1);
			}
			try {
				succeed = checkService(msaws);
			} catch (UnsupportedRuntimeException ignored) {
				System.exit(1);
			} finally {
				((Closeable) msaws).close();
			}
			reportResults(service, succeed);
			System.exit(0);
		}

		System.out
				.println("<ServiceName> is not provided checking all known services...");

		for (Services serv : Services.values()) {
			System.out.println();
			msaws = connect(host, serv);
			if (msaws == null) {
				continue;
			}
			try {
				succeed = checkService(msaws);
			} catch (UnsupportedRuntimeException ignored) {
				System.out.println("Service " + serv + " IS NOT FUNCTIONAL");
				continue;
			} finally {
				((Closeable) msaws).close();
			}
			reportResults(serv, succeed);
		}

	}

	private static void reportResults(Services serv, boolean succeed) {
		if (succeed) {
			System.out.println("Check is completed. The Service " + serv
					+ " IS WORKING");
		} else {
			System.out.println("Check is completed. The Service " + serv
					+ " HAS SOME PROBLEMS");
		}
	}
}
