// $Id: Sequence.cpp,v 1.14 2018/01/03 21:32:57 mkkuhner Exp $

/*
Copyright 2002-2005 Mary K. Kuhner, Peter Beerli, and Joseph Felsenstein

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

*/

/* Authors: Patrick Colacurcio, Peter Beerli, Mary Kuhner, Jon Yamato 
/* Authors: Patrick Colacurcio, Peter Beerli, Mary Kuhner, Jon Yamato 
and Joseph Felsenstein */


#include "Converter_Sequence.h"
#include "Converter_DataSourceException.h"
#include "random.h"

#ifdef DMALLOC_FUNC_CHECK
#include "/usr/local/include/dmalloc.h"
#endif

using namespace std;

//------------------------------------------------------------------------------------

Sequence::Sequence (const string &src)
{
    m_sequence = AsStringVec(src);
    trimSequence();
    string sequence = asString();
    validate(sequence);
    setDataType ( "DNA" );  // Default Data Type
    Random random; // uses system clock
    setName ( random.Name() );
}                               // Sequence::Sequence

Sequence::Sequence (const string &src,
                    const string &dataType,
                    const string &name)
{
    if (dataType == lamarcstrings::DNA || dataType == lamarcstrings::SNP)
    {
        m_sequence = AsStringVec(src);
        trimSequence();
    }
    else if (dataType == lamarcstrings::MICROSAT || dataType == lamarcstrings::KALLELE)
    {
        m_sequence.push_back(src);
    }

    if (dataType == lamarcstrings::DNA)
    {
        string sequence = asString();
        validate(sequence);
    }

    setDataType ( dataType );
    setName ( name );
}                               // Sequence::Sequence

Sequence::Sequence (const vector<string> &src,
                    const string &dataType,
                    const string &name)
{
    m_sequence = src;
    if (dataType == lamarcstrings::DNA || dataType == lamarcstrings::SNP ) trimSequence();

    if (dataType == lamarcstrings::DNA)
    {
        string sequence = asString();
        validate(sequence);
    }

    setDataType ( dataType );
    setName ( name );
}                               // Sequence::Sequence

//------------------------------------------------------------------------------------

Sequence::~Sequence ()
{
}                               // Sequence::~Sequence

//------------------------------------------------------------------------------------

// If only the standard allowed for a string::string(char&) ctor,
// then we'd just use
//   vector<string>::assign(src.begin(),src.end())
// in place of this whole messy function!
vector<string> Sequence::AsStringVec(const string& src) const
{
    vector<string> sequence;
    string::const_iterator marker;
    for(marker = src.begin(); marker != src.end(); ++marker)
        sequence.push_back(string(1L,*marker));

    return sequence;

} // AsStringVec

//------------------------------------------------------------------------------------

string
Sequence::asString() const
{
    string sequence;
    vector<string>::const_iterator seq;
    for(seq = m_sequence.begin(); seq != m_sequence.end(); ++seq)
        sequence += *seq;
    return sequence;
}

// ____________________________________________________

long
Sequence::getSequenceLength() const
{
    long length = m_sequence.size();
    return length;
}

void
Sequence::setDataType( const string &dataType)
{
    m_dataType = dataType;
}

void
Sequence::validate(const string &src) const
{
    int n = src.length();
    int illegalPosition;

    illegalPosition = src.find_first_not_of ("agctumrwsykvhdbnox?-AGCTUMRWSYKVHDBNOX");

    if ((illegalPosition >= 0) && (illegalPosition < n))
    {
        {
            string err
                ("Invalid character in sequence: ");
            err += src[illegalPosition];
            err += ";";

            throw InvalidNucleotideError(err);
        }

    }
}

void
Sequence::trimSequence()
{
    string sequence = asString();
    // Gets rid of the whitespace
    size_t beg = sequence.find_first_not_of(" \n\t");
    size_t end = sequence.find_last_not_of(" \n\t");

    // Mary fix:  off by one error (STL uses half-open intervals) 2/20/02
    sequence = sequence.substr(beg, end - beg + 1);

    m_sequence = AsStringVec(sequence);
}

string
Sequence::getXML(unsigned int numTabs) const
{
    string sequenceXML;

    addTabs(numTabs, sequenceXML);
    sequenceXML += "<sample name=\"" + m_name + "\">\n";

    ++numTabs;
    addTabs(numTabs, sequenceXML);
    sequenceXML += "<datablock type=\"" + m_dataType + "\">\n";

    ++numTabs;
    addTabs(numTabs, sequenceXML);
    string sequence;
    if (m_dataType == lamarcstrings::DNA || m_dataType == lamarcstrings::SNP)
        sequence = asString();
    else
    {
        vector<string>::const_iterator mseq;
        for(mseq = m_sequence.begin(); mseq != m_sequence.end(); ++mseq)
        {
            sequence += *mseq + " ";
        }
    }
    sequenceXML += sequence;
    sequenceXML += "\n";

    --numTabs;
    addTabs(numTabs, sequenceXML);
    sequenceXML += "</datablock>\n";

    --numTabs;
    addTabs(numTabs, sequenceXML);
    sequenceXML += "</sample>\n";

    return sequenceXML;
}

//____________________________________________________________________________________
