/*
 * =====================================================================================
 *
 *       Filename:  DNASequence_gtest.cpp
 *
 *    Description:  Test DNASequence.hpp
 *
 *        Version:  1.0
 *        Created:  10/27/2012 09:42:13 AM
 *       Revision:  none
 *       Compiler:  gcc
 *
 *         Author:  Yuan Li (yli), yli@pacificbiosciences.com
 *        Company:  Pacific Biosciences
 *
 * =====================================================================================
 */

#include <climits>
#include <fstream>
#include <iostream>
#include <string>

#include <gtest/gtest.h>

#include <pbdata/DNASequence.hpp>

//Note ::testing::Test not ::testing::TEST
//SetUp() and TearDown(), not Setup() and Teardown()
class DNASequenceTest : public ::testing::Test
{
public:
    DNASequence dnaOne;
};

//Test DNASequence constructor
TEST_F(DNASequenceTest, Constructor)
{
    DNASequence dnaSeq;
    EXPECT_TRUE(dnaSeq.seq == NULL);
    EXPECT_TRUE(dnaSeq.length == 0);
    EXPECT_TRUE(dnaSeq.size() == dnaSeq.length);
    EXPECT_TRUE(dnaSeq.bitsPerNuc == 8);
    EXPECT_FALSE(dnaSeq.deleteOnExit);

    Nucleotide HKITTY[] = "HELLO,KITTY!";
    dnaSeq.seq = HKITTY;
    dnaSeq.length = sizeof(HKITTY) / sizeof(Nucleotide) - 1;
    //    dnaSeq.Print(std::cout);
    EXPECT_EQ(dnaSeq.size(), 12u);

    DNALength thisLen = 12;
    Nucleotide* thisNuc = new Nucleotide[thisLen];
    memcpy(thisNuc, HKITTY, thisLen);
    DNASequence newDnaSeq;
    newDnaSeq.seq = thisNuc;
    newDnaSeq.length = thisLen;
    //    newDnaSeq.Print(std::cout);
    EXPECT_EQ(memcmp(newDnaSeq.seq, dnaSeq.seq, thisLen), 0);
    EXPECT_EQ(newDnaSeq.length, thisLen);
    if (!thisNuc) delete[] thisNuc;

    DNASequence nnewDnaSeq;
    thisLen = 12;
    std::string atgc("atgcatgcatgc");
    thisNuc = new Nucleotide[thisLen];
    for (DNALength i = 0; i < thisLen; i++) {
        thisNuc[i] = atgc[i];
    }
    std::string ret;
    nnewDnaSeq.seq = thisNuc;
    nnewDnaSeq.length = thisLen;
    for (DNALength i = 0; i < thisLen; i++) {
        ret += nnewDnaSeq.seq[i];
    }
    EXPECT_STREQ(ret.c_str(), atgc.c_str());
}

//Test DNASequence Append()
TEST_F(DNASequenceTest, Append)
{
    DNALength oneLen = 10;
    Nucleotide* one = new Nucleotide[oneLen];

    std::string As("AAAAAAAAAA");
    for (DNALength i = 0; i < oneLen; i++) {
        one[i] = As[i];
    }
    //Can not memcpy a string to a DNASequence directly
    //such as memcpy(one, As.c_str(), oneLen), because
    //DNASequence.seq is of type unsigned char, not char

    DNALength twoLen = 20;
    Nucleotide* two = new Nucleotide[twoLen];

    std::string Gs("GGGGGGGGGGGGGGGGGGGG");
    for (DNALength i = 0; i < twoLen; i++) {
        two[i] = Gs[i];
    }
    //memcpy(two, Gs.c_str(), twoLen);

    Nucleotide* three = new Nucleotide[oneLen + twoLen];
    memcpy(three, one, oneLen);
    memcpy(three + oneLen, two, twoLen);

    DNASequence dnaTwo;
    dnaOne.seq = one;
    dnaOne.length = oneLen;
    dnaOne.deleteOnExit = true;
    dnaTwo.seq = two;
    dnaTwo.length = twoLen;

    dnaOne.Append(dnaTwo, 0);
    EXPECT_EQ(dnaOne.length, oneLen + twoLen);
    EXPECT_EQ(memcmp(dnaOne.seq, three, dnaOne.length), 0);

    std::string AGs("AAAAAAAAAAGGGGGGGGGGGGGGGGGGGG");
    for (DNALength i = 0; i < dnaOne.length; i++) {
        EXPECT_EQ(AGs[i], (char)dnaOne.seq[i]);
    }

    //if appendPos is positive, overwrite this sequence
    //from appendPos to the end
    AGs = "AAGGGGGGGGGGGGGGGGGGGG";
    DNALength appendPos = 2;
    dnaOne.Append(dnaTwo, appendPos);
    EXPECT_EQ(dnaOne.length, appendPos + twoLen);
    for (DNALength i = 0; i < dnaOne.length; i++) {
        EXPECT_EQ(AGs[i], (char)dnaOne.seq[i]);
    }

    if (!one) delete[] one;
    if (!two) delete[] two;
    if (!three) delete[] three;
}

//Test DNASequence TakeOwnership
TEST_F(DNASequenceTest, TakeOwnership)
{
    DNALength oneLen = 10;
    Nucleotide* one = new Nucleotide[oneLen];

    dnaOne.seq = one;
    dnaOne.length = oneLen;

    DNASequence dnaTwo;

    //a bug may occur if deleteOneExit is true and
    //TakeOwnership() is called twice. In that case, both
    //dnaOne and dnaTwo will become wild pointers
    dnaTwo.deleteOnExit = true;
    dnaTwo.TakeOwnership(dnaOne);

    EXPECT_EQ(dnaTwo.length, dnaOne.length);
    EXPECT_EQ(dnaTwo.deleteOnExit, dnaOne.deleteOnExit);
    EXPECT_EQ(dnaTwo.seq, dnaOne.seq);

    if (!one) delete[] one;
}

//Test DNASequence ShallowCopy
TEST_F(DNASequenceTest, ShallowCopy)
{
    DNALength oneLen = 10;
    Nucleotide* one = new Nucleotide[oneLen];

    std::string As("AAAAAAAAAA");
    for (DNALength i = 0; i < oneLen; i++) {
        one[i] = As[i];
    }
    dnaOne.seq = one;
    dnaOne.length = oneLen;

    DNASequence dnaTwo;
    dnaTwo.ShallowCopy(dnaOne);

    EXPECT_EQ(dnaTwo.length, dnaOne.length);
    EXPECT_EQ(dnaTwo.seq, dnaOne.seq);
    EXPECT_EQ(dnaTwo.deleteOnExit, dnaOne.deleteOnExit);
}

//Test DNASequence.Copy(const DNASequence rhs,
//                      DNALength rhsPos,
//                      DNALength rhsLength)
TEST_F(DNASequenceTest, Copy)
{
    DNALength oneLen = 10;
    Nucleotide* one = new Nucleotide[oneLen];

    std::string As("AGAAAAACAA");
    for (DNALength i = 0; i < oneLen; i++) {
        one[i] = As[i];
    }

    dnaOne.seq = one;
    dnaOne.length = oneLen;

    DNASequence dnaTwo;
    dnaTwo.Copy(dnaOne);

    EXPECT_EQ(dnaTwo.length, dnaOne.length);
    EXPECT_NE(dnaTwo.seq, dnaOne.seq);
    EXPECT_TRUE(dnaTwo.deleteOnExit);
    EXPECT_EQ(memcmp(dnaTwo.seq, dnaOne.seq, dnaOne.length), 0);

    //if rhs.length is 0, return this *
    DNASequence dnaThree;
    dnaTwo.Copy(dnaThree);
    //dnaTwo remains unchanged
    EXPECT_EQ(dnaTwo.length, 0u);
    EXPECT_NE(dnaTwo.seq, dnaOne.seq);
    EXPECT_TRUE(dnaTwo.deleteOnExit);
    EXPECT_TRUE(dnaTwo.seq == NULL);

    //if rhsPos is not 0 and rhsLength is 0
    dnaTwo.Copy(dnaOne, 2);
    EXPECT_EQ(dnaTwo.length, dnaOne.length - 2);
    EXPECT_TRUE(dnaTwo.deleteOnExit);
    EXPECT_EQ(memcmp(dnaTwo.seq, dnaOne.seq + 2, dnaTwo.length), 0);

    //if the subsequence to copy is out of bounds
    EXPECT_GT(200u, dnaOne.length);
    //EXPECT_EXIT(dnaTwo.Copy(dnaOne, 200), ::testing::ExitedWithCode(1), "");

    //if both rhsPos and rhsLength are less than MAXINT,
    //but rhsPos+ rhsLength > MAXINT
    DNALength rhsPos = 3;
    DNALength rhsLength = UINT_MAX - 1;
    EXPECT_TRUE(rhsPos < UINT_MAX && rhsLength < UINT_MAX);
    EXPECT_TRUE(rhsLength > dnaOne.length + 1);
    //EXPECT_EXIT(dnaTwo.Copy(dnaOne, rhsPos, rhsLength), ::testing::ExitedWithCode(1), "");

    //if rhsPos > rhs.length
    //EXPECT_EXIT(dnaTwo.Copy(dnaOne, dnaOne.length + 1), ::testing::ExitedWithCode(1), "")
    //    << "Copy a subsequence which is out of bounds. This needs to be taken care of. See bug 21867.";
}

//Test DNASequence Allocate(DNALength)
TEST_F(DNASequenceTest, Allocate)
{
    dnaOne.Allocate(0);
    EXPECT_EQ(dnaOne.length, 0u);

    DNASequence dnaTwo;
    dnaTwo.Allocate(100);
    EXPECT_EQ(dnaTwo.length, 100u);
}

//Test DNASequence ReferenceSubstring(rhs, pos, substrLength)
TEST_F(DNASequenceTest, ReferenceSubstring)
{
    DNALength oneLen = 10;
    dnaOne.seq = new Nucleotide[oneLen];
    dnaOne.length = oneLen;

    DNASequence dnaTwo;
    dnaTwo.ReferenceSubstring(dnaOne);

    EXPECT_EQ(dnaOne.seq, dnaTwo.seq);
    EXPECT_EQ(dnaOne.length, dnaTwo.length);
    EXPECT_FALSE(dnaTwo.deleteOnExit);

    //    EXPECT_DEATH_IF_SUPPORTED(dnaTwo.ReferenceSubstring(dnaOne, 100), "");
    delete[] dnaOne.seq;
}

//Test DNASequence ToString()
TEST_F(DNASequenceTest, ToString)
{
    DNALength oneLen = 10;
    dnaOne.seq = new Nucleotide[oneLen];
    dnaOne.length = oneLen;

    std::string As("AGAAAAACAA");
    for (DNALength i = 0; i < oneLen; i++) {
        dnaOne.seq[i] = As[i];
    }

    EXPECT_EQ(dnaOne.ToString(), As);
    EXPECT_EQ(dnaOne.ToString(2), std::string("AG\nAA\nAA\nAC\nAA"));
    EXPECT_EQ(dnaOne.ToString(3), std::string("AGA\nAAA\nACA\nA"));
}

//Test DNASequence ReverseComplementSelf()
TEST_F(DNASequenceTest, ReverseComplementSelf)
{
    const std::string s("AGAATTCG");
    const std::string r("CGAATTCT");
    dnaOne.Copy(s);

    EXPECT_EQ(dnaOne.ReverseComplementSelf().ToString(), r);
    EXPECT_EQ(dnaOne.length, r.size());

    DNASequence dnaTwo;
    dnaTwo.ReferenceSubstring(dnaOne, 1, 3);
    EXPECT_EQ(dnaTwo.ToString(), "GAA");
    EXPECT_EQ(dnaTwo.ReverseComplementSelf().ToString(), "TTC");
    EXPECT_EQ(dnaTwo.length, 3u);
}

// Test DNASequence Copy from string
TEST_F(DNASequenceTest, CopyFromString)
{
    const std::string s("AGAATTCG");
    dnaOne.Copy(s);
    EXPECT_EQ(dnaOne.ToString(), s);
    EXPECT_EQ(dnaOne.length, s.size());
    dnaOne.Free();

    const std::string str = "ATGCGGGCCTCGCCG";
    dnaOne.Copy(str);

    for (DNALength i = 0; i < str.size(); i++) {
        EXPECT_EQ(dnaOne.seq[i], str[i]);
    }

    // Test operator = (const std::string)
    DNASequence dnaTwo;
    dnaTwo = str;
    for (DNALength i = 0; i < str.size(); i++) {
        EXPECT_EQ(dnaOne.seq[i], str[i]);
    }
}
