package ca.ucalgary.services.util.test;

import ca.ucalgary.services.util.WSDLConfig;

import junit.framework.*;

import javax.xml.namespace.QName;
import javax.xml.transform.Source;
import javax.xml.ws.*;

import java.net.URL;
import java.util.Map;

public class WSDLConfigTestCase extends TestCase{
    private final static String WSDL_RESOURCE_RPC = "ca/ucalgary/services/util/test/KEGG.wsdl";
    private final static String WSDL_RESOURCE_DOCLIT = "ca/ucalgary/services/util/test/OntologyQuery.wsdl";

    private final static int NUM_SERVICES = 1;
    private final static String SERVICE_NAME = "getGenesByECNumber";
    private final static String SERVICE_CATEGORY = "Retrieval";
    private final static String SERVICE_AUTHORITY = "genome.jp";
    private final static String SERVICE_DESC = "Retrieves the list of genes in a given organism that " +
	                                       "are annotated as having a particular EC (enzyme) number";
    private final static String SERVICE_CONTACT = "gordonp@ucalgary.ca";

    private final static String OP_NAME = "get_genes_by_enzyme";
    private final static String OP_INPUT_NAME = "get_genes_by_enzymeRequest";
    private final static String OP_OUTPUT_NAME = "get_genes_by_enzymeResponse";

    private final static String SERVICE_NAME_LIT = "ebi-ols-getTermByID";
    private final static String OP_NAME_LIT = "getTermById";
    private final static String OP_INPUT_NAME_LIT = "getTermById";
    private final static String OP_OUTPUT_NAME_LIT = "getTermByIdResponse";

    private final static int NUM_INPUTS = 1;
    private final static String INPUT_NAME1 = "enzyme_id";
    private final static String INPUT_TYPE1 = "enzyme_id:Object:EC";
    private final static String INPUT_XSDTYPE1 = "xsd:string";
    private final static String INPUT_SCHEMA_MAPPING1 = "urn:lsid:bioxml.info:mobyLoweringSchemaMapping:EC2ecPrefixedString";
    private final static int NUM_SECONDARY_INPUTS = 1;
    private final static String INPUT_SECONDARY_NAME1 = "org";
    private final static String INPUT_SECONDARY_TYPE1 = "org:String:[^:]+ - \\S+:\\[.*\\]";  //form of the secondary param
    private final static String INPUT_SECONDARY_XSDTYPE1 = "xsd:string";
    private final static String INPUT_SECONDARY_SCHEMA_MAPPING1 = "urn:lsid:bioxml.info:mobyLoweringSchemaMapping:KeggDefSecondaryString2String";

    private final static int NUM_OUTPUTS = 1;
    private final static String OUTPUT_NAME1 = "return";
    private final static String OUTPUT_TYPE1 = "return:Collection(Object):KEGG_GENES";  //should be collection due to XSD array type of return
    private final static String OUTPUT_XSDTYPE1 = "typens:ArrayOfstring";
    private final static String OUTPUT_SCHEMA_MAPPING1 = "urn:lsid:bioxml.info:mobyLiftingSchemaMapping:string2KEGG_GENES";

    // RPC/Encoded service test
    private final static String SOAP_SERVICE_NAME = "KEGG";
    private final static String SOAP_SERVICE_URI = "SOAP/KEGG";
    private final static String SOAP_PORT_NAME = "KEGGPort";
    private final static String SOAP_PORT_URI = "SOAP/KEGG";

    // Document/Literal service test
    private final static String SOAP_SERVICE_NAME_LIT = "QueryService";
    private final static String SOAP_SERVICE_URI_LIT = "http://www.ebi.ac.uk/ontology-lookup/OntologyQuery";
    private final static String SOAP_PORT_NAME_LIT = "OntologyQuery";
    private final static String SOAP_PORT_URI_LIT = "http://www.ebi.ac.uk/ontology-lookup/OntologyQuery";

    public WSDLConfigTestCase(String name){
	super(name);
    }

    /**
     * See if the xhtml and moby spec class values are properly parsed
     */
    public void testParsingBasic(){
	loadWSDL(WSDL_RESOURCE_RPC);
    }

    private WSDLConfig loadWSDL(String resource){
	URL u = getClass().getClassLoader().getResource(resource);
	assertNotNull("Could not find the test WSDL resource ("+resource+")", u);

	WSDLConfig wsdl = null;
	try{
	    wsdl = new WSDLConfig(u);
	} catch(Exception e){
	    e.printStackTrace();
	    fail("Could not load and parse the test input resource ("+u+"): " + e);
	}

	return wsdl;
    }

    public void testMetadata(){
	WSDLConfig wsdl = loadWSDL(WSDL_RESOURCE_RPC);

	String[] serviceNames = wsdl.getServiceNames();
	assertNotNull("WSDLConfig returned null instead of an array of service names for test WSDL file", 
		      serviceNames);
	assertTrue("The number of Moby services (" + serviceNames.length + 
		   ") in the test WSDL file was not the expected number (" + NUM_SERVICES + ")",
		   serviceNames.length == NUM_SERVICES);

	wsdl.setCurrentService(SERVICE_NAME);
	assertTrue("The service category '" + wsdl.getServiceType() + "' for service " + 
		   SERVICE_NAME + " in the test WSDL was not '" + SERVICE_CATEGORY + "' as expected", 
		   SERVICE_CATEGORY.equals(wsdl.getServiceType()));
	assertTrue("The service authority '" + wsdl.getProviderURI() + "' for service " + 
		   SERVICE_NAME + " in the test WSDL was not '" + SERVICE_AUTHORITY + "' as expected", 
		   SERVICE_AUTHORITY.equals(wsdl.getProviderURI()));
	assertTrue("The service description '" + wsdl.getServiceDesc() + "' for service " + 
		   SERVICE_NAME + " in the test WSDL was not '" + SERVICE_DESC + "' as expected", 
		   SERVICE_DESC.equals(wsdl.getServiceDesc()));
	assertTrue("The service contact '" + wsdl.getContactEmail() + "' for service " + 
		   SERVICE_NAME + " in the test WSDL was not '" + SERVICE_CONTACT + "' as expected", 
		   SERVICE_CONTACT.equals(wsdl.getContactEmail()));
    }
    
    public void testIOSpecsRPCEncoded(){
	WSDLConfig wsdl = loadWSDL(WSDL_RESOURCE_RPC);

	wsdl.setCurrentService(SERVICE_NAME);
	assertTrue("The SOAP operation name '" + wsdl.getOperationName() + "' for service " + 
		   SERVICE_NAME + " in the test WSDL was not '" + OP_NAME + "' as expected", 
		   OP_NAME.equals(wsdl.getOperationName()));
	assertTrue("The SOAP operation input name '" + wsdl.getOperationInputQName().getLocalPart() + "' for service " + 
		   SERVICE_NAME + " in the test WSDL was not '" + OP_INPUT_NAME + "' as expected", 
		   OP_INPUT_NAME.equals(wsdl.getOperationInputQName().getLocalPart()));
	assertTrue("The SOAP operation output name '" + wsdl.getOperationOutputQName().getLocalPart() + "' for service " + 
		   SERVICE_NAME + " in the test WSDL was not '" + OP_OUTPUT_NAME + "' as expected", 
		   OP_OUTPUT_NAME.equals(wsdl.getOperationOutputQName().getLocalPart()));

	Map<String,String> inputs = wsdl.getPrimaryInputs();
	assertNotNull("The input specification parsed from the test WSDL was unexpectedly null", inputs);
	assertTrue("The number of SOAP operation inputs (" + inputs.size() + 
		   " in the test WSDL was not " + NUM_INPUTS + " as expected ",
		   NUM_INPUTS == inputs.size());
	assertTrue("The expected input parameter " + INPUT_NAME1 + 
		   " does not exist in the parsed test WSDL input spec", 
		   inputs.containsKey(INPUT_NAME1));
	assertTrue("The expected type (" + INPUT_TYPE1 + ") for input parameter " + INPUT_NAME1 +
		   " was not found, but rather: " + inputs.get(INPUT_NAME1),
		   INPUT_TYPE1.equals(inputs.get(INPUT_NAME1)));
	
	Map<String,String> inputs2 = wsdl.getSecondaryInputs();
	assertNotNull("The secondary input specification parsed from the test WSDL was unexpectedly null", 
		      inputs2);
	assertTrue("The number of SOAP operation inputs (" + inputs2.size() + 
		   " in the test WSDL was not " + NUM_SECONDARY_INPUTS + " as expected ",
		   NUM_SECONDARY_INPUTS == inputs2.size());
	assertTrue("The expected secondary input parameter " + INPUT_SECONDARY_NAME1 + 
		   " does not exist in the parsed test WSDL input spec", 
		   inputs2.containsKey(INPUT_SECONDARY_NAME1));
	assertTrue("The expected pattern match (" + INPUT_SECONDARY_TYPE1 + ") for secondary input parameter " + 
		   INPUT_SECONDARY_NAME1 +
		   " was not found, but rather the value is: " + inputs2.get(INPUT_SECONDARY_NAME1),
		   inputs2.get(INPUT_SECONDARY_NAME1) != null &&
		   inputs2.get(INPUT_SECONDARY_NAME1).matches(INPUT_SECONDARY_TYPE1));

	Map<String,String> inputFormats = wsdl.getPrimaryInputFormats();
	assertNotNull("The input schema mapping specification parsed from the test WSDL " +
		      "was unexpectedly null", inputFormats);
	assertTrue("The number of SOAP operation input schema mappings (" + inputFormats.size() + 
		   " in the test WSDL was not " + NUM_INPUTS + " as expected ",
		   NUM_INPUTS == inputFormats.size());
	assertTrue("The expected input parameter " + INPUT_NAME1 + 
		   " schema mapping does not exist in the parsed test WSDL input spec", 
		   inputFormats.containsKey(INPUT_NAME1));
	assertTrue("The expected schema mapping LSID " + INPUT_SCHEMA_MAPPING1 + 
		   " for input parameter " + INPUT_NAME1 + " was not found, but rather: " +
		   inputFormats.get(INPUT_NAME1),
		   INPUT_SCHEMA_MAPPING1.equals(inputFormats.get(INPUT_NAME1)));

	Map<String,String> inputXSDTypes = wsdl.getInputXSDTypes();
	assertNotNull("The input XSD-type specification parsed from the test WSDL was unexpectedly null", inputXSDTypes);
	assertTrue("The number of SOAP operation input XSD-type specs (" + inputXSDTypes.size() + 
		   " in the test WSDL was not " + NUM_INPUTS + " as expected ",
		   NUM_INPUTS == inputXSDTypes.size());
	assertTrue("The expected input parameter " + INPUT_NAME1 + 
		   " XSD-type spec does not exist in the parsed test WSDL input spec", 
		   inputXSDTypes.containsKey(INPUT_NAME1));
	assertTrue("The expected XSD-type spec " + INPUT_XSDTYPE1 + 
		   " for input parameter " + INPUT_NAME1 + " was not found, but rather: " +
		   inputXSDTypes.get(INPUT_NAME1),
		   INPUT_XSDTYPE1.equals(inputXSDTypes.get(INPUT_NAME1)));

	Map<String,String> outputs = wsdl.getPrimaryOutputs();
	assertNotNull("The output specification parsed from the test WSDL was unexpectedly null", outputs);
	assertTrue("The number of SOAP operation output schema mappings (" + outputs.size() + 
		   " in the test WSDL was not " + NUM_OUTPUTS + " as expected ",
		   NUM_OUTPUTS == outputs.size());
	assertTrue("The expected output parameter " + OUTPUT_NAME1 + 
		   " does not exist in the parsed test WSDL output spec", 
		   outputs.containsKey(OUTPUT_NAME1));
	assertTrue("The expected type (" + OUTPUT_TYPE1 + ") for output parameter " + OUTPUT_NAME1 +
		   " was not found, but rather: " + outputs.get(OUTPUT_NAME1),
		   OUTPUT_TYPE1.equals(outputs.get(OUTPUT_NAME1)));

	Map<String,String> outputFormats = wsdl.getPrimaryOutputFormats();
	assertNotNull("The output schema mapping specification parsed from the test WSDL was unexpectedly null", outputFormats);
	assertTrue("The number of SOAP operation output schema mappings (" + outputFormats.size() + 
		   " in the test WSDL was not " + NUM_OUTPUTS + " as expected ",
		   NUM_OUTPUTS == outputFormats.size());
	assertTrue("The expected schema mapping LSID " + OUTPUT_SCHEMA_MAPPING1 + 
		   " for output parameter " + OUTPUT_NAME1 + " was not found, but rather: " +
		   outputFormats.get(OUTPUT_NAME1),
		   OUTPUT_SCHEMA_MAPPING1.equals(outputFormats.get(OUTPUT_NAME1)));

	Map<String,String> outputXSDTypes = wsdl.getOutputXSDTypes();
	assertNotNull("The output XSD-type specification parsed from the test WSDL was unexpectedly null", outputXSDTypes);
	assertTrue("The number of SOAP operation output XSD-type specs (" + outputXSDTypes.size() + 
		   " in the test WSDL was not " + NUM_OUTPUTS + " as expected ",
		   NUM_OUTPUTS == outputXSDTypes.size());
	assertTrue("The expected output parameter " + OUTPUT_NAME1 + 
		   " XSD-type spec does not exist in the parsed test WSDL output spec", 
		   outputXSDTypes.containsKey(OUTPUT_NAME1));
	assertTrue("The expected XSD-type spec " + OUTPUT_XSDTYPE1 + 
		   " for output parameter " + OUTPUT_NAME1 + " was not found, but rather: " +
		   outputXSDTypes.get(OUTPUT_NAME1),
		   OUTPUT_XSDTYPE1.equals(outputXSDTypes.get(OUTPUT_NAME1)));    

	QName serviceQName = wsdl.getServiceQName();
	assertNotNull("The service QName was unexpectedly null", serviceQName);
	assertTrue("The local name of the service (" + serviceQName.getLocalPart() +
		   ") was not the expected value (" + SOAP_SERVICE_NAME + ")",
		   SOAP_SERVICE_NAME.equals(serviceQName.getLocalPart()));
	assertTrue("The namespace URI of the service (" + serviceQName.getNamespaceURI() +
		   ") was not the expected value (" + SOAP_SERVICE_URI + ")",
		   SOAP_SERVICE_URI.equals(serviceQName.getNamespaceURI()));
	QName portQName = wsdl.getPortQName();
	assertNotNull("The service port QName was unexpectedly null", portQName);
	assertTrue("The local name of the service port (" + portQName.getLocalPart() +
		   ") was not the expected value (" + SOAP_PORT_NAME + ")",
		   SOAP_PORT_NAME.equals(portQName.getLocalPart()));
	assertTrue("The namespace URI of the service port (" + portQName.getNamespaceURI() +
		   ") was not the expected value (" + SOAP_PORT_URI + ")",
		   SOAP_PORT_URI.equals(portQName.getNamespaceURI()));

	// Verify if the service info was actually parsed properly by trying to use it with JAX-WS
	Service service = null;
	try{
	    service = Service.create(getClass().getClassLoader().getResource(WSDL_RESOURCE_RPC), 
				     serviceQName);
	} catch(Exception e){
	    e.printStackTrace();
	    fail(e.getClass().getName() + " while using JAX-WS to create a handle for" +
		 "the service, either the WSDL or the expected serviceQName is wrong");
	}

	try{
	    Dispatch<Source> dispatch = service.createDispatch(portQName,
							       Source.class,
							       Service.Mode.PAYLOAD);
	} catch(Exception e){
	    e.printStackTrace();
	    fail(e.getClass().getName() + " while using JAX-WS to create a dispatch a port on " +
		 "the service " + serviceQName + ", either the WSDL or the expected portQName is wrong");
	}
    }
    
    public void testIOSpecsDocumentLiteral(){
	WSDLConfig wsdl = loadWSDL(WSDL_RESOURCE_DOCLIT);

	
	wsdl.setCurrentService(SERVICE_NAME_LIT);
	assertTrue("The SOAP operation name '" + wsdl.getOperationName() + "' for service " + 
		   SERVICE_NAME_LIT + " in the test WSDL was not '" + OP_NAME_LIT + "' as expected", 
		   OP_NAME_LIT.equals(wsdl.getOperationName()));
	assertTrue("The SOAP operation input name '" + wsdl.getOperationInputQName().getLocalPart() + "' for service " + 
		   SERVICE_NAME_LIT + " in the test WSDL was not '" + OP_INPUT_NAME_LIT + "' as expected", 
		   OP_INPUT_NAME_LIT.equals(wsdl.getOperationInputQName().getLocalPart()));
	assertTrue("The SOAP operation output name '" + wsdl.getOperationOutputQName().getLocalPart() + "' for service " + 
		   SERVICE_NAME_LIT + " in the test WSDL was not '" + OP_OUTPUT_NAME_LIT + "' as expected", 
		   OP_OUTPUT_NAME_LIT.equals(wsdl.getOperationOutputQName().getLocalPart()));

	QName serviceQName = wsdl.getServiceQName();
	assertNotNull("The service QName was unexpectedly null", serviceQName);
	assertTrue("The local name of the service (" + serviceQName.getLocalPart() +
		   ") was not the expected value (" + SOAP_SERVICE_NAME_LIT + ")",
		   SOAP_SERVICE_NAME_LIT.equals(serviceQName.getLocalPart()));
	assertTrue("The namespace URI of the service (" + serviceQName.getNamespaceURI() +
		   ") was not the expected value (" + SOAP_SERVICE_URI_LIT + ")",
		   SOAP_SERVICE_URI_LIT.equals(serviceQName.getNamespaceURI()));
	QName portQName = wsdl.getPortQName();
	assertNotNull("The service port QName was unexpectedly null", portQName);
	assertTrue("The local name of the service port (" + portQName.getLocalPart() +
		   ") was not the expected value (" + SOAP_PORT_NAME_LIT + ")",
		   SOAP_PORT_NAME_LIT.equals(portQName.getLocalPart()));
	assertTrue("The namespace URI of the service port (" + portQName.getNamespaceURI() +
		   ") was not the expected value (" + SOAP_PORT_URI_LIT + ")",
		   SOAP_PORT_URI_LIT.equals(portQName.getNamespaceURI()));


	// Verify if the service info was actually parsed properly by trying to use it with JAX-WS
	Service service = null;
	try{
	    service = Service.create(getClass().getClassLoader().getResource(WSDL_RESOURCE_DOCLIT), 
				     serviceQName);
	} catch(Exception e){
	    e.printStackTrace();
	    fail(e.getClass().getName() + " while using JAX-WS to create a handle for" +
		 "the service, either the WSDL or the expected serviceQName is wrong");
	}

	try{
	    Dispatch<Source> dispatch = service.createDispatch(portQName,
							       Source.class,
							       Service.Mode.PAYLOAD);
	} catch(Exception e){
	    e.printStackTrace();
	    fail(e.getClass().getName() + " while using JAX-WS to create a dispatch a port on " +
		 "the service " + serviceQName + ", either the WSDL or the expected portQName is wrong");
	}
    }

    /**
     * @return a test suite for all the test methods of this test case.
     */
    public static Test suite() {

	TestSuite suite = new TestSuite();
 	suite.addTest(new WSDLConfigTestCase("testParsingBasic"));
 	suite.addTest(new WSDLConfigTestCase("testMetadata"));
 	suite.addTest(new WSDLConfigTestCase("testIOSpecsRPCEncoded"));
 	suite.addTest(new WSDLConfigTestCase("testIOSpecsDocumentLiteral"));
	
        return suite;
    }

    public static void main(String[] args){
	junit.textui.TestRunner.run(suite());
    }
}
