// $Id: toxml.cpp,v 1.26 2018/01/03 21:33:06 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: Peter Beerli, Mary Kuhner, Jon Yamato and Joseph Felsenstein */


#include <cassert>

#include "toxml.h"
#include "stringx.h"      // for MakeTag/MakeIndent/etc in foo::ToXML()
#include "xml_strings.h"  // for xmlstr::foo in foo::ToXML()
#include "registry.h"     // for GetForceSummary in SampleXML::ToXML()
#include "forcesummary.h" // for GetForceByTag in SampleXML::ToXML()
#include "force.h"        // for PartitionForce in SampleXML::ToXML()
#include "region.h"

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

using namespace std;

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

PopulationXML::PopulationXML(const Region& region, const string& name)
    : m_name(name)
{
    // break out population-specific vector<tipdata>
    vector<TipData> popdata;
    long loc;
    for(loc = 0; loc < region.GetNloci(); ++loc)
    {
        vector<TipData> locpopdata(region.GetLocus(loc).
                                   GetPopulationTipData(m_name));
        popdata.insert(popdata.end(),locpopdata.begin(),locpopdata.end());
    }

    // classify and process the tips
    long ind;
    m_panelcount = 0;
    m_panelname = "";
    const TipData* tip;
    for(ind = 0; ind < region.GetNIndividuals(); ++ind)
    {
        tip = IndividualIsIn(popdata, ind);
        if (tip != NULL)
        {
            if (tip->GetDataSource() == dsource_panel)
            {
                // panel members
                // cout << "panel: " << tip->label << endl;
                m_panelcount += 1;
                if (m_panelcount == 1)
                {
                    // extract the panel name
                    // format is "region_population_panelname_p<number>"
                    string fullname = tip->label;
                    int lunder = fullname.find("_");           // end of region name
                    int nunder = fullname.find("_", lunder+1); // end of population name
                    int runder = fullname.rfind("_");          // before p# counter
                    m_panelname = fullname.substr(nunder+1, runder-nunder-1);
                }
            }
            else
            {
                // samples
                // cout << "sample: " << tip->label << endl;
                // pass Individuals to Individual ctor
                m_individuals.push_back(IndividualXML(region,popdata,ind));
            }
        }
    }
} // PopulationXML::ctor

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

const TipData* PopulationXML::IndividualIsIn(const vector<TipData>& popdata, long whichind) const
{
    vector<TipData>::const_iterator tip;
    for(tip = popdata.begin(); tip != popdata.end(); ++tip)
    {
        if (tip->BelongsTo(whichind))
        {
            return &(*tip);  // essentially a cast
        }
    }

    return NULL;
} // PopulationXML::IndividualIsIn

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

StringVec1d PopulationXML::ToXML(unsigned long nspaces) const
{
    assert(!m_name.empty());
    StringVec1d xmllines;

    string line = MakeIndent(MakeTagWithName(xmlstr::XML_TAG_POPULATION,m_name),nspaces);
    xmllines.push_back(line);

    // panel size name and counter
    if (m_panelcount > 0)
    {
        nspaces += INDENT_DEPTH;
        line = MakeIndent(MakeTagWithName(xmlstr::XML_TAG_PANEL,m_panelname),nspaces);
        xmllines.push_back(line);

        nspaces += INDENT_DEPTH;
        line = MakeIndent(MakeTag(xmlstr::XML_TAG_PANELSIZE),nspaces);
        xmllines.push_back(line);

        nspaces += INDENT_DEPTH;
        line = MakeIndent(ToString(m_panelcount),nspaces);
        xmllines.push_back(line);
        nspaces -= INDENT_DEPTH;

        line = MakeIndent(MakeCloseTag(xmlstr::XML_TAG_PANELSIZE),nspaces);
        xmllines.push_back(line);
        nspaces -= INDENT_DEPTH;

        line = MakeIndent(MakeCloseTag(xmlstr::XML_TAG_PANEL),nspaces);
        xmllines.push_back(line);
        nspaces -= INDENT_DEPTH;
    }

    // individual samples
    nspaces += INDENT_DEPTH;
    vector<IndividualXML>::const_iterator ind;
    for(ind = m_individuals.begin(); ind != m_individuals.end(); ++ind)
    {
        StringVec1d indxml(ind->ToXML(nspaces));
        xmllines.insert(xmllines.end(),indxml.begin(),indxml.end());
    }
    nspaces -= INDENT_DEPTH;

    line = MakeIndent(MakeCloseTag(xmlstr::XML_TAG_POPULATION),nspaces);
    xmllines.push_back(line);

    return xmllines;
} // PopulationXML::ToXML

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

IndividualXML::IndividualXML(const Region& region,
                             const vector<TipData>& popdata, long whichind)
    : m_name(region.GetIndividual(whichind).GetName()),
      m_phases(region.GetIndividual(whichind).GetPhaseSites()),
      m_individual(region.GetIndividual(whichind))
{
    Individual testind = region.GetIndividual(whichind); // jrmhack

    // break out individual-specific vector<tipdata>
    vector<TipData> inddata;
    vector<TipData>::const_iterator tip;
    for(tip = popdata.begin(); tip != popdata.end(); ++tip)
    {
        if (tip->BelongsTo(whichind)) inddata.push_back(*tip);
    }

    // parse into haplotypes
    long biggest(FLAGLONG);
    vector<TipData>::iterator hap;
    //Find out how many haplotypes there are
    for(hap = inddata.begin(); hap != inddata.end(); ++hap)
    {
        if (hap->m_hap > biggest)
        {
            biggest = hap->m_hap;
        }
    }
    vector<vector<TipData> > haps(biggest+1);
    vector<TipData>          traits;
    for(hap = inddata.begin(); hap != inddata.end(); ++hap)
    {
        if (!hap->m_nodata)
        {
            //We don't have Samples for m_nodata tips.
            haps[hap->m_hap].push_back(*hap);
        }
    }

    // pass the haplotypes to Sample ctor
    vector<vector<TipData> >::iterator hapit;
    for(hapit = haps.begin(); hapit != haps.end(); ++hapit)
    {
        m_samples.push_back(SampleXML(region,*hapit));
    }

} // IndividualXML::ctor

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

StringVec1d IndividualXML::ToXML(unsigned long nspaces) const
{
    assert(!m_name.empty() && !m_phases.empty());
    StringVec1d xmllines;

    string line = MakeIndent(MakeTagWithName(xmlstr::XML_TAG_INDIVIDUAL,m_name),nspaces);
    xmllines.push_back(line);

    nspaces += INDENT_DEPTH;

    if (!m_phases.empty())
    {
        bool hasAnyPhaseData = false;
        LongVec2d::const_iterator phaseIter;
        for(phaseIter=m_phases.begin(); phaseIter != m_phases.end(); phaseIter++)
        {
            if((*phaseIter).size() > 0)
            {
                hasAnyPhaseData = true;
                break;
            }
        }
        if(hasAnyPhaseData)
        {
            LongVec2d::const_iterator locus;
            for(locus = m_phases.begin(); locus != m_phases.end(); ++locus)
            {
                line = MakeIndent(MakeTagWithType(xmlstr::XML_TAG_PHASE,
                                                  xmlstr::XML_ATTRVALUE_UNKNOWN),nspaces);
                xmllines.push_back(line);
                nspaces += INDENT_DEPTH;
                line = MakeIndent(ToString(*locus),nspaces);
                xmllines.push_back(line);
                nspaces -= INDENT_DEPTH;
                line = MakeIndent(MakeCloseTag(xmlstr::XML_TAG_PHASE),nspaces);
                xmllines.push_back(line);
            }
        }
    }

    StringVec1d traitxml = m_individual.GetTraitXML(nspaces);
    xmllines.insert(xmllines.end(), traitxml.begin(), traitxml.end());

    vector<SampleXML>::const_iterator sample;
    for(sample = m_samples.begin(); sample != m_samples.end(); ++sample)
    {
        StringVec1d samplexml(sample->ToXML(nspaces));
        xmllines.insert(xmllines.end(),samplexml.begin(),samplexml.end());
    }
    nspaces -= INDENT_DEPTH;

    line = MakeIndent(MakeCloseTag(xmlstr::XML_TAG_INDIVIDUAL),nspaces);
    xmllines.push_back(line);

    return xmllines;
} // IndividualXML::ToXML

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

SampleXML::SampleXML(const Region& region, const vector<TipData>& loci) :
    m_stati(loci[0].partitions)
{
    vector<TipData>::const_iterator loc;
    for(loc = loci.begin(); loc != loci.end(); ++loc)
    {
        assert(m_stati == loc->partitions);
        DataType_ptr mydatatype(region.GetLocus(loc->m_locus).GetDataTypePtr());
        m_geneticdatatype.push_back(mydatatype->GetXMLTag());
        m_geneticdata.push_back(loc->GetFormattedData(mydatatype->GetDelimiter()));
        m_geneticdatasource.push_back(loc->GetDataSource());
    }
    // JDEBUG--is this an appropiate name?
    m_name = loci[0].label;

} // SampleXML::ctor

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

StringVec1d SampleXML::ToXML(unsigned long nspaces) const
{
    assert(!m_name.empty() && !m_geneticdata.empty());
    StringVec1d xmllines;

    string line = MakeIndent(MakeTagWithName(xmlstr::XML_TAG_SAMPLE,m_name),nspaces);
    xmllines.push_back(line);
    nspaces += INDENT_DEPTH;
    StringVec1d::const_iterator gdata, gtype;
    DataSourceVec1d::const_iterator gsource;
    assert(m_geneticdata.size() == m_geneticdatatype.size());
    assert(m_geneticdata.size() == m_geneticdatasource.size());
    for(gdata = m_geneticdata.begin(), gtype = m_geneticdatatype.begin(), gsource = m_geneticdatasource.begin();
        gdata != m_geneticdata.end(); ++gdata, ++gtype, ++gsource)
    {
        line = MakeIndent(MakeTagWithType(xmlstr::XML_TAG_DATABLOCK,*gtype),nspaces);
        xmllines.push_back(line);
        nspaces += INDENT_DEPTH;
        xmllines.push_back(MakeIndent(*gdata,nspaces));
        nspaces -= INDENT_DEPTH;
        line = MakeIndent(MakeCloseTag(xmlstr::XML_TAG_DATABLOCK),nspaces);
        xmllines.push_back(line);
    }
    // JDEBUG -- evil kludgy if-code to handle the fact that migration partitions are
    // handled differently from all other partitions in the xml
    if (m_stati.size() > 1 ||
        (m_stati.size() == 1 &&
         m_stati.find(force_MIG) == m_stati.end() &&
         m_stati.find(force_DIVMIG) == m_stati.end()))
    {
        line = MakeIndent(MakeTag(xmlstr::XML_TAG_STATUS),nspaces);
        xmllines.push_back(line);
        nspaces += INDENT_DEPTH;
        map<force_type,string>::const_iterator status;
        for(status = m_stati.begin(); status != m_stati.end(); ++status)
        {
            line = dynamic_cast<PartitionForce*>
                ( *(registry.GetForceSummary().GetForceByTag(status->first)) )->
                MakeStatusXML(status->second);

            if (!line.empty())
                xmllines.push_back(MakeIndent(line,nspaces));
        }
        nspaces -= INDENT_DEPTH;
        line = MakeIndent(MakeCloseTag(xmlstr::XML_TAG_STATUS),nspaces);
        xmllines.push_back(line);
    }
    nspaces -= INDENT_DEPTH;
    line = MakeIndent(MakeCloseTag(xmlstr::XML_TAG_SAMPLE),nspaces);
    xmllines.push_back(line);

    return xmllines;
} // SampleXML::ToXML

//____________________________________________________________________________________
