package test;

import bionet.*;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.Vector;
import java.util.logging.Level;
import java.util.logging.Logger;
import junit.framework.*;

/**
 * Unit testing for {@link Reaction} class. Because of how they are set up, this class
 * also confirms proper functioning of {@link Michaelis}, {@link Linear},
 * {@link Modifier}, et al.
 * @author quintin
 */
public class ReactionTest extends TestCase{
    Node initialNode;
    Reaction r;
    Vector<Node> v;
    static File testfile;
    RuleTable ruletable;

    public static void main(String[] args){
        try {
        testfile = File.createTempFile("testfile",".txt");
    }catch(IOException e) {}
    junit.textui.TestRunner.run(new TestSuite(ReactionTest.class));
    }

    //may need to clarify the behavior of Node and RuleTable in here
@Override public void setUp() throws FileNotFoundException, IOException{
        double[] spts = {0,0};

        //String target = (new File(testfilePath).getCanonicalPath());
        ruletable = new RuleTable(testfile.getCanonicalPath());

        initialNode = new Node(0, false, "node", 0.0, 2, spts, "string", 0);
        r = new Reaction(0, "reaction", initialNode, "sub", 1.0, ruletable);
        r.addNode(0, 0, ruletable, 0, spts);
        v = new Vector<Node>(5);
        v.add(0,initialNode);
    }

    public void testgetRateCoeff(){
        assertEquals("Unmodified constant rate is not correct",(double) 1,r.getRateCoeff(v));
    }

    public void testAddModifier(){
        double[] spts = {0,2};  //this is a nonsense filler. Why is it here?
        Node modNode = new Node(1, false, "node", 2.0, 2, spts, "string", 0);
        r.addModifier(1,1);
        v.add(modNode);
        assertEquals("Rate with default modification at [modifier] == 2 should be 20/21",
                (double) 20 / 21, r.getRateCoeff(v));
        r.getModifier(0).setPower(2);
        assertEquals("Rate with modification at power == 2 should be 400/401",
                (double) 400/401, r.getRateCoeff(v));
        r.getModifier(0).setPower(-1);
        assertEquals("Rate with modification at power == -1 should be 1/21",
                (double) 1/21, r.getRateCoeff(v));
        r.getModifier(0).setPower(0);
        assertEquals("A modifier with power == 0 should not affect the rate",
                (double) 1, r.getRateCoeff(v));
    }

    public void testAddLinear(){
        r.addLinear(0);
        v.elementAt(0).setValue(2);
        assertEquals("Linear effect in Reaction.getRateCoeff", (double) 2, r.getRateCoeff(v));
    }

    public void testAddMichaelis(){
        r.addMichaelis(0);
        v.elementAt(0).setValue(2);
        assertEquals("Michaelis coefficient", (double) 2/3.01, r.getRateCoeff(v));
    }

    public void testAddSubstrateAndAddCatalyst(){
        r.addSubstrate(0);
        v.elementAt(0).setValue(2);
        assertEquals("addSubstrate- linear", (double) 2, r.getRateCoeff(v));
        double[] spts = {0,2};
        Node modNode = new Node(1, false, "node", 2.0, 2, spts, "string", 0);
        v.add(modNode);
        r.addLinear(1,false,true);
        assertEquals("addCatalyst", 4/3.01, r.getRateCoeff(v));
        r.addSubstrate(0);
        //this is off by 10^-16 because of rounding. Within tolerable limits
        double val = (double) 8/Math.pow(3.01,2) - r.getRateCoeff(v);
        assertTrue("addSubstrate- Michaelis",
                Math.pow(10,-14) > val && -Math.pow(10,-14) < val);
    }
/*
    public void testModifier2Catalyst(){
        double[] spts = {0,2};
        Node modNode = new Node(1, false, "node", 2.0, 2, spts, "string", 0);
        r.addModifier(1,1);
        v.add(modNode);
        assertEquals(r.getRateCoeff(v), (double) 20/21);
        r.modifier2Catalyst(1);
        assertEquals("Simple Modifier2Catalyst",r.getRateCoeff(v), (double) 2);
    }

    public void testModifier2Catalyst2() {
        r.addModifier(0,1);
        double[] spts = {0,2};
        Node modNode = new Node(1, false, "node", 2.0, 2, spts, "string", 0);
        r.addModifier(1,1);
        v.add(1,modNode);
        v.elementAt(0).setValue(2);
        assertEquals("pre-Modifier2Catalyst2",r.getRateCoeff(v),((double) 20/21) * ((double) 20/21));
        r.modifier2Catalyst(1);
        assertEquals("Modifier2Catalyst2",r.getRateCoeff(v), (double) 40/21);
    }
    
    public void testLinear2Modifier(){
        r.addLinear(0);
        v.elementAt(0).setValue(2);
        assertEquals("pre-linear2Modifier",(double) 2, r.getRateCoeff(v));
        r.linear2Modifier(0);
        assertEquals("linear2Modifier",(double) 400/401, r.getRateCoeff(v));
    }

    public void testLinear2Michaelis(){
        r.addLinear(0,false,true);
        double[] spts = {0,2};
        Node node = new Node(1, false, "node", 2.0, 2, spts, "string", 0);
        r.addLinear(1);
        v.add(1,node);
        v.elementAt(0).setValue(2);
        r.linear2Michaelis(1);
        assertEquals("linear2Michaelis", (double) 4 / 3.01, r.getRateCoeff(v));
    }
 * */

    private void assertClose(String exp, double arg1, double arg2, double range){
        assertTrue(exp + " Lower Bound", (arg1 > arg2 - range));
        assertTrue(exp + " Upper Bound", (arg1 < arg2 + range));
    }

    public void testNewModImplementation1() {
        double[] spts = {0, 2};  
        Node modNode = new Node(1, false, "node", 2.0, 2, spts, "string", 0);
        v.add(modNode);
        r.addNode(1, 0, ruletable, 1, new double[0]);
        double val = Math.pow(modNode.value / Modifier.defaultA, Modifier.defaultPower);
        assertEquals("new ModImplementation with no parameters",
                (double) val / (1 + val), r.getRateCoeff(v));
    }

    public void testNewModImplementation2() {
        double[] spts = {0, 2};  
        Node modNode = new Node(1, false, "node", 2.0, 2, spts, "string", 0);
        v.add(modNode);
        double[] d = {3.0};
        r.addNode(1, 0, ruletable, 1, d);
        double val = Math.pow(modNode.value / Modifier.defaultA, 3.0);
        assertEquals("new ModImplementation with no parameters",
                (double) val / (1 + val), r.getRateCoeff(v));
    }

    public void testNewModImplementation3() {
        double[] spts = {0, 2}; 
        Node modNode = new Node(1, false, "node", 2.0, 2, spts, "string", 0);
        v.add(modNode);
        double[] d = {3.0, 0.05};
        r.addNode(1, 0, ruletable, 1, d);
        double val = Math.pow(modNode.value / 0.05, 3.0);
        assertEquals("new ModImplementation with no parameters",
                (double) val / (1 + val), r.getRateCoeff(v));
    }

    public void testNewModImplementation4(){
        double[] spts = {0, 2};  
        Node modNode = new Node(1, false, "node", 2.0, 2, spts, "string", 0);
        v.add(modNode);
        double[] d = {-3};
        r.addNode(1, 0, ruletable, 1, d);
        double val = Math.pow(modNode.value / Modifier.defaultA, 3.0);
        assertEquals("new ModImplementation with no parameters",
                (double) 1 / (1 + val), r.getRateCoeff(v));
    }

    public void testToString_Fuzzy(){
        String targetString = "0 1.0 ; 0 0 0 ; ";
        String result = r.toString();
        boolean same = targetString.equals(result);
        //assertEquals("Test toString with one fuzzy node", targetString, result);
        assertTrue("toString with one fuzzy node: expected:" + targetString + " but was:" + result, same);
    }
    public void testToString_Modifier(){
        String targetString = "0 1.0 ; 0 0 0 ; 9 1 -3.0 0.5 ; ";
        double[] spts = {0, 2};  //this is a nonsense filler.
        Node modNode = new Node(1, false, "node", 2.0, 2, spts, "string", 0);
        v.add(modNode);
        r.addModifier(1, -3, .5);
        String result = r.toString();
        boolean same = result.equals(targetString);
        assertTrue("toString with Mod. expected: " + targetString + " but was: " + result, same);
    }
    public void testToString_Linear(){
        String targetString = "0 1.0 ; 0 0 0 ; 10 1 1 0 ; ";
                double[] spts = {0, 2};  //this is a nonsense filler.
        Node modNode = new Node(1, false, "node", 2.0, 2, spts, "string", 0);
        v.add(modNode);
        r.addLinear(1);
        String result = r.toString();
        boolean same = result.equals(targetString);
        assertTrue("Reaction.toString() with a Linear substrate",same);
    }
    public void testToString_MM1(){
        String targetString = "0 1.0 ; 0 0 0 ; 11 1 0.3 ; ";
                double[] spts = {0, 2};  //this is a nonsense filler.
        Node modNode = new Node(1, false, "node", 2.0, 2, spts, "string", 0);
        v.add(modNode);
        r.addMichaelis(1, .3);
        String result = r.toString();
        boolean same = result.equals(targetString);
        assertTrue(same);
    }
    public void testToString_MM2(){
        String targetString = "0 1.0 ; 0 0 0 ; 11 1 1.0 0.01 1.0 ; ";
                double[] spts = {0, 2};  //this is a nonsense filler.
        Node modNode = new Node(1, false, "node", 2.0, 2, spts, "string", 0);
        v.add(modNode);
        r.addMichaelis(1);
        String result = r.toString();
        boolean same = result.equals(targetString);
        assertTrue(same);
    }
    public void testSetReactions(){
        try {
            String filepath = "/./Users/quintin/Workspace/repository/trunk/test/testfile2.txt";
            //File file = new File(path);
            //String filepath = file.getCanonicalPath();
            //System.out.println(filepath);
            Bionet bn = new Bionet(null, filepath, 1, filepath, filepath);
           //for(int i = 0; i < bn.getReactions().size(); i++){
                //String s = bn.getReactions().elementAt(i).toString();
                //System.out.println("line " + i + ": " + s); }
           String[] target = {"0 1.0 ; 5 1 0 ; 4 2 0 ; ","1 1.0 ; 5 2 0 ; 4 1 0 ; "};
            bn.setReactions(target);
            assertEquals("compare |" + target[0] + "| and |" +bn.getReactions().elementAt(0).toString() + "|",
                    target[0], bn.getReactions().elementAt(0).toString());
            assertEquals(target[1], bn.getReactions().elementAt(1).toString());

            //System.out.println("last line == " + bn.getReactions().elementAt(bn.getReactions().size() -1));
        } catch (IOException ex) {
            Logger.getLogger(ReactionTest.class.getName()).log(Level.SEVERE, null, ex);
        }
    }

    public void testMutationDemo(){
        try {
            String filepath = "/./Users/quintin/Workspace/repository/trunk/test/testfile2.txt";
            //System.out.println(filepath);
            Bionet bn = new Bionet(null, filepath, 1, filepath, filepath);
           for(int i = 0; i < bn.getReactions().size(); i++){
                String s = bn.getReactions().elementAt(i).toString();
                System.out.println("line " + i + ": " + s);
           }
           String[] target = {"0 1.0 ; 5 1 0 ; 4 2 0 ; ","1 1.0 ; 5 2 0 ; 4 1 0 ; "};
            bn.setReactions(target);
            int[] ar = {0, 1};
            Genomics gen = Genomics.makeIndependent(ar,ruletable.getNRules());

            String[][] pop = gen.createPopulation(Genomics.reactions2Array(bn.getReactions()));
            pop = gen.crossover(pop);
            for (int i = 0; i < pop.length; i++){
                pop[i] = gen.mutateGenome(pop[i]);
            }
            pop = gen.crossover(pop);
            for (int i = 0; i < pop.length; i++){
                System.out.println("member " + i + ":");
                for (int j = 0; j < pop[i].length; j++){
                    System.out.println(pop[i][j]);
                }
            }

        } catch (IOException ex) {
            Logger.getLogger(ReactionTest.class.getName()).log(Level.SEVERE, null, ex);
        }

    }

    public void testSortByFitness() {
        int[] ar = {0, 1};
        Genomics gen = Genomics.makeIndependent(ar, 5);
        String[] first = {"This was made first, should go second"};
        String[] second = {"This was made second, should go third"};
        String[] third = {"This was made third, should go first"};
        String[] fourth = {"This was made fourth. Does it go first?"};
        double[] fits = {2.0, 3.5, 0, 0};
        String[][] begin = {first, second, third, fourth};
        //Fit fit = new Fit();
        //String[][] sorted = fit.sortByFitness(begin, fits);
        /*assertEquals(sorted[0], third);
        assertEquals(sorted[1], first);
        assertEquals(sorted[2], second);*/
        //for (String[] s : sorted) System.out.println(s[0]);
    }

    /*public void testRuleTable(){
        System.out.println(ruletable.getIndex("inh"));
        int[] list = ruletable.getRule(7);
        for (int i : list) System.out.println(i);
    }*/

    /*
    public void testHedges(){
        double initialRate = r.getRateCoeff(v);
        double[] params = {3.0};
        r.addNode(0,0,ruletable,0,params);

    }*/
}
