/*
 *                    BioJava development code
 *
 * This code may be freely distributed and modified under the
 * terms of the GNU Lesser General Public Licence.  This should
 * be distributed with the code.  If you do not have a copy,
 * see:
 *
 *      http://www.gnu.org/copyleft/lesser.html
 *
 * Copyright for this code is held jointly by the individual
 * authors.  These should be listed in @author doc comments.
 *
 * For more information on the BioJava project and its aims,
 * or to join the biojava-l mailing list, visit the home page
 * at:
 *
 *      http://www.biojava.org/
 *
 * Created on June 15, 2010
 * Author: Mark Chapman
 */

package org.biojava3.alignment;

import static org.junit.Assert.*;

import java.util.Arrays;
import java.util.List;

import org.biojava3.alignment.template.AlignedSequence;
import org.biojava3.alignment.template.AlignedSequence.Step;
import org.biojava3.alignment.template.Profile;
import org.biojava3.alignment.template.Profile.StringFormat;
import org.biojava3.core.sequence.AccessionID;
import org.biojava3.core.sequence.ProteinSequence;
import org.biojava3.core.sequence.compound.AminoAcidCompound;
import org.biojava3.core.sequence.compound.AminoAcidCompoundSet;
import org.junit.Before;
import org.junit.Ignore;
import org.junit.Test;

public class SimpleProfileTest {

    private ProteinSequence query, target;
    private Profile<ProteinSequence, AminoAcidCompound> global, local, single;

    @Before
    public void setup() {
        query = new ProteinSequence("ARND");
        target = new ProteinSequence("RDG");
        query.setAccession(new AccessionID("Query"));
        target.setAccession(new AccessionID("Target"));
        global = new SimpleProfile<ProteinSequence, AminoAcidCompound>(query, target, Arrays.asList(new Step[] {
                Step.COMPOUND, Step.COMPOUND, Step.COMPOUND, Step.COMPOUND, Step.GAP}), 0, 0, Arrays.asList(
                new Step[] {Step.GAP, Step.COMPOUND, Step.GAP, Step.COMPOUND, Step.COMPOUND}), 0, 0);
        local = new SimpleProfile<ProteinSequence, AminoAcidCompound>(query, target, Arrays.asList(new Step[] {
                Step.COMPOUND, Step.COMPOUND, Step.COMPOUND}), 1, 0, Arrays.asList(new Step[] { Step.COMPOUND,
                Step.GAP, Step.COMPOUND}), 0, 1);
        single = new SimpleProfile<ProteinSequence, AminoAcidCompound>(query);
    }

    @Test(expected=IllegalArgumentException.class)
    public void testSimpleProfile() {
        new SimpleProfile<ProteinSequence, AminoAcidCompound>(query, target, Arrays.asList(new Step[] {
                Step.COMPOUND, Step.COMPOUND, Step.COMPOUND, Step.COMPOUND, Step.GAP}), 0, 0, Arrays.asList(
                new Step[] {Step.GAP, Step.COMPOUND, Step.GAP, Step.COMPOUND}), 0, 0);
    }

    @Test
    public void testGetAlignedSequenceInt() {
        assertEquals(global.getAlignedSequence(1).toString(), "ARND-");
        assertEquals(global.getAlignedSequence(2).toString(), "-R-DG");
        assertEquals(local.getAlignedSequence(1).toString(), "RND");
        assertEquals(local.getAlignedSequence(2).toString(), "R-D");
        assertEquals(single.getAlignedSequence(1).toString(), "ARND");
    }

    @Test(expected=IndexOutOfBoundsException.class)
    public void testGetAlignedSequenceIntOutOfBounds() {
        global.getAlignedSequence(0);
    }

    @Test(expected=IndexOutOfBoundsException.class)
    public void testGetAlignedSequenceIntOutOfBounds2() {
        global.getAlignedSequence(3);
    }

    @Test(expected=IndexOutOfBoundsException.class)
    public void testGetAlignedSequenceIntOutOfBounds3() {
        local.getAlignedSequence(0);
    }

    @Test(expected=IndexOutOfBoundsException.class)
    public void testGetAlignedSequenceIntOutOfBounds4() {
        local.getAlignedSequence(3);
    }

    @Test(expected=IndexOutOfBoundsException.class)
    public void testGetAlignedSequenceIntOutOfBounds5() {
        single.getAlignedSequence(0);
    }

    @Test(expected=IndexOutOfBoundsException.class)
    public void testGetAlignedSequenceIntOutOfBounds6() {
        single.getAlignedSequence(2);
    }

    @Test
    public void testGetAlignedSequenceS() {
        assertEquals(global.getAlignedSequence(query).toString(), "ARND-");
        assertEquals(global.getAlignedSequence(target).toString(), "-R-DG");
        assertNull(global.getAlignedSequence(new ProteinSequence("AR")));
        assertEquals(local.getAlignedSequence(query).toString(), "RND");
        assertEquals(local.getAlignedSequence(target).toString(), "R-D");
        assertNull(local.getAlignedSequence(new ProteinSequence("AR")));
        assertEquals(single.getAlignedSequence(query).toString(), "ARND");
        assertNull(single.getAlignedSequence(target));
    }

    @Test
    public void testGetAlignedSequences() {
        List<AlignedSequence<ProteinSequence, AminoAcidCompound>> list = global.getAlignedSequences();
        assertEquals(list.size(), 2);
        assertEquals(list.get(0).toString(), "ARND-");
        assertEquals(list.get(1).toString(), "-R-DG");
        list = local.getAlignedSequences();
        assertEquals(list.size(), 2);
        assertEquals(list.get(0).toString(), "RND");
        assertEquals(list.get(1).toString(), "R-D");
        list = single.getAlignedSequences();
        assertEquals(list.size(), 1);
        assertEquals(list.get(0).toString(), "ARND");
    }

    @Test
    public void testGetAlignedSequencesIntArray() {
        List<AlignedSequence<ProteinSequence, AminoAcidCompound>> list = global.getAlignedSequences(2, 1, 2);
        assertEquals(list.size(), 3);
        assertEquals(list.get(0).toString(), "-R-DG");
        assertEquals(list.get(1).toString(), "ARND-");
        assertEquals(list.get(2).toString(), "-R-DG");
        list = local.getAlignedSequences(2, 2, 1);
        assertEquals(list.size(), 3);
        assertEquals(list.get(0).toString(), "R-D");
        assertEquals(list.get(1).toString(), "R-D");
        assertEquals(list.get(2).toString(), "RND");
        list = single.getAlignedSequences(1, 1);
        assertEquals(list.size(), 2);
        assertEquals(list.get(0).toString(), "ARND");
        assertEquals(list.get(1).toString(), "ARND");
    }

    @Test
    public void testGetAlignedSequencesSArray() {
        List<AlignedSequence<ProteinSequence, AminoAcidCompound>> list = global.getAlignedSequences(query, query,
                target);
        assertEquals(list.size(), 3);
        assertEquals(list.get(0).toString(), "ARND-");
        assertEquals(list.get(1).toString(), "ARND-");
        assertEquals(list.get(2).toString(), "-R-DG");
        list = local.getAlignedSequences(target, query, target);
        assertEquals(list.size(), 3);
        assertEquals(list.get(0).toString(), "R-D");
        assertEquals(list.get(1).toString(), "RND");
        assertEquals(list.get(2).toString(), "R-D");
        list = single.getAlignedSequences(query, query);
        assertEquals(list.size(), 2);
        assertEquals(list.get(0).toString(), "ARND");
        assertEquals(list.get(1).toString(), "ARND");
    }

    @Test
    public void testGetCompoundAtIntInt() {
        assertEquals(global.getCompoundAt(1, 4).getShortName(), "D");
        assertEquals(global.getCompoundAt(2, 3).getShortName(), "-");
        assertEquals(local.getCompoundAt(1, 1).getShortName(), "R");
        assertEquals(local.getCompoundAt(2, 2).getShortName(), "-");
        assertEquals(single.getCompoundAt(1, 3).getShortName(), "N");
    }

    @Test(expected=IndexOutOfBoundsException.class)
    public void testGetCompoundAtIntIntOutOfBounds() {
        global.getCompoundAt(0, 4);
    }

    @Test(expected=IndexOutOfBoundsException.class)
    public void testGetCompoundAtIntIntOutOfBounds2() {
        global.getCompoundAt(3, 4);
    }

    @Test(expected=IndexOutOfBoundsException.class)
    public void testGetCompoundAtIntIntOutOfBounds3() {
        global.getCompoundAt(1, 0);
    }

    @Test(expected=IndexOutOfBoundsException.class)
    public void testGetCompoundAtIntIntOutOfBounds4() {
        global.getCompoundAt(2, 6);
    }

    @Test(expected=IndexOutOfBoundsException.class)
    public void testGetCompoundAtIntIntOutOfBounds5() {
        local.getCompoundAt(0, 2);
    }

    @Test(expected=IndexOutOfBoundsException.class)
    public void testGetCompoundAtIntIntOutOfBounds6() {
        local.getCompoundAt(3, 2);
    }

    @Test(expected=IndexOutOfBoundsException.class)
    public void testGetCompoundAtIntIntOutOfBounds7() {
        local.getCompoundAt(1, 0);
    }

    @Test(expected=IndexOutOfBoundsException.class)
    public void testGetCompoundAtIntIntOutOfBounds8() {
        local.getCompoundAt(2, 4);
    }

    @Test(expected=IndexOutOfBoundsException.class)
    public void testGetCompoundAtIntIntOutOfBounds9() {
        single.getCompoundAt(1, 0);
    }

    @Test
    public void testGetCompoundAtSInt() {
        assertEquals(global.getCompoundAt(query, 2).getShortName(), "R");
        assertEquals(global.getCompoundAt(target, 5).getShortName(), "G");
        assertNull(global.getCompoundAt(new ProteinSequence("AR"), 3));
        assertEquals(local.getCompoundAt(query, 2).getShortName(), "N");
        assertEquals(local.getCompoundAt(target, 3).getShortName(), "D");
        assertNull(local.getCompoundAt(new ProteinSequence("AR"), 3));
        assertEquals(single.getCompoundAt(query, 2).getShortName(), "R");
        assertNull(single.getCompoundAt(target, 3));
    }

    @Test(expected=IndexOutOfBoundsException.class)
    public void testGetCompoundAtSIntOutOfBounds() {
        global.getCompoundAt(query, 0);
    }

    @Test(expected=IndexOutOfBoundsException.class)
    public void testGetCompoundAtSIntOutOfBounds2() {
        global.getCompoundAt(target, 6);
    }

    @Test(expected=IndexOutOfBoundsException.class)
    public void testGetCompoundAtSIntOutOfBounds3() {
        local.getCompoundAt(target, 0);
    }

    @Test(expected=IndexOutOfBoundsException.class)
    public void testGetCompoundAtSIntOutOfBounds4() {
        local.getCompoundAt(query, 4);
    }

    @Test(expected=IndexOutOfBoundsException.class)
    public void testGetCompoundAtSIntOutOfBounds5() {
        single.getCompoundAt(query, 0);
    }

    @Test
    public void testGetCompoundSet() {
        assertEquals(global.getCompoundSet(), AminoAcidCompoundSet.getAminoAcidCompoundSet());
        assertEquals(local.getCompoundSet(), AminoAcidCompoundSet.getAminoAcidCompoundSet());
        assertEquals(single.getCompoundSet(), AminoAcidCompoundSet.getAminoAcidCompoundSet());
    }

    @Test
    public void testGetCompoundsAt() {
        List<AminoAcidCompound> column = global.getCompoundsAt(5);
        assertEquals(column.size(), 2);
        assertEquals(column.get(0).getShortName(), "-");
        assertEquals(column.get(1).getShortName(), "G");
        column = local.getCompoundsAt(2);
        assertEquals(column.size(), 2);
        assertEquals(column.get(0).getShortName(), "N");
        assertEquals(column.get(1).getShortName(), "-");
        column = single.getCompoundsAt(2);
        assertEquals(column.size(), 1);
        assertEquals(column.get(0).getShortName(), "R");
    }

    @Test(expected=IndexOutOfBoundsException.class)
    public void testGetCompoundsAtOutOfBounds() {
        global.getCompoundsAt(0);
    }

    @Test(expected=IndexOutOfBoundsException.class)
    public void testGetCompoundsAtOutOfBounds2() {
        global.getCompoundsAt(6);
    }

    @Test(expected=IndexOutOfBoundsException.class)
    public void testGetCompoundsAtOutOfBounds3() {
        local.getCompoundsAt(0);
    }

    @Test(expected=IndexOutOfBoundsException.class)
    public void testGetCompoundsAtOutOfBounds4() {
        local.getCompoundsAt(4);
    }

    @Test(expected=IndexOutOfBoundsException.class)
    public void testGetCompoundsAtOutOfBounds5() {
        single.getCompoundsAt(0);
    }

    @Test(expected=IndexOutOfBoundsException.class)
    public void testGetCompoundsAtOutOfBounds6() {
        single.getCompoundsAt(5);
    }

    @Test
    public void testGetIndexOf() {
        AminoAcidCompoundSet cs = AminoAcidCompoundSet.getAminoAcidCompoundSet();
        assertEquals(global.getIndexOf(cs.getCompoundForString("A")), 1);
        assertEquals(global.getIndexOf(cs.getCompoundForString("R")), 2);
        assertEquals(global.getIndexOf(cs.getCompoundForString("N")), 3);
        assertEquals(global.getIndexOf(cs.getCompoundForString("D")), 4);
        assertEquals(global.getIndexOf(cs.getCompoundForString("G")), 5);
        assertEquals(global.getIndexOf(cs.getCompoundForString("-")), 1);
        assertEquals(global.getIndexOf(cs.getCompoundForString("E")), -1);
        assertEquals(local.getIndexOf(cs.getCompoundForString("R")), 1);
        assertEquals(local.getIndexOf(cs.getCompoundForString("N")), 2);
        assertEquals(local.getIndexOf(cs.getCompoundForString("D")), 3);
        assertEquals(local.getIndexOf(cs.getCompoundForString("-")), 2);
        assertEquals(local.getIndexOf(cs.getCompoundForString("K")), -1);
        assertEquals(single.getIndexOf(cs.getCompoundForString("A")), 1);
        assertEquals(single.getIndexOf(cs.getCompoundForString("R")), 2);
        assertEquals(single.getIndexOf(cs.getCompoundForString("N")), 3);
        assertEquals(single.getIndexOf(cs.getCompoundForString("D")), 4);
        assertEquals(single.getIndexOf(cs.getCompoundForString("G")), -1);
    }

    @Test
    public void testGetIndicesAt() {
        assertArrayEquals(global.getIndicesAt(1), new int[] {1, 1});
        assertArrayEquals(global.getIndicesAt(2), new int[] {2, 1});
        assertArrayEquals(global.getIndicesAt(3), new int[] {3, 1});
        assertArrayEquals(global.getIndicesAt(4), new int[] {4, 2});
        assertArrayEquals(global.getIndicesAt(5), new int[] {4, 3});
        assertArrayEquals(local.getIndicesAt(1), new int[] {2, 1});
        assertArrayEquals(local.getIndicesAt(2), new int[] {3, 1});
        assertArrayEquals(local.getIndicesAt(3), new int[] {4, 2});
        assertArrayEquals(single.getIndicesAt(1), new int[] {1});
        assertArrayEquals(single.getIndicesAt(2), new int[] {2});
        assertArrayEquals(single.getIndicesAt(3), new int[] {3});
        assertArrayEquals(single.getIndicesAt(4), new int[] {4});
    }

    @Test(expected=IndexOutOfBoundsException.class)
    public void testGetIndicesAtOutOfBounds() {
        global.getIndicesAt(0);
    }

    @Test(expected=IndexOutOfBoundsException.class)
    public void testGetIndicesAtOutOfBounds2() {
        global.getIndicesAt(6);
    }

    @Test(expected=IndexOutOfBoundsException.class)
    public void testGetIndicesAtOutOfBounds3() {
        local.getIndicesAt(0);
    }

    @Test(expected=IndexOutOfBoundsException.class)
    public void testGetIndicesAtOutOfBounds4() {
        local.getIndicesAt(4);
    }

    @Test(expected=IndexOutOfBoundsException.class)
    public void testGetIndicesAtOutOfBounds5() {
        single.getIndicesAt(0);
    }

    @Test(expected=IndexOutOfBoundsException.class)
    public void testGetIndicesAtOutOfBounds6() {
        single.getIndicesAt(5);
    }

    @Test
    public void testGetLastIndexOf() {
        AminoAcidCompoundSet cs = AminoAcidCompoundSet.getAminoAcidCompoundSet();
        assertEquals(global.getLastIndexOf(cs.getCompoundForString("A")), 1);
        assertEquals(global.getLastIndexOf(cs.getCompoundForString("R")), 2);
        assertEquals(global.getLastIndexOf(cs.getCompoundForString("N")), 3);
        assertEquals(global.getLastIndexOf(cs.getCompoundForString("D")), 4);
        assertEquals(global.getLastIndexOf(cs.getCompoundForString("G")), 5);
        assertEquals(global.getLastIndexOf(cs.getCompoundForString("-")), 5);
        assertEquals(global.getLastIndexOf(cs.getCompoundForString("E")), -1);
        assertEquals(local.getLastIndexOf(cs.getCompoundForString("R")), 1);
        assertEquals(local.getLastIndexOf(cs.getCompoundForString("N")), 2);
        assertEquals(local.getLastIndexOf(cs.getCompoundForString("D")), 3);
        assertEquals(local.getLastIndexOf(cs.getCompoundForString("-")), 2);
        assertEquals(local.getLastIndexOf(cs.getCompoundForString("K")), -1);
        assertEquals(single.getLastIndexOf(cs.getCompoundForString("A")), 1);
        assertEquals(single.getLastIndexOf(cs.getCompoundForString("R")), 2);
        assertEquals(single.getLastIndexOf(cs.getCompoundForString("N")), 3);
        assertEquals(single.getLastIndexOf(cs.getCompoundForString("D")), 4);
        assertEquals(single.getLastIndexOf(cs.getCompoundForString("G")), -1);
    }

    @Test
    public void testGetLength() {
        assertEquals(global.getLength(), 5);
        assertEquals(local.getLength(), 3);
        assertEquals(single.getLength(), 4);
    }

    @Test
    public void testGetOriginalSequences() {
        List<ProteinSequence> list = global.getOriginalSequences();
        assertEquals(list.size(), 2);
        assertEquals(list.get(0), query);
        assertEquals(list.get(1), target);
        list = local.getOriginalSequences();
        assertEquals(list.size(), 2);
        assertEquals(list.get(0), query);
        assertEquals(list.get(1), target);
        list = single.getOriginalSequences();
        assertEquals(list.size(), 1);
        assertEquals(list.get(0), query);
    }

    @Test
    public void testGetSize() {
        assertEquals(global.getSize(), 2);
        assertEquals(local.getSize(), 2);
        assertEquals(single.getSize(), 1);
    }

    @Ignore // TODO SimpleProfile.getSubProfile(Location)
    @Test
    public void testGetSubProfile() {
        fail("Not yet implemented");
    }

    @Test
    public void testIsCircular() {
        assertFalse(global.isCircular());
        assertFalse(local.isCircular());
        assertFalse(single.isCircular());
    }

    @Test
    public void testToStringInt() {
    
    	
    
    	
        assertEquals(global.toString(3), String.format(
                "          1 3%n" +
                "Query   1 ARN 3%n" +                
                "           | %n"+
                "Target  1 -R- 1%n" +
                "%n" +
                "          4 5%n" +
                "Query   4 D- 4%n" +
                "          | %n"+
                "Target  2 DG 3%n"));
        assertEquals(local.toString(4), String.format(
                "          1 3%n" +
                "Query   2 RND 4%n" +
                "          | |%n"+
                "Target  1 R-D 2%n"));
        assertEquals(single.toString(4), String.format(
                "         1  4%n" +
                "Query  1 ARND 4%n" ));
    }

    @Test
    public void testToStringFormatted() {
       
        assertEquals(global.toString(StringFormat.ALN), String.format(
                "CLUSTAL W MSA from BioJava%n%n" +
                "Query     ARND- 4%n" +
                "           | | %n"+
                "Target    -R-DG 3%n"));
        assertEquals(local.toString(StringFormat.FASTA), String.format(
                ">Query%n" +
                "RND%n" +
                ">Target%n" +
                "R-D%n"));
        assertEquals(single.toString(StringFormat.MSF), String.format(
                "MSA from BioJava%n%n" +
                " MSF: 4  Type: P  Check: 735 ..%n%n" +
                " Name: Query  Len: 4  Check:  735  Weight: 1.0%n" +
                "%n//%n%n" +
                "Query ARND%n"));
    }

    @Test
    public void testToString() {
        assertEquals(global.toString(), String.format("ARND-%n-R-DG%n"));
        assertEquals(local.toString(), String.format("RND%nR-D%n"));
        assertEquals(single.toString(), String.format("ARND%n"));
    }

    @Test
    public void testIterator() {
        for (AlignedSequence<ProteinSequence, AminoAcidCompound> s : global) {
            assertEquals(s.toString().length(), 5);
        }
        for (AlignedSequence<ProteinSequence, AminoAcidCompound> s : local) {
            assertEquals(s.toString().length(), 3);
        }
        for (AlignedSequence<ProteinSequence, AminoAcidCompound> s : single) {
            assertEquals(s.toString().length(), 4);
        }
    }

}
