/*
 * Copyright (c) 2012 The Broad Institute
 *
 * Permission is hereby granted, free of charge, to any person
 * obtaining a copy of this software and associated documentation
 * files (the "Software"), to deal in the Software without
 * restriction, including without limitation the rights to use,
 * copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following
 * conditions:
 *
 * The above copyright notice and this permission notice shall be
 * included in all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
 * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR
 * THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */

package htsjdk.variant.variantcontext;

// the imports for unit testing.

import htsjdk.variant.VariantBaseTest;
import org.testng.Assert;
import org.testng.annotations.BeforeSuite;
import org.testng.annotations.DataProvider;
import org.testng.annotations.Test;

//    public Allele(byte[] bases, boolean isRef) {
//    public Allele(boolean isRef) {
//    public Allele(String bases, boolean isRef) {
//    public boolean isReference()        { return isRef; }
//    public boolean isNonReference()     { return ! isReference(); }
//    public byte[] getBases() { return bases; }
//    public boolean equals(Allele other) {
//    public int length() {

/**
 * Basic unit test for RecalData
 */
public class AlleleUnitTest extends VariantBaseTest {
    private Allele ARef, A, T, ATIns, ATCIns, NoCall, SpandDel, NonRef, UnspecifiedAlternate;

    @BeforeSuite
    public void before() {
        A = Allele.create("A");
        ARef = Allele.create("A", true);
        T = Allele.create("T");

        ATIns = Allele.create("AT");
        ATCIns = Allele.create("ATC");

        NoCall = Allele.create(Allele.NO_CALL_STRING);

        SpandDel = Allele.create(Allele.SPAN_DEL_STRING);

        NonRef = Allele.create(Allele.NON_REF_STRING);
        UnspecifiedAlternate = Allele.create(Allele.UNSPECIFIED_ALTERNATE_ALLELE_STRING);
    }

    @Test
    public void testCreatingSNPAlleles() {
        Assert.assertTrue(A.isNonReference());
        Assert.assertFalse(A.isReference());
        Assert.assertTrue(A.basesMatch("A"));
        Assert.assertEquals(A.length(), 1);

        Assert.assertTrue(ARef.isReference());
        Assert.assertFalse(ARef.isNonReference());
        Assert.assertTrue(ARef.basesMatch("A"));
        Assert.assertFalse(ARef.basesMatch("T"));

        Assert.assertTrue(T.isNonReference());
        Assert.assertFalse(T.isReference());
        Assert.assertTrue(T.basesMatch("T"));
        Assert.assertFalse(T.basesMatch("A"));
    }

    @Test
    public void testCreatingNoCallAlleles() {
        Assert.assertTrue(NoCall.isNonReference());
        Assert.assertFalse(NoCall.isReference());
        Assert.assertFalse(NoCall.basesMatch(Allele.NO_CALL_STRING));
        Assert.assertEquals(NoCall.length(), 0);
        Assert.assertTrue(NoCall.isNoCall());
        Assert.assertFalse(NoCall.isCalled());
    }

    @Test
    public void testCreatingSpanningDeletionAlleles() {
        Assert.assertTrue(SpandDel.isNonReference());
        Assert.assertFalse(SpandDel.isReference());
        Assert.assertTrue(SpandDel.basesMatch(Allele.SPAN_DEL_STRING));
        Assert.assertEquals(SpandDel.length(), 1);
    }

    @Test
    public void testCreatingIndelAlleles() {
        Assert.assertEquals(ATIns.length(), 2);
        Assert.assertEquals(ATCIns.length(), 3);
        Assert.assertEquals(ATIns.getBases(), "AT".getBytes());
        Assert.assertEquals(ATCIns.getBases(), "ATC".getBytes());
    }

    @Test
    public void testConstructors1() {
        Allele a1 = Allele.create("A");
        Allele a2 = Allele.create("A".getBytes());
        Allele a3 = Allele.create("A");
        Allele a4 = Allele.create("A", true);

        Assert.assertTrue(a1.equals(a2));
        Assert.assertTrue(a1.equals(a3));
        Assert.assertFalse(a1.equals(a4));
    }

    @Test
    public void testInsConstructors() {
        Allele a1 = Allele.create("AC");
        Allele a2 = Allele.create("AC".getBytes());
        Allele a3 = Allele.create("AC");
        Allele a4 = Allele.create("AC", true);

        Assert.assertTrue(a1.equals(a2));
        Assert.assertTrue(a1.equals(a3));
        Assert.assertFalse(a1.equals(a4));
    }

    @Test
    public void testVCF42Breakend() {
        Allele a;

        a = Allele.create("A.");
        Assert.assertTrue(a.isSymbolic());
        Assert.assertEquals("A.", a.getDisplayString());

        a = Allele.create(".A");
        Assert.assertTrue(a.isSymbolic());
        Assert.assertEquals(".A", a.getDisplayString());

        Assert.assertTrue(Allele.create("AA.").isSymbolic());
        Assert.assertTrue(Allele.create(".AA").isSymbolic());
    }

    @Test
    public void testBreakpoint() {
        Allele a = Allele.create("A[chr1:1[");

        Assert.assertTrue(a.isSymbolic());
        Assert.assertEquals("A[chr1:1[", a.getDisplayString());

        Assert.assertTrue(Allele.create("]chr1:1]A").isSymbolic());
        Assert.assertTrue(Allele.create("[chr1:1[A").isSymbolic());
        Assert.assertTrue(Allele.create("A]chr1:1]").isSymbolic());
    }

    @Test
    public void testBreakpointSymbolicBreakend() {
        Assert.assertTrue(Allele.create("A[<contig>:1[").isSymbolic());
        Assert.assertTrue(Allele.create("A]<contig>:1]").isSymbolic());
        Assert.assertTrue(Allele.create("]<contig>:1]A").isSymbolic());
        Assert.assertTrue(Allele.create("[<contig>:1[A").isSymbolic());
    }

    @Test
    public void testInsSymbolicShorthand() {
        Assert.assertTrue(Allele.create("A<ctg1>").isSymbolic());
        Assert.assertTrue(Allele.create("<ctg1>A").isSymbolic());
    }

    @Test
    public void testTelomericBreakend() {
        Assert.assertTrue(Allele.create(".[1:10]").isSymbolic());
        Assert.assertTrue(Allele.create("[1:10].").isSymbolic());
    }

    @Test
    public void testSymbolic() {
        Allele a = Allele.create("<SYMBOLIC>");

        Assert.assertTrue(a.isSymbolic());
        Assert.assertEquals("<SYMBOLIC>", a.getDisplayString());
    }

    @Test
    public void testNonRefAllele() {
        Assert.assertTrue(NonRef.isNonRefAllele());
        Assert.assertTrue(UnspecifiedAlternate.isNonRefAllele());

        Assert.assertFalse(T.isNonRefAllele());
        Assert.assertFalse(ATIns.isNonRefAllele());

        Assert.assertTrue(Allele.NON_REF_ALLELE.isNonRefAllele());
        Assert.assertTrue(Allele.UNSPECIFIED_ALTERNATE_ALLELE.isNonRefAllele());

        Allele a = Allele.create(new String("<*>"));
        Assert.assertTrue(a.isNonRefAllele());
    }

    @Test
    public void testEquals() {
        Assert.assertTrue(ARef.basesMatch(A));
        Assert.assertFalse(ARef.equals(A));
        Assert.assertFalse(ARef.equals(ATIns));
        Assert.assertFalse(ARef.equals(ATCIns));

        Assert.assertTrue(T.basesMatch(T));
        Assert.assertFalse(T.basesMatch(A));
        Assert.assertFalse(T.equals(A));

        Assert.assertTrue(ATIns.equals(ATIns));
        Assert.assertFalse(ATIns.equals(ATCIns));
        Assert.assertTrue(ATIns.basesMatch("AT"));
        Assert.assertFalse(ATIns.basesMatch("A"));
        Assert.assertFalse(ATIns.basesMatch("ATC"));

        Assert.assertTrue(ATIns.basesMatch("AT"));
        Assert.assertFalse(ATIns.basesMatch("ATC"));
    }

    @Test(expectedExceptions = IllegalArgumentException.class)
    public void testBadConstructorArgs1() {
        byte[] foo = null;
        Allele.create(foo);
    }

    @Test(expectedExceptions = IllegalArgumentException.class)
    public void testBadConstructorArgs2() {
        Allele.create("x");
    }

    @Test(expectedExceptions = IllegalArgumentException.class)
    public void testBadConstructorArgs3() {
        Allele.create("--");
    }

    @Test(expectedExceptions = IllegalArgumentException.class)
    public void testBadConstructorArgs4() {
        Allele.create("-A");
    }

    @Test(expectedExceptions = IllegalArgumentException.class)
    public void testBadConstructorArgs5() {
        Allele.create("A A");
    }

    @Test(expectedExceptions = IllegalArgumentException.class)
    public void testBadConstructorArgs6() {
        Allele.create("<symbolic>", true); // symbolic cannot be ref allele
    }

    @Test(expectedExceptions = IllegalArgumentException.class)
    public void testBadNoCallAllelel() {
        Allele.create(Allele.NO_CALL_STRING, true); // no call cannot be ref allele
    }

    @Test(expectedExceptions = IllegalArgumentException.class)
    public void testBadSpanningDeletionAllelel() {
        Allele.create(Allele.SPAN_DEL_STRING, true); // spanning deletion cannot be ref allele
    }

    @DataProvider
    public Object[][] getExtendTests() {
        return new Object[][] {
            {Allele.create("A"), "T", "AT"},
            {Allele.create("A"), "TA", "ATA"},
            {Allele.NO_CALL, "A", "A"},
            {Allele.create("AT"), "CGA", "ATCGA"},
            {Allele.create("ATC"), "GA", "ATCGA"}
        };
    }

    @Test(dataProvider = "getExtendTests")
    public void testExtend(Allele toExtend, String extension, String expected) {
        final Allele extended = Allele.extend(toExtend, extension.getBytes());
        Assert.assertEquals(extended, Allele.create(expected));
    }

    @DataProvider
    public Object[][] getTestCasesForCheckingSymbolicAlleles() {
        return new Object[][] {
            // allele, isSymbolic, isBreakpoint, isSingleBreakend
            {"<DEL>", true, false, false},
            {"G]17:198982]", true, true, false},
            {"]13:123456]T", true, true, false},
            {"AAAAAA[chr1:1234[", true, true, false},
            {"AAAAAA]chr1:1234]", true, true, false},
            {"A.", true, false, true},
            {".A", true, false, true},
            {"AA", false, false, false},
            {"A", false, false, false}
        };
    }

    @Test
    public void testCompareTo() {
        Assert.assertEquals(Allele.REF_A.compareTo(Allele.REF_A), 0);
        Assert.assertEquals(Allele.REF_A.compareTo(Allele.ALT_A), -1);
        Assert.assertEquals(Allele.ALT_A.compareTo(Allele.REF_A), 1);
        Assert.assertEquals(Allele.ALT_C.compareTo(Allele.REF_A), 1);
        Assert.assertEquals(Allele.create("ATCA".getBytes(), false).compareTo(Allele.create("ATC".getBytes())), 1);
    }

    @Test(dataProvider = "getTestCasesForCheckingSymbolicAlleles")
    public void testWouldBeSymbolic(String baseString, boolean isSymbolic, boolean isBreakpoint, boolean isBreakend) {
        Assert.assertEquals(Allele.wouldBeSymbolicAllele(baseString.getBytes()), isSymbolic);
    }

    @Test(dataProvider = "getTestCasesForCheckingSymbolicAlleles")
    public void testWouldBeBreakpoint(String baseString, boolean isSymbolic, boolean isBreakpoint, boolean isBreakend) {
        Assert.assertEquals(Allele.wouldBeBreakpoint(baseString.getBytes()), isBreakpoint);
    }

    @Test(dataProvider = "getTestCasesForCheckingSymbolicAlleles")
    public void testWouldBeBreakend(String baseString, boolean isSymbolic, boolean isBreakpoint, boolean isBreakend) {
        Assert.assertEquals(Allele.wouldBeSingleBreakend(baseString.getBytes()), isBreakend);
    }
}
