// $Id: PopulationDS.cpp,v 1.23 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 <stdio.h>

#include "Converter_PopulationDS.h"
#include "Converter_DataSourceException.h"
#include "random.h"
#include "stringx.h" // for CaselessStrCmp() in ValidateNewIndividual()

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

using namespace std;

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

PopulationDS::PopulationDS (const string& popName)
    : m_popName(popName),
      m_sequenceLength(0)
{
    // Initially, an empty population.
    // No individuals in the population
}                               // PopulationDS::PopulationDS

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

PopulationDS::PopulationDS (const string& popName,
                            const string& individualName,
                            const string& sequence)
    : m_popName(popName),
      m_sequenceLength (sequence.length())
{
    // Now initialize the Individual.
    IndividualDS thisIndividual(individualName, sequence);

    // Push it onto the vector.
    m_individuals.push_back(thisIndividual);
}                               // PopulationDS::PopulationDS

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

PopulationDS::PopulationDS (const string& popName,
                            const IndividualDS& ind)
    : m_popName(popName),
      m_sequenceLength(ind.getSequenceLength())
{
    // Push it onto the vector.
    m_individuals.push_back(ind);

}                               // PopulationDS::PopulationDS

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

PopulationDS::~PopulationDS ()
{
}                               // PopulationDS::~PopulationDS

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

list<IndividualDS>::const_iterator
PopulationDS::getFirstIndividual() const
{
    return m_individuals.begin();
}

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

list<IndividualDS>::const_iterator
PopulationDS::getLastIndividual() const
{
    return m_individuals.end();
}

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

string
PopulationDS::getName() const
{
    return m_popName;
}

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

void
PopulationDS::setName(const string& name)
{
    m_popName = name;
}

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

void
PopulationDS::setFirstSequenceLength(const int sequenceLength)
{
    if (m_sequenceLength == 0)
    {
        m_sequenceLength = sequenceLength;
    }
}

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

int
PopulationDS::getSequenceLength() const
{
    return m_sequenceLength;
}

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

int
PopulationDS::getNumberOfIndividuals() const
{
    return m_individuals.size();
}

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

long PopulationDS::GetNumberOfOTUs() const
{
    long count = 0;
    list<IndividualDS>::const_iterator ind;
    for(ind = getFirstIndividual(); ind != getLastIndividual(); ++ind)
    {
        count += ind->GetNumberOfSequences();
    }

    return count;

} // PopulationDS::GetNumberOfOTUs

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

vector<string> PopulationDS::GetAllSeqNames() const
{
    vector<string> seqnames;

    list<IndividualDS>::const_iterator ind;
    for(ind = getFirstIndividual(); ind != getLastIndividual(); ++ind)
    {
        vector<string> isnames = ind->GetAllSeqNames();
        seqnames.insert(seqnames.end(),isnames.begin(),isnames.end());
    }

    return seqnames;

} // PopulationDS::GetAllSeqNames

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

void
PopulationDS::setComment(const string& comment)
{
    m_comment = comment;
}

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

void
PopulationDS::addIndividual(IndividualDS individual)
{
    // First validate the individual
    validateNewIndividual(individual);

    // Next, check to see if this individual is already present
    if (doesIndividualNameExist(individual.getName()))
    {
        std::string msg = "Two or more samples with name \""
            + individual.getName()
            + "\" were found for Population \""
            + getName()
            + "\".  Either you have added the same file twice "
            + "(not legal) or you need to change your sample names.";
#ifdef GUI
        throw InconsistentDataError(msg);
#else
        cout << endl << msg << endl;
        // We've already got this individual, so return
        exit(1);
        return;
#endif
    }

    // Otherwise, validate it and shove it into the vector
    m_individuals.push_back(individual);
}

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

void
PopulationDS::addIndividual(const string& name,
                            const Sequence& seq)
{
    IndividualDS individual(name, seq);

    // Next, check to see if this individual is already present
    validateNewIndividual(individual);

    if (doesIndividualNameExist(individual.getName()))
    {
        // We've already got this individual, so return
        return;
    }

    // Otherwise, validate it and shove it into the vector
    m_individuals.push_back(individual);
}

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

void PopulationDS::AddIndividuals(list<IndividualDS> individuals)
{
    list<IndividualDS>::iterator indiv;
    for(indiv = individuals.begin(); indiv != individuals.end(); ++indiv)
    {
        addIndividual(*indiv);
    }

} // PopulationDS::AddIndividuals

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

string
PopulationDS::getUniqueName() const
{
    // Use the random in lamarc/lib/
    Random random; // uses system clock

    int char1 = abs(random.Long() % 26) + 65;
    int char2 = abs(random.Long() % 26) + 65;
    int char3 = abs(random.Long() % 26) + 65;
    int char4 = abs(random.Long() % 26) + 65;
    char name[6];

    sprintf(name, "%c%c%c%c", char1, char2, char3, char4);

    if (doesIndividualNameExist((string)name))
    {
        return getUniqueName();
    }

    return string(name);
}

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

void
PopulationDS::validateNewIndividual(IndividualDS& ind)
{
    //  First, if the name of the individual is whitespace or an empty string, get it a new name
    string indName = ind.getName();
    int strPosition = indName.find_first_not_of (" ");

    if ((strPosition >= 0) && (strPosition < (int)indName.length()))
    {
        // Do nothing
    }
    else
    {
        // Its an anonymous individual, so get a unique name for it and set the name
        string newName = getUniqueName();
        ind.setName(newName);
    }

    // Now, do the validation stuff.
    if (m_sequenceLength == 0)
    {
        m_sequenceLength = ind.getSequenceLength();
        return;
    }
    else if (ind.getSequenceLength() == 0)
    {
        return;
    }
    else if (ind.getSequenceLength() != m_sequenceLength)
    {
        throw InvalidSequenceLengthError("Tried to add a new sequence to a population with a different \
length from the other sequences.");
    }

    return;
}

//  TODO.  Ripe for optimization
bool
PopulationDS::doesIndividualNameExist(const string& name) const
{
    list<IndividualDS>::const_iterator i;

    for (i = getFirstIndividual(); i != getLastIndividual(); i++)
    {
        if(CaselessStrCmp(i->getName(),name))
            return true;
    }
    return false;
}

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

Sequence PopulationDS::PopSequence(const string& seqname,
                                   list<IndividualDS>::iterator individual)
{
    Sequence sequence = individual->PopSequence(seqname);

    if (individual->HasNoSequences())
        m_individuals.erase(individual);

    return sequence;

} // PopulationDS::PopSequence

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

vector< pair<string,list<IndividualDS>::iterator> >
PopulationDS::FindAllSequences(const vector<string>& seqnames)
{
    vector< pair<string,list<IndividualDS>::iterator> > seqpairs;

    vector<string>::const_iterator seqname;
    for(seqname = seqnames.begin();
        seqname != seqnames.end(); ++seqname)
    {
        list<IndividualDS>::iterator individual;
        for(individual = m_individuals.begin();
            individual != m_individuals.end(); ++individual)
        {
            if (individual->HasSequence(*seqname))
            {
                seqpairs.push_back(make_pair(*seqname,individual));
                break;
            }
        }
    }

    if (seqnames.size() != seqpairs.size()) seqpairs.clear();

    return seqpairs;

} // PopulationDS::FindAllSequences

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

void PopulationDS::EraseIndividuals()
{
    m_individuals.clear();
} // PopulationDS::EraseIndividuals

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

bool PopulationDS::IsGhost() const
{
    return m_individuals.empty();
} // PopulationDS::IsGhost

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

bool PopulationDS::HasNonContiguousData() const
{
    list<IndividualDS>::const_iterator ind;
    for(ind = getFirstIndividual(); ind != getLastIndividual(); ++ind)
    {
        if (ind->HasNonContiguousData())
            return true;
    }

    return false;

} // PopulationDS::HasNonContiguousData

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

bool PopulationDS::HasSNPs() const
{
    list<IndividualDS>::const_iterator ind;
    for(ind = getFirstIndividual(); ind != getLastIndividual(); ++ind)
    {
        if (ind->HasSNPs())
            return true;
    }

    return false;

} // PopulationDS::HasSNPs

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

string
PopulationDS::getXML(unsigned int numTabs) const
{
    string populationXML;

    addTabs(numTabs, populationXML);
    populationXML = populationXML + "<population name=\"" + m_popName + "\">\n";

    //  If there's a comment, lets write it out.
    if (m_comment != "")
    {
        addTabs(numTabs, populationXML);
        populationXML += "<!--  " + m_comment + " -->\n";
    }

    //  Now, lets write out all of this populations individuals.
    ++numTabs;
    list<IndividualDS>::const_iterator i;
    for (i = getFirstIndividual(); i != getLastIndividual(); i++)
    {
        populationXML+= i->getXML(numTabs);
    }

    --numTabs;
    addTabs(numTabs, populationXML);
    populationXML += "</population>\n";

    return populationXML;
}

//____________________________________________________________________________________
