// MobyParserTest.java
//
// Created: February 2008
//
// Copyright 2008 Martin Senger
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//    http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//

package org.biomoby.shared.parser;

import org.biomoby.shared.MobyException;
import org.biomoby.shared.data.MobyProvisionInfo;
import org.biomoby.shared.datatypes.*;

import org.junit.Before;
import org.junit.Test;
import static org.junit.Assert.*;

import org.apache.commons.io.IOUtils;
import org.apache.commons.io.FileUtils;
import org.apache.commons.lang.StringUtils;

import java.io.InputStream;
import java.io.IOException;
import java.util.Map;
import java.util.HashMap;
import java.util.Arrays;


public class MobyParserTest {

    private static final String INPUT_1     = "parser-test-input-1.xml";
    private static final String INPUT_2     = "parser-test-input-2.xml";
    private static final String INPUT_3     = "parser-test-input-3.xml";
    private static final String INPUT_4     = "parser-test-input-4.xml";
    private static final String INPUT_XREFS = "parser-test-input-xrefs.xml";
    private static final String INPUT_SET   = "parser-test-input-set.xml";
 
    /**************************************************************************
     *
     *                              Tests
     *
     **************************************************************************/

    @Test
    public void testNormal()
	throws IOException, MobyException {
	MobyPackage moby = parse (INPUT_1, null, null);
	assertEquals ("freetext notes here", moby.getServiceNotes());
	assertEquals ("http://\"me\".<org>&", moby.getAuthority());
	MobyJob[] jobs = moby.getJobs();
	assertEquals (4, jobs.length);
	assertEquals ("a1", jobs[0].getId());
	MobyObject data = jobs[0].getData();
	assertEquals ("NucleotideSequence", data.getMobyTypeName());
	assertEquals ("", data.getName());
	assertEquals ("DragonDB_Allele", data.getNamespace());
	assertEquals ("chlorae", data.getId());
	assertFalse (data.isPrimitiveType());
	assertEquals ("org.biomoby.shared.datatypes.NucleotideSequence", data.getClass().getName());
	NucleotideSequence typedData = (NucleotideSequence)jobs[0].getData();
	assertEquals (">tata\ntatata", typedData.get_SequenceString());
	assertEquals (">tata\ntatata", typedData.getMoby_SequenceString().getValue());
	assertEquals (10, typedData.getMoby_Length().getIntValue());
    }

     @Test
     public void testUnknownTopLevel()
 	throws IOException, MobyException {
	MobyPackage moby = parse (INPUT_2, "DNASequence", null);
	DNASequence typedData = (DNASequence)moby.getJob(0).getData ("myDNA");
	assertEquals ("abcd", typedData.get_SequenceString());
	assertEquals (12, typedData.getMoby_Length().getIntValue());
     }

    @Test
    public void testUnknownTopLevelAndMember()
	throws IOException, MobyException {
	MobyPackage moby = parse (INPUT_3, "BasicGFF3MultiFeature", null);
	BasicGFF3MultiFeature typedData = (BasicGFF3MultiFeature)moby.getJob(0).getData ("myPairs");
	BasicGFF3SequenceFeature[] data = typedData.getMoby_BasicGFF3SequenceFeature();
	assertEquals (1, data.length);
	assertEquals (1, data[0].getMoby_start().getIntValue());
	assertEquals (12.34, data[0].getMoby_score().getFloatValue(), 0.01);
	assertEquals ("b", data[0].get_phase());
	multi_key_value_pair[] pairs = data[0].getMoby_column9_tag_value();
	assertEquals (2, pairs.length);
	assertEquals ("key1", pairs[0].get_key());
	assertEquals (3, pairs[0].get_the_value().length);
	assertEquals ("value1", pairs[0].get_the_value()[0]);
	assertEquals ("key2", pairs[1].get_key());
	assertEquals (1, pairs[1].get_the_value().length);
	assertEquals ("valueA", pairs[1].get_the_value()[0]);
    }

    @Test
    public void testMoreUnknownTopLevels()
	throws IOException, MobyException {
	Map<String,String> backups = new HashMap<String,String>();
	backups.put ("mySeq",   "DNASequence");
	backups.put ("myPairs", "BasicGFF3MultiFeature");
	MobyPackage moby = parse (INPUT_4, null, backups);
	BasicGFF3MultiFeature typedData = (BasicGFF3MultiFeature)moby.getJob(0).getData ("myPairs");
	BasicGFF3SequenceFeature[] data = typedData.getMoby_BasicGFF3SequenceFeature();
	assertEquals (1, data.length);
	assertEquals (1, data[0].getMoby_start().getIntValue());
	assertEquals (12.34, data[0].getMoby_score().getFloatValue(), 0.01);
	assertEquals ("b", data[0].get_phase());
	multi_key_value_pair[] pairs = data[0].getMoby_column9_tag_value();
	assertEquals (1, pairs.length);
	assertEquals ("key1", pairs[0].get_key());
	assertEquals (1, pairs[0].get_the_value().length);
	assertEquals ("value1", pairs[0].get_the_value()[0]);
	DNASequence seqData = (DNASequence)moby.getJob(0).getData ("mySeq");
	assertEquals ("xyz", seqData.get_SequenceString());
	assertEquals (123, seqData.getMoby_Length().getIntValue());
    }

    @Test
    public void testSetWithSubstitution()
	throws IOException, MobyException {
 	Map<String,String> backups = new HashMap<String,String>();
 	backups.put ("mySequenceCollection", "GenericSequence");
	MobyPackage moby = parse (INPUT_SET, null, backups);
	MobyObject[] data = moby.getJob(0).getDataSet ("mySequenceCollection");
	assertEquals ("DNASequence", data[0].getMobyTypeName());
	assertEquals ("GenericSequence", data[1].getMobyTypeName());
	GenericSequence typedData = (GenericSequence)data[0];
	assertEquals ("tatatatata", typedData.get_SequenceString());
	assertEquals (10, typedData.getMoby_Length().getIntValue());
	typedData = (GenericSequence)data[1];
	assertEquals ("", typedData.get_SequenceString());
	assertEquals (5, typedData.getMoby_Length().getIntValue());
    }

    @Test
    public void testXref()
	throws IOException, MobyException {
	MobyPackage moby = parse (INPUT_XREFS, null, null);
	assertEquals ("This is a value", moby.getJob(0).getData().getValue().trim());
	MobyXref[] xrefs = moby.getJob(0).getData().getXrefs();
	assertEquals (3, xrefs.length);
	assertTrue  (xrefs[0].isSimpleXref());
	assertTrue  (xrefs[1].isSimpleXref());
	assertFalse (xrefs[2].isSimpleXref());
	assertEquals ("At263644", xrefs[0].getId());
	assertEquals ("TIGR", xrefs[0].getNamespace());
	assertEquals ("yes", xrefs[2].getId());
	assertEquals ("LION", xrefs[2].getNamespace());
	assertEquals ("IEA", xrefs[2].getEvidenceCode());

	MobyProvisionInfo pi = moby.getJob(0).getData().getProvision();
	assertEquals ("InterPro", pi.getSoftwareName());
	assertEquals ("1.2", pi.getSoftwareVersion());
	assertEquals ("HMMER's not run", pi.getSoftwareComment());
	assertEquals ("Genbank/nt", pi.getDBName());
	assertEquals ("April 8, 2003", pi.getDBVersion());
	assertEquals ("DBver", pi.getDBComment());
	assertEquals ("this <&>\"<&>\" is a software comment", pi.getComment());
    }


    /**************************************************************************
     * Create and return a MobyPackage form the XML 'infile'. Use
     * 'backup' (or 'backups') as the 'latest known data top-level(s)'
     * data type (any of them may be null).
     **************************************************************************/
    protected MobyPackage parse (String infile,
				 String backup,
				 Map<String,String> backups)
	throws IOException, MobyException {

	InputStream ins = null;
	try {
	    // reading and parsing input
	    ins = this.getClass().getClassLoader().getResourceAsStream (infile);
	    if (ins == null) {
		fail ("Could not find test data resource '" + infile + "'.");
		throw new MobyException ("Test parsing failed.");
	    }
	    MobyPackage moby = null;
	    if (backups != null) {
		moby = MobyPackage.createFromXML (IOUtils.toByteArray (ins), backups);
	    } else {
		moby = MobyPackage.createFromXML (IOUtils.toByteArray (ins), backup);
	    }
	    assertFalse (moby == null);
	    return moby;

	} finally {
	    IOUtils.closeQuietly (ins);
	}

    }



    /**************************************************************************
     *
     *                            Optional parts
     *
     **************************************************************************/

    /**************************************************************************
     * This is to be able to run this JUnit 4 tests with a JUnit 3.x runner.
     **************************************************************************/
    public static junit.framework.Test suite() {
        return new junit.framework.JUnit4TestAdapter (getThisClass());
    }

    /**************************************************************************
     * Run tests from the command line.
     **************************************************************************/
    public static void main (String args[]) {
	org.junit.runner.JUnitCore.main (getThisClassName());
    }

    /**************************************************************************
     * Get the class (name) of this class (note that this is a static
     * method). This madness is here just because I do not want to
     * change the class name in the optional methods above when I copy
     * and paste this into a new test file.
     **************************************************************************/
    private static String getThisClassName() {
	Exception e = new Exception();
	StackTraceElement[] sTrace = e.getStackTrace();
	// sTrace[0] will be always there
	return sTrace[0].getClassName();
    }

    private static Class getThisClass() {
	try {
	    return org.apache.commons.lang.ClassUtils.getClass (getThisClassName());
	} catch (ClassNotFoundException e) {
	    System.err.println ("Cannot get class name.");
	    return java.lang.Object.class;
	}
    }

}
