// ==========================================================================
//                 SeqAn - The Library for Sequence Analysis
// ==========================================================================
// Copyright (c) 2006-2013, Knut Reinert, FU Berlin
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
//     * Redistributions of source code must retain the above copyright
//       notice, this list of conditions and the following disclaimer.
//     * Redistributions in binary form must reproduce the above copyright
//       notice, this list of conditions and the following disclaimer in the
//       documentation and/or other materials provided with the distribution.
//     * Neither the name of Knut Reinert or the FU Berlin nor the names of
//       its contributors may be used to endorse or promote products derived
//       from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL KNUT REINERT OR THE FU BERLIN BE LIABLE
// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
// OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
// DAMAGE.
//
// ==========================================================================

#ifndef SEQAN_HEADER_TEST_GRAPH_TCOFFEE_H
#define SEQAN_HEADER_TEST_GRAPH_TCOFFEE_H

#include <seqan/random.h>

using namespace seqan;



//////////////////////////////////////////////////////////////////////////////

void Test_Distances() {
	typedef String<AminoAcid> TString;
	typedef StringSet<TString, Dependent<> > TStringSet;
	typedef Graph<Alignment<TStringSet, unsigned int> > TGraph;
	
	TString str1 = "GARFIELDTHELASTFATCAT";
	TString str2 = "GARFIELDTHEFASTCAT";
	TString str3 = "GARFIELDTHEVERYFASTCAT";
	TString str4 = "THEFATCAT";
	TStringSet strSet;
	assignValueById(strSet, str1);
	assignValueById(strSet, str2);
	assignValueById(strSet, str3);
	assignValueById(strSet, str4);
	TGraph g(strSet);

	String<double> distanceMatrix;
	getDistanceMatrix(g,distanceMatrix);
	SEQAN_ASSERT(distanceMatrix[3] ==  1.0 - 5.0 / 7.0);
	SEQAN_ASSERT(distanceMatrix[1 * length(strSet) + 3] == 1.0 - 5.0 / 7.0);
	SEQAN_ASSERT(distanceMatrix[2 * length(strSet) + 3] == 1.0 - 3.0 / 7.0);
	clear(distanceMatrix);
	String<unsigned int> pList;
	selectPairs(strSet, pList);
	Blosum62 score_type(-1,-11);
	String<Fragment<> > matches;
	String<int> scores;
	String<double> dist;
	appendSegmentMatches(strSet, pList, score_type, matches, scores, dist, GlobalPairwiseLibrary() );
	buildAlignmentGraph(matches, scores, g, FrequencyCounting() );
	getDistanceMatrix(g,distanceMatrix,LibraryDistance());
	SEQAN_ASSERT(getValue(distanceMatrix, 0 * length(strSet) + 1) < getValue(distanceMatrix, 2 * length(strSet) + 3));
	SEQAN_ASSERT(getValue(distanceMatrix, 1 * length(strSet) + 2) < getValue(distanceMatrix, 2 * length(strSet) + 3));
}


//////////////////////////////////////////////////////////////////////////////

void 
testquickAlign__(Graph<Alignment<StringSet<String<AminoAcid>, Dependent<> >, unsigned int> >& g) 
{
	Graph<Alignment<StringSet<String<AminoAcid>, Dependent<> >, void, WithoutEdgeId> > gOut(stringSet(g));
	tripletLibraryExtension(g);
	String<double> distForGuideTree;
	getDistanceMatrix(g,distForGuideTree,LibraryDistance());
	Graph<Tree<double> > guideTree;
	njTree(distForGuideTree, guideTree);
	progressiveAlignment(g, guideTree, gOut);
	//std::cout << gOut << std::endl;
	String<char> alignMat;	
	convertAlignment(gOut,alignMat);
	unsigned int len = length(alignMat) / 4;
	SEQAN_ASSERT(String<char>(infix(alignMat, 0, 8)) == "GARFIELD");
	SEQAN_ASSERT(String<char>(infix(alignMat, 1*len + 0, 1*len+8)) == "GARFIELD");
	SEQAN_ASSERT(String<char>(infix(alignMat, 2*len + 0, 2*len+8)) == "GARFIELD");
	SEQAN_ASSERT(String<char>(infix(alignMat, 3*len + 0, 3*len+8)) == "--------");

	//std::cout << gOut << std::endl;
}

void Test_Libraries() {
	typedef String<AminoAcid> TString;
	typedef StringSet<TString, Dependent<> > TStringSet;
	typedef Graph<Alignment<TStringSet, unsigned int> > TGraph;
	
	TString str1 = "GARFIELDTHELASTFATCAT";
	TString str2 = "GARFIELDTHEFASTCAT";
	TString str3 = "GARFIELDTHEVERYFASTCAT";
	TString str4 = "THEFATCAT";
	TStringSet strSet;
	appendValue(strSet, str1);
	appendValue(strSet, str2);
	appendValue(strSet, str3);
	appendValue(strSet, str4);
	String<unsigned int> pList;
	selectPairs(strSet, pList);
	TGraph g(strSet);
	Blosum62 score_type(-1,-11);
	String<Fragment<> > matches;
	String<int> scores;
	appendSegmentMatches(strSet, pList, matches, scores, LcsLibrary() );
	buildAlignmentGraph(matches, scores, g, FrequencyCounting() );
	testquickAlign__(g);
	clear(matches);
	clear(scores);
	appendSegmentMatches(strSet, matches, scores, KmerLibrary() );
	buildAlignmentGraph(matches, scores, g, FrequencyCounting() );
	testquickAlign__(g);
	clear(matches);
	clear(scores);
	appendSegmentMatches(strSet, pList, score_type, matches, scores, LocalPairwiseLibrary() );
	buildAlignmentGraph(matches, scores, g, FrequencyCounting() );
	testquickAlign__(g);
	clear(matches);
	clear(scores);
	Nothing noth;
	appendSegmentMatches(strSet, pList, score_type, matches, scores, noth, GlobalPairwiseLibrary() );
	buildAlignmentGraph(matches, scores, g, FrequencyCounting() );
	testquickAlign__(g);
	clear(matches);
	clear(scores);
	String<double> distanceMatrix;
	appendSegmentMatches(strSet, pList, score_type, matches, scores, distanceMatrix, AlignConfig<false,false,false,false>(), GlobalPairwiseLibrary() );
	buildAlignmentGraph(matches, scores, g, FrequencyCounting() );
	testquickAlign__(g);
	Graph<Undirected<double> > distGraph;
	clear(matches);
	clear(scores);
	appendSegmentMatches(strSet, pList, score_type, matches, scores, distanceMatrix, AlignConfig<false,false,false,false>(), GlobalPairwiseLibrary() );
	buildAlignmentGraph(matches, scores, g, FrequencyCounting() );
	testquickAlign__(g);
}

void Test_ExternalLibraries() {
	typedef String<char> TName;
	typedef StringSet<TName, Owner<> > TNameSet;
	typedef String<AminoAcid> TSequence;
	typedef StringSet<TSequence, Owner<> > TSequenceSet;
	
	TSequenceSet seqSet;
	appendValue(seqSet, "GARFIELDTHELASTFATCAT");
	appendValue(seqSet, "GARFIELDTHEFASTCAT");
	appendValue(seqSet, "GARFIELDTHEVERYFASTCAT");
	appendValue(seqSet, "THEFATCAT");
	String<unsigned int> pList;
	selectPairs(seqSet, pList);
	TNameSet nameSet;
	appendValue(nameSet, "seq1");
	appendValue(nameSet, "seq2");
	appendValue(nameSet, "seq3");
	appendValue(nameSet, "seq4");

	typedef Graph<Alignment<StringSet<TSequence, Dependent<> >, unsigned int> > TGraph;
	TGraph g(seqSet);
	Blosum62 score_type(-1,-11);
	String<Fragment<> > matches;
	String<int> scores;
	Nothing noth;
	appendSegmentMatches(stringSet(g), pList, score_type, matches, scores, noth, GlobalPairwiseLibrary() );
	buildAlignmentGraph(matches, scores, g, FrequencyCounting() );
	//std::cout << g << std::endl;

	// T-Coffee lib format
    char fileName[1024];
    strcpy(fileName, SEQAN_TEMP_FILENAME());

	// Writing
	std::fstream strm;
	strm.open(fileName, std::ios_base::out | std::ios_base::trunc);
	write(strm, g, nameSet, TCoffeeLib());
	strm.close();
	//_debugRefinedMatches(g);
	testquickAlign__(g);

	// Reading
	clear(seqSet);
	clear(nameSet);
	std::fstream strmRead;
	strmRead.open(fileName, std::ios_base::in | std::ios_base::binary);
	read(strmRead,seqSet,nameSet,TCoffeeLib());
	strmRead.close();
	std::cout << g << std::endl;


	// Blast format
    strcpy(fileName, SEQAN_TEMP_FILENAME());

	// Writing
	std::fstream strm2;
	strm2.open(fileName, std::ios_base::out | std::ios_base::trunc);
	write(strm2, g, nameSet, BlastLib());
	strm2.close();

	// Reading
	std::fstream strmRead2;
	strmRead2.open(fileName, std::ios_base::in | std::ios_base::binary);
	clearVertices(g);
	clear(matches);
	clear(scores);
	read(strmRead2, matches, scores, nameSet,BlastLib());
	buildAlignmentGraph(matches, scores, g, FrequencyCounting() );
	strmRead2.close();
    // TODO(holtgrew): Disabled output here, need to compare g before writing out and a g2 after reading in.
	// std::cout << g << std::endl;

	clear(g);
	assignStringSet(g, seqSet);
	clear(matches);
	clear(scores);
	appendSegmentMatches(stringSet(g), pList, score_type, matches, scores, noth, GlobalPairwiseLibrary() );
	buildAlignmentGraph(matches, scores, g, FrequencyCounting() );
	// Blast lib format

	// Writing
    strcpy(fileName, SEQAN_TEMP_FILENAME());
	std::fstream strmBlast;
	strmBlast.open(fileName, std::ios_base::out | std::ios_base::trunc);
	write(strmBlast, g, nameSet, BlastLib());
	strmBlast.close();
	//_debugRefinedMatches(g);
	testquickAlign__(g);
	
	// Reading
	clear(g);
	assignStringSet(g, seqSet);
	std::fstream strmBlastLib;
	strmBlastLib.open(fileName, std::ios_base::in | std::ios_base::binary);
	clear(matches);
	clear(scores);
	read(strmBlastLib, matches, scores, nameSet,BlastLib());
	buildAlignmentGraph(matches, scores, g, FrequencyCounting() );
	strmBlastLib.close();
	//_debugRefinedMatches(g);
	testquickAlign__(g);

}

void Test_TripletExtension() {
	typedef String<char> TName;
	typedef StringSet<TName, Owner<> > TNameSet;
	typedef String<AminoAcid> TSequence;
	typedef StringSet<TSequence, Owner<> > TSequenceSet;
	typedef StringSet<TSequence, Dependent<> > TDependentSequenceSet;

	TSequenceSet seqSet;//1234567890
	appendValue(seqSet, "GARFIELDTHE");
	appendValue(seqSet, "GARFIELDTHE");
	appendValue(seqSet, "GARFIELDTHE");
	appendValue(seqSet, "THE");
	TNameSet nameSet;
	appendValue(nameSet, "seq1");
	appendValue(nameSet, "seq2");
	appendValue(nameSet, "seq3");
	appendValue(nameSet, "seq4");

	// Triplet extension
	typedef Graph<Alignment<TDependentSequenceSet, unsigned int> > TGraph;
	TGraph g(seqSet);
	addEdge(g, addVertex(g, 0, 0, 8), addVertex(g, 1, 0, 8), 40);
	addEdge(g, findVertex(g, 0, 0), addVertex(g, 2, 0, 8), 30);
	addEdge(g, addVertex(g, 1, 8, 3), addVertex(g, 0, 8, 3), 40);
	addEdge(g, findVertex(g, 1, 8), addVertex(g, 2, 8, 3), 30);
	addEdge(g, findVertex(g, 1, 8), addVertex(g, 3, 0, 3), 20);
	addEdge(g, findVertex(g, 2, 8), findVertex(g, 3, 0), 40);
	tripletLibraryExtension(g);
	SEQAN_ASSERT(cargo(findEdge(g, findVertex(g, 1, 0), findVertex(g, 2, 0))) == 30);
	SEQAN_ASSERT(cargo(findEdge(g, findVertex(g, 0, 8), findVertex(g, 3, 0))) == 20);
	SEQAN_ASSERT(cargo(findEdge(g, findVertex(g, 0, 8), findVertex(g, 2, 8))) == 30);
	SEQAN_ASSERT(cargo(findEdge(g, findVertex(g, 2, 8), findVertex(g, 3, 0))) == 60);
}

void Test_SumOfPairsScore()
{
	typedef String<char> TName;
	typedef StringSet<TName, Owner<> > TNameSet;
	typedef String<Dna> TSequence;
	typedef StringSet<TSequence, Owner<> > TSequenceSet;
	typedef StringSet<TSequence, Dependent<> > TDependentSequenceSet;
	typedef Graph<Alignment<TDependentSequenceSet, unsigned int> > TGraph;

	TSequenceSet seqSet;
	appendValue(seqSet, "ACAAGTA");
	appendValue(seqSet, "AA");
	appendValue(seqSet, "ACCTA");
	TNameSet nameSet;
	appendValue(nameSet, "seq1");
	appendValue(nameSet, "seq2");
	appendValue(nameSet, "seq3");

	TGraph g(seqSet);
	Score<int> score_type = Score<int>(5,-4,-2,-10);
	String<double> distanceMatrix;
	String<Fragment<> > matches;
	String<int> scores;
	String<unsigned int> pList;
	selectPairs(seqSet, pList);
	appendSegmentMatches(stringSet(g), pList, score_type, matches, scores, distanceMatrix, GlobalPairwiseLibrary() );
	buildAlignmentGraph(matches, scores, g, FractionalScore() );
	tripletLibraryExtension(g);
	Graph<Tree<double> > guideTree;
	njTree(distanceMatrix, guideTree);
	TGraph gOut(seqSet);
	progressiveAlignment(g, guideTree, gOut);
	SEQAN_ASSERT(sumOfPairsScore(gOut, score_type) == -8);
	SEQAN_ASSERT(sumOfPairsScoreInd(gOut, score_type) == 16);

	seqSet[1] = "AAG";
	Score<int> scType = Score<int>(5,-4,-1,-2);
	clear(distanceMatrix);
	clearVertices(g);
	clear(matches);
	clear(scores);
	appendSegmentMatches(stringSet(g), pList, scType, matches, scores, distanceMatrix, GlobalPairwiseLibrary() );
	buildAlignmentGraph(matches, scores, g, FractionalScore() );
	tripletLibraryExtension(g);
	clear(guideTree);
	njTree(distanceMatrix, guideTree);
	clearVertices(gOut);
	progressiveAlignment(g, guideTree, gOut);
	SEQAN_ASSERT(sumOfPairsScore(gOut, scType) == 20);

	resize(seqSet, 2);
	seqSet[0] = "TTT";
	seqSet[1] = "AAA";
	clear(distanceMatrix);
	clear(g);
	assignStringSet(g, seqSet);
	clear(matches);
	clear(scores);
	selectPairs(seqSet, pList);
	appendSegmentMatches(stringSet(g), pList, scType, matches, scores, distanceMatrix, GlobalPairwiseLibrary() );
	buildAlignmentGraph(matches, scores, g, FractionalScore() );
	tripletLibraryExtension(g);
	clear(guideTree);
	njTree(distanceMatrix, guideTree);
	clear(gOut);
	assignStringSet(gOut, seqSet);
	progressiveAlignment(g, guideTree, gOut);
	SEQAN_ASSERT(sumOfPairsScore(gOut, scType) == -8);

	seqSet[0] = "TTTAAATTT";
	seqSet[1] = "AAA";
	clear(distanceMatrix);
	clearVertices(g);
	clear(matches);
	clear(scores);
	selectPairs(seqSet, pList);
	appendSegmentMatches(stringSet(g), pList, scType, matches, scores, distanceMatrix, GlobalPairwiseLibrary() );
	buildAlignmentGraph(matches, scores, g, FractionalScore() );
	tripletLibraryExtension(g);
	clear(guideTree);
	njTree(distanceMatrix, guideTree);
	clearVertices(gOut);
	progressiveAlignment(g, guideTree, gOut);
	SEQAN_ASSERT(sumOfPairsScore(gOut, scType) == 7);

	seqSet[0] = "AAAAAA";
	seqSet[1] = "TTTAAATTTAAATTT";
	clear(distanceMatrix);
	clearVertices(g);
	clear(matches);
	clear(scores);
	selectPairs(seqSet, pList);
	appendSegmentMatches(stringSet(g), pList, scType, matches, scores, distanceMatrix, GlobalPairwiseLibrary() );
	buildAlignmentGraph(matches, scores, g, FractionalScore() );
	tripletLibraryExtension(g);
	clear(guideTree);
	njTree(distanceMatrix, guideTree);
	clearVertices(gOut);
	progressiveAlignment(g, guideTree, gOut);
	SEQAN_ASSERT(sumOfPairsScore(gOut, scType) == 18);
}


void Test_Progressive() {
	typedef String<char> TName;
	typedef StringSet<TName, Owner<> > TNameSet;
	typedef String<AminoAcid> TSequence;
	typedef StringSet<TSequence, Owner<> > TSequenceSet;
	typedef StringSet<TSequence, Dependent<> > TDependentSequenceSet;
	
	TSequenceSet seqSet;
	appendValue(seqSet, "GARFIELDTHELASTFATCAT");
	appendValue(seqSet, "GARFIELDTHEFASTCAT");
	appendValue(seqSet, "GARFIELDTHEVERYFASTCAT");
	appendValue(seqSet, "THEFATCAT");
	appendValue(seqSet, "GARFIELDTHELASTFATCAT");
	appendValue(seqSet, "GARFIELDTHEFASTCAT");
	appendValue(seqSet, "GARFIELDTHEVERYFASTCAT");
	appendValue(seqSet, "THEFATCAT");
	TNameSet nameSet;
	appendValue(nameSet, "seq1");
	appendValue(nameSet, "seq2");
	appendValue(nameSet, "seq3");
	appendValue(nameSet, "seq4");
	appendValue(nameSet, "seq5");
	appendValue(nameSet, "seq6");
	appendValue(nameSet, "seq7");
	appendValue(nameSet, "seq8");

	typedef Graph<Alignment<TDependentSequenceSet, unsigned int> > TGraph;
	TGraph g(seqSet);
	Blosum62 score_type(-1,-11);
	String<double> distanceMatrix;
	String<Fragment<> > matches;
	String<int> scores;
	String<unsigned int> pList;
	selectPairs(seqSet, pList);
	appendSegmentMatches(stringSet(g), pList, score_type, matches, scores, distanceMatrix, GlobalPairwiseLibrary() );
	buildAlignmentGraph(matches, scores, g, FractionalScore() );
	tripletLibraryExtension(g);
	Graph<Tree<double> > guideTree;
	njTree(distanceMatrix, guideTree);
	TGraph gOut(seqSet);
	progressiveAlignment(g, guideTree, gOut);
	sumOfPairsScore(gOut, score_type);
}


void Test_ReversableFragments() {
	typedef unsigned int TSize;
	typedef String<Dna> TSequence;
	TSequence seq1 = "AACGTT";
	TSequence seq2 = "AACGTTC";
	typedef StringSet<TSequence, Dependent<> > TDepSequenceSet;
	TDepSequenceSet strSet;
	appendValue(strSet, seq1);
	appendValue(strSet, seq2);
	typedef Fragment<TSize, ExactReversableFragment<> > TFragment;
	typedef String<TFragment> TFragmentString;
	TFragmentString matches;
	appendValue(matches, TFragment(0,0,1,0,2));
	appendValue(matches, TFragment(0,3,1,3,3));
	appendValue(matches, TFragment(0,1,1,2,3, true));
	typedef Graph<Alignment<TDepSequenceSet, TSize> > TGraph;
	TGraph g(strSet);
	matchRefinement(matches,strSet,g);
	SEQAN_ASSERT(findEdge(g, findVertex(g, 0, 1), findVertex(g, 1, 2)) == 0);
	SEQAN_ASSERT(findEdge(g, findVertex(g, 0, 1), findVertex(g, 1, 4)) != 0);
	SEQAN_ASSERT(findEdge(g, findVertex(g, 0, 2), findVertex(g, 1, 3)) != 0);
	SEQAN_ASSERT(findEdge(g, findVertex(g, 0, 3), findVertex(g, 1, 2)) != 0);
	SEQAN_ASSERT(findEdge(g, findVertex(g, 0, 3), findVertex(g, 1, 4)) == 0);
	SEQAN_ASSERT(findEdge(g, findVertex(g, 0, 0), findVertex(g, 1, 0)) != 0);
	SEQAN_ASSERT(findEdge(g, findVertex(g, 0, 1), findVertex(g, 1, 1)) != 0);
}

SEQAN_DEFINE_TEST(test_distances)
{
	Test_Distances();
}
	
SEQAN_DEFINE_TEST(test_libraries)
{
	Test_Libraries();	
}
SEQAN_DEFINE_TEST(test_external_libraries)
{
	Test_ExternalLibraries();	
}
SEQAN_DEFINE_TEST(test_triplet_extension)
{
	Test_TripletExtension();	
}
SEQAN_DEFINE_TEST(test_sop)
{
	Test_SumOfPairsScore();
}
SEQAN_DEFINE_TEST(test_progressive)
{
	Test_Progressive();
}
SEQAN_DEFINE_TEST(test_reversable_fragments)
{
	Test_ReversableFragments();
}

#endif

