package ca.ucalgary.seahawk.gui.test;

import junit.framework.*;

import ca.ucalgary.seahawk.gui.MobyContentGUI;
import ca.ucalgary.seahawk.services.MobyClient;
import org.biomoby.client.CentralImpl;
import org.biomoby.client.MobyRequest;
import org.biomoby.shared.*;
import org.biomoby.shared.data.*;

public class MobyContentGUITestCase extends TestCase{

    private Central mobyCentral;
    private MobyService mobyService;
    private MobyRequest mobyRequest;
    private MobyDataObject mobyInputData;

    /** The Java property to set to specify what MOBY service should be tested */
    public static String SERVICE_NAME_PROP = "moby.serviceName";

    public static String xmlDocumentFileName = "testdata/Guillardia_theta.xml";
    public static String testNode = "//agave:gene";
    public static String testXPath = ".//agave:classification[@system='GO']//@id";
    public static String[] testMobyTypes = {"GO"};

    public static String simpleServiceName = "GetPubmed";
    public static String inputSimpleDataType = "PMID";
    public static String inputSimpleData = "15031777";

    public static String collectionServiceName = "getSHoundNeighboursFromGi";
    public static String inputCollectionDataType = "NCBI_gi";
    public static String inputCollectionData = "431260";

    /**
     * @param name Test case name.
     */
    public MobyContentGUITestCase(String name) {
        super(name);
    }

    /**
     * Sets up the test fixture.
     * Called before every test case method.
     */
    protected void setUp() {
	mobyRequest = null;
	mobyInputData = null;

	// Use default MOBY registry to find the service
	try{
	    mobyCentral = new CentralImpl();
	} catch(MobyException mobye){
	    fail("Could not find MOBY Service, MOBY Central client failed: " + mobye);
	}
    }

    /**
     * Tears down the test fixture.
     * Called after every test case method.
     */
    protected void tearDown() {
    }

    /**
     * Tries to run a service that returns Simples.  
     */
    public void testCorrectMobyServiceInvocationSimple(){
	findService(new MobyService (simpleServiceName));
	
	mobyInputData = new MobyDataObject(inputSimpleDataType, inputSimpleData);
	
	runService();
    }

    /**
     * Tries to run a service that returns a Collection type.  
     */
    public void testCorrectMobyServiceInvocationCollection(){
	findService(new MobyService (collectionServiceName));
	
	mobyInputData = new MobyDataObject(inputCollectionDataType, inputCollectionData);

	runService();
    }

    /**
     * Tries to use the Bluejay MobyClient to find services based on XPath mapping
     * in a document's contents to MOBY types, then invoke the service on the XPath results.
     */
    public void testCorrectMobyClientBasedInvocation(){
	if(true)return;
	MobyClient client = null;
	try{
	    client = new MobyClient();
	}
	catch(MobyException mobye){
	    mobye.printStackTrace();
	    fail("Could not instantiate MobyClient " + 
		 ", MOBY Central client failed: " + mobye);
	}

	// Add mappings from XML parts (resolved using XPath) to MOBY data types 
	client.addXPathMapping(testXPath, testMobyTypes);	
	//client.addMapping("should_not_match", {"no_type1", "no_type2"});
	
	// Load an XML document
	javax.xml.parsers.DocumentBuilder docBuilder = null;
	try{
            javax.xml.parsers.DocumentBuilderFactory dbf = 
		javax.xml.parsers.DocumentBuilderFactory.newInstance();
	    dbf.setNamespaceAware(true);	
	    //dbf.setNamespaceAware(false);	
	    docBuilder = dbf.newDocumentBuilder();
	}
	catch(javax.xml.parsers.ParserConfigurationException pce){
	    fail("An XML parser could not be found or configured: " + pce);
	}

	org.w3c.dom.Document domDoc = null;
	try{
	    domDoc = docBuilder.parse(new java.io.FileInputStream(xmlDocumentFileName));
	} catch(org.xml.sax.SAXException saxe){
	    fail("The test XML document (" + xmlDocumentFileName + 
		 ") could not be parsed: " + saxe);
	} catch(java.io.IOException ioe){
	    fail("The test XML file (" + xmlDocumentFileName +
		 ") could not be loaded:" + ioe);
	}

	// Select a node from the document and see if any of the mappings
	// work out, in which case we can check what services we can run
	org.w3c.dom.Node context_node = null;
	org.w3c.dom.Node doc_root = domDoc.getDocumentElement();
	Object xobject = null;
	try{
	    xobject = javax.xml.xpath.XPathFactory.newInstance().newXPath().evaluate(testNode,
										     doc_root,
										     javax.xml.xpath.XPathConstants.NODESET);
	    if(xobject == null || !(xobject instanceof org.w3c.dom.NodeList)){
		fail("The returned value from running context node specifier " +
		     "(" + testNode + ") was not a XNodeSet as expected, cannot test MobyClient test" +
		     " without a context node");
	    }
	    org.w3c.dom.NodeList result = (org.w3c.dom.NodeList) xobject;
	    System.out.println("There are " + result.getLength() + 
			       " instances of " + testNode + " in the document");
	    if(result.getLength() == 0){
		fail("The context node specifier XPath (" + testNode + 
		     ") did not return any nodes under " +
		     doc_root + ". Cannot test MobyClient without a context node");
	    }
	    // Take the first node context node of the right type that we find in the document
	    context_node = result.item(0);
	    System.out.println("Context node ID: " + ((org.w3c.dom.Element) context_node).getAttribute("element_id"));
	}catch(javax.xml.xpath.XPathExpressionException xpe){
	    fail("The context node specifier XPath (" + testNode +
		 ") could not be evaluated, cannot test MobyClient without a context: " + xpe);
	}

	//tmp
	try{
	    xobject = javax.xml.xpath.XPathFactory.newInstance().newXPath().evaluate(testXPath, 
										     context_node,
										     javax.xml.xpath.XPathConstants.NODESET);
	    if(xobject == null || !(xobject instanceof org.w3c.dom.NodeList)){
		fail("The returned value from running context node specifier " +
		     "(" + testNode + ") was not a XNodeSet as expected, cannot test MobyClient test" +
		     " without a context node");
	    }
	    org.w3c.dom.NodeList result = (org.w3c.dom.NodeList) xobject;
	    System.out.println("There are " + result.getLength() + 
			       " instances of " + testXPath + " in the context node");
	    if(result.getLength() == 0){
		fail("The MOBY data node specifier XPath (" + testXPath + 
		     ") did not return any nodes under " +
		     context_node + ". Cannot test MobyClient without a MOBY data node");
	    }
	}catch(javax.xml.xpath.XPathExpressionException xpe){
	    fail("The MOBY node specifier XPath (" + testXPath +
		 ") could not be evaluated, cannot test MobyClient without a context: " + xpe);
	}
	//end tmp
	
	MobyDataServiceAssocInstance[] ms = null;
	try{
	    ms = client.getServices(context_node);
	}
	catch(MobyException mobye2){
	    fail("Could not retrieve list of MobyServices from initialized MobyClient using the " +
		 "context node " + context_node + ": " + mobye2);
	}
	assertTrue("Moby client returned 0 services (no mapped XPaths matched " +
		   "for the given context node " + context_node + 
		   " in XML document " + xmlDocumentFileName + ")",
		   ms != null && ms.length != 0);
	System.out.println("Client returned " + ms.length + " services");

	// Take the first mapped value we find, and run it on its first listed available service
	mobyService = ms[0].getServices()[3];
	mobyInputData = (MobyDataObject) ms[0];

	// Once we have the service and input data, try running it
	try{
	    mobyRequest = new MobyRequest(client.getMobyCentralImpl());
	} catch(javax.xml.parsers.ParserConfigurationException pce){
	    fail("Could not construct new instance of MobyRequest due to exception: " +pce);
	}
	
	runService();
    }

    protected void findService(MobyService serviceTemplate){
	MobyService[] ms = null;
 	try{
	    ms = mobyCentral.findService(serviceTemplate);
	} catch(MobyException mobye){
	    fail("Could not find MOBY Service " + serviceTemplate + 
		 ", MOBY Central client failed: " + mobye);
	}
	assertNotNull("Moby Central returned null finding service " + serviceTemplate.getName(), ms);
	assertTrue("Moby Central returned 0 services when searching for service named " +
		   serviceTemplate.getName() , ms.length != 0);
	System.out.println("Moby Central returned " + ms.length + " services with the name " + serviceTemplate.getName());
	mobyService = ms[0];
	assertNotNull("Moby Central returned null for description of service " + serviceTemplate.getName(), (Object) mobyService);
    }

    protected void runService(){
	if(mobyRequest == null){
	    try{
		mobyRequest = new MobyRequest(mobyCentral);
	    } catch(javax.xml.parsers.ParserConfigurationException pce){
		fail("Could not construct new instance of MobyRequest due to exception: " +pce);
	    }
	}

	mobyRequest.setDebugMode(true);
	mobyRequest.setService(mobyService);
	assertEquals("getService did not return the service set earlier", (Object) mobyRequest.getService(), (Object) mobyService);
	
	try{
	    mobyRequest.setInput(mobyInputData);
	}
	catch(MobyException me){
	    fail("Exception while setting service input parameters: " + me);
	}
	assertEquals("getInput did not return the data set earlier", mobyRequest.getInput(), mobyInputData);

	try{
	    mobyRequest.invokeService();
	} catch(MobyException mobye){
	    fail("Failure in MOBY protocol: " + mobye);
	} catch(SOAPException soape){
	    fail("Failure in SOAP transaction: " + soape);
	} catch(NoSuccessException nse){
	    fail("Failure in MOBY logic (input was not acceptable for this service)" + nse);
	} catch(Exception e){
	    fail("Failure: " + e);
	}
	
    }

    /**
     * @return a test suite for all the test methods of this test case.
     */
    public static Test suite() {
        // Reflection is used here to add all
        // the testXXX() methods to the suite.
        //
        //TestSuite suite = new TestSuite(MobyRequestTest.class);

	TestSuite suite = new TestSuite();
	suite.addTest(new MobyContentGUITestCase("testCorrectMobyClientBasedInvocation"));
        return suite;
    }

    /**
     * Runs the test suite when the class is invoked on the command line.
     * The name of the service can be defined through a Java property, 
     * moby.testService
     */
    public static void main(String args[]) throws Exception{
	
        junit.textui.TestRunner.run(suite());
    } 
}
