// $Id: SpacingDS.cpp,v 1.17 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 <algorithm>

#include "Converter_SpacingDS.h"
#include "Converter_DataSourceException.h"
#include "stringx.h"

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

SpacingDS::SpacingDS()
    : DataSourceIf(),
      m_length(0),
      m_offset(1),
      m_map_position(1)
{
} // SpacingDS::default ctor

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

void SpacingDS::CheckConsistency(long nmarkers) const
{
    if (m_positions.back() - m_positions.front() >= m_length)
    {
#if 0
        string errormsg = "The input length is less than the length ";
        errormsg += "covered by the markers.";
        throw InconsistentDataError(errormsg);
#endif
        throw MarkerLengthMismatchDataError(  m_positions.front(),
                                              m_positions.back(),
                                              m_length);
    }

#if 0
    if (m_offset > m_positions.front() || m_offset + m_length <= m_positions.back())
    {
        string errormsg = "The given start of the region places some markers ";
        errormsg += "outside of the region.";
        throw InconsistentDataError(errormsg);
    }
#endif

    if (m_offset > m_positions.front())
    {
        throw OffsetAfterFirstPositionDataError(m_offset,m_positions.front());
    }

    if (m_offset + m_length <= m_positions.back())
    {
        throw RegionEndBeforeLastPositionDataError(m_offset,m_length,m_positions.back());
    }

    long npositions = static_cast<long>(m_positions.size());

    if (nmarkers != npositions)
    {
#if 0
        string errormsg = "There are " + ToString(nmarkers);
        errormsg += " markers in your data and ";
        errormsg += ToString(npositions) + " positions for them to occupy.";

        throw InconsistentDataError(errormsg);
#endif

        throw MarkerPositionMismatchDataError(nmarkers,npositions);
    }

} // SpacingDS::CheckInternalConsistency

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

SpacingDS::SpacingDS(long length, long nmarkers) : DataSourceIf(), m_length(length),
                                                   m_offset(1), m_map_position(1)
{
    // This constructor makes a SpacingDS when no information except
    // length is given.  It assumes that all of the markers are lined
    // up at the left end of the given length.  Data files produced
    // in this way MUST NOT BE USED TO INFER RECOMBINATION.  The faculty
    // is provided only for non-recombination uses.

    long i;
    for (i = 0; i < nmarkers; ++i)
    {
        m_positions.push_back(i);
    }

} // SpacingDS::ctor

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

SpacingDS::SpacingDS(const vector<long>& positions, long length, long nmarkers) :
    DataSourceIf(), m_positions(positions), m_length(length)
{
    std::sort(m_positions.begin(), m_positions.end());

    m_offset = 1;
    m_map_position = 1;

    CheckConsistency(nmarkers);

} // SpacingDS::ctor

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

SpacingDS::SpacingDS(const vector<long>& positions, long length,
                     long offset, long map_position, long nmarkers) :
    DataSourceIf(), m_positions(positions), m_length(length), m_offset(offset),
    m_map_position(map_position)
{
    std::sort(m_positions.begin(), m_positions.end());

    CheckConsistency(nmarkers);

} // SpacingDS::maximal ctor

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

SpacingDS::~SpacingDS()
{
} // SpacingDS::default dtor

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

string SpacingDS::getXML(unsigned int numTabs) const
{
    string spaceXML;

    if (m_length == 0) return spaceXML;

    addTabs(numTabs,spaceXML);
    spaceXML += string("<spacing>\n");

    ++numTabs;
    addTabs(numTabs,spaceXML);
    spaceXML += string("<block>\n"); // this will cover loci later, so
    // will have a loop here

    ++numTabs;
    addTabs(numTabs,spaceXML);
    spaceXML += string("<map_position> ") + ToString(m_map_position);
    spaceXML += string(" </map_position>\n");

    addTabs(numTabs,spaceXML);
    spaceXML += string("<length> ") + ToString(m_length);
    spaceXML += string(" </length>\n");

    if (!m_positions.empty())
    {
        addTabs(numTabs,spaceXML);
        spaceXML += string("<locations> ");
        vector<long>::const_iterator pos;
        for(pos = m_positions.begin(); pos != m_positions.end(); ++pos)
        {
            spaceXML += ToString(*pos) + " ";
        }
        spaceXML += "\n";
        addTabs(numTabs,spaceXML);
        spaceXML += string("</locations>\n");
    }

    addTabs(numTabs,spaceXML);
    spaceXML += string("<offset> ") + ToString(m_offset);
    spaceXML += string(" </offset>\n");

    --numTabs;
    addTabs(numTabs,spaceXML);
    spaceXML += string("</block>\n");

    --numTabs;
    addTabs(numTabs,spaceXML);
    spaceXML += string("</spacing>\n");

    return spaceXML;

} // SpacingDS::getXML

//____________________________________________________________________________________
