// $Id: intervaldata.cpp,v 1.26 2018/01/03 21:33:03 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 <fstream>
#include <iostream>

#include "force.h"
#include "intervaldata.h"
#include "range.h"                      // For Link-related typedefs and constants.
#include "runreport.h"
#include "stringx.h"
#include "summary.h"
#include "xmlsum_strings.h"             // for xml sumfile strings

using namespace std;

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

Interval * IntervalData::AddInterval(Interval * prev, double time, const LongVec2d & pk,
                                     const LongVec1d & xk, Linkweight recweight, xpart_t ostat, xpart_t nstat,
                                     long int recpoint, const LongVec1d & picks, force_type type)
{
    // "recweight" is a Link recombination weight (Biglink weight or number of Littlelinks).
    // "recpoint" is a Littlelink recombination location (or FLAGLONG).
    // Intervals are assumed to be in time-sorted order.
    Interval interval(time, pk, xk, recweight, ostat, nstat, recpoint, picks, type);
    m_intervals.push_back(interval);
    Interval * thisinterval = &(m_intervals.back());
    if (prev)
    {
        prev->m_next = thisinterval;
    }
    return thisinterval;
} // AddInterval

//------------------------------------------------------------------------------------
// This AddInterval variant is used when a dummy interval is being added as part
// of fatal attraction prevention code.  It is always called with arguments which
// specify a "nonexistent" interval (enough to enable non-zero recombination rate,
// but "fake" in that it does not "distort" the summary's intervals.

Interval * IntervalData::AddDummyInterval(Interval * prev, Linkweight recweight, xpart_t ostat,
                                          xpart_t nstat, long int recpoint, force_type type)
{
    // We will construct some dummy vectors of appropriate size.
    // NB:  All previous code made them gratuitously big.

    // "recweight" is a Link recombination weight (Biglink weight or number of Littlelinks).
    // This function is always called with "recpoint" either = 0 (recombinant case) or FLAGLONG (non-recombinant case).

    // "fake_xk" is number of lineages per crosspartition.
    xpart_t nxparts = registry.GetDataPack().GetNCrossPartitions();
    LongVec1d fake_xk(nxparts, 0L);

    // "fake_pk" is partition-forces x number of lineages per partition.
    LongVec2d fake_pk;
    const ForceVec& partforces = registry.GetForceSummary().GetPartitionForces();
    unsigned long int i;

    for (i = 0; i < partforces.size(); ++i)
    {
        xpart_t nparts = partforces[i]->GetNParams();
        LongVec1d fake_partlines(nparts, 0L);
        fake_pk.push_back(fake_partlines);
    }

    // MDEBUG:  should this have real info in it??
    // "npicks" is number of local partition-forces.
    long int npicks = registry.GetForceSummary().GetNLocalPartitionForces();
    LongVec1d fake_picks(npicks, 0L);

    return AddInterval(prev, 0.0, fake_pk, fake_xk, recweight, ostat, nstat, recpoint, fake_picks, type);
} // AddDummyInteral

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

void IntervalData::PrintIntervalData() const
{
    list<Interval>::const_iterator it;

    for (it = m_intervals.begin(); it != m_intervals.end(); ++it)
    {
        const Interval& inter = *it;
        cerr << "time " << inter.m_endtime << " recweight " <<
            inter.m_recweight << " old status " << inter.m_oldstatus <<
            " new status " << inter.m_newstatus << " recpoint " << inter.m_recpoint <<
            endl;
        cerr << "xpartition lines are ";
        unsigned long int i;
        for (i = 0; i < inter.m_xpartlines.size(); ++i)
            cerr << inter.m_xpartlines[i] << " ";
        cerr << endl;
        cerr << "partition lines are ";
        for (i = 0; i < inter.m_partlines.size(); ++i)
        {
            unsigned long int j;
            for(j = 0; j < inter.m_partlines[i].size(); ++j)
                cerr << inter.m_partlines[i][j] << " ";
            cerr << endl;
        }
        cerr << "Next is " << inter.m_next << endl;
    }
} // PrintIntervalData

//------------------------------------------------------------------------------------
// WriteIntervalData is used when writing tree summaries and growth
// has been turned on, since the tree summaries cannot be summarized.

void IntervalData::WriteIntervalData(ofstream& sumout) const
{
    list<Interval>::const_iterator it;
    string error;

    for (it = m_intervals.begin(); it != m_intervals.end(); ++it)
    {
        const Interval& inter = *it;

        force_type type = inter.m_type;

        // All forces have a type and an endtime.

        sumout << "\t\t" << xmlsum::FORCE_START << " " << ToString(type) << " "
               << xmlsum::FORCE_END << endl;
        sumout << "\t\t\t" << xmlsum::ENDTIME_START << " "
               << inter.m_endtime << " " << xmlsum::ENDTIME_END << endl;

        // For clarity, we'll do a big if/else loop here for the member
        // variables that are only used for certain forces.

        switch (type)
        {
            case force_COAL:
                // Oldstatus is used for all partition forces and coalescence, not recombination.
                sumout << "\t\t\t" << xmlsum::OLDSTATUS_START << " "
                       << inter.m_oldstatus << " " << xmlsum::OLDSTATUS_END << endl;
                break;

            case force_MIG:
            case force_DIVMIG:
            case force_DISEASE:
                // Oldstatus is used for all partition forces and coalescence, not recombination.
                sumout << "\t\t\t" << xmlsum::OLDSTATUS_START << " "
                       << inter.m_oldstatus << " " << xmlsum::OLDSTATUS_END << endl;

                // Newstatus is used for all partition forces (and not coalescence).
                sumout << "\t\t\t" << xmlsum::NEWSTATUS_START << " "
                       << inter.m_newstatus << " " << xmlsum::NEWSTATUS_END << endl;
                break;

            case force_REC:
                // m_recpoint (Littlelink location) and m_recweight (Link weight) used only for recombination.
                sumout << "\t\t\t" << xmlsum::RECWEIGHT_START << " "
                       << inter.m_recweight << " " << xmlsum::RECWEIGHT_END << endl;
                sumout << "\t\t\t" << xmlsum::RECPOINT_START << " "
                       << inter.m_recpoint << " " << xmlsum::RECPOINT_END << endl;
                // m_partnerpicks is only used for recombination; this vector will be
                // empty if no local partition force is also active.
                if (!inter.m_partnerpicks.empty())
                {
                    sumout << "\t\t\t" << xmlsum::PARTNERPICKS_START << " ";
                    for (unsigned long int i = 0; i < inter.m_partnerpicks.size(); ++i)
                        sumout << inter.m_partnerpicks[i] << " ";
                    sumout << xmlsum::PARTNERPICKS_END << endl;
                }
                break;

            case force_GROW:
                error = "Error:  No interval of type " + lamarcstrings::GROW
                    + " should be possible.  Exiting IntervalData::WriteIntervalData.";
                throw implementation_error(error);
                break;

            default:
                error = "Error:  unknown interval type '" + ToString(type)
                    + "' encountered.  Exiting IntervalData::WriteIntervalData.";
                throw implementation_error(error);
                break;
        }

        // All forces have m_xpartlines.
        sumout << "\t\t\t" << xmlsum::XPARTLINES_START << " ";
        for (unsigned long int i = 0; i < inter.m_xpartlines.size(); ++i)
            sumout << inter.m_xpartlines[i] << " ";
        sumout << xmlsum::XPARTLINES_END << endl;

        // Partlines can be tested vs. their size, so we don't need to check the force type.
        if (inter.m_partlines.size())
        {
            sumout << "\t\t\t" << xmlsum::PARTLINES_START << " ";
            for (unsigned long int i = 0; i < inter.m_partlines.size(); ++i)
            {
                for(unsigned long int j = 0; j < inter.m_partlines[i].size(); ++j)
                    sumout << inter.m_partlines[i][j] << " ";
                sumout << ". ";
            }
            sumout << xmlsum::PARTLINES_END << endl;
        }
    }
} // WriteIntervalData

//____________________________________________________________________________________
