// $Id: Converter_RegionDS.h,v 1.20 2018/01/03 21:32:56 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 */


// RegionDS is a representation of the data on any one region.
// Each region owns the following.
// A Model
// A number of Uniquely Named Populations.

// Validation is done whenever a new population is added.  Region makes sure that
// population Sequences are always the same length.
// If they're different lengths, an InvalidSequenceLengthError is tossed.
// Region also validates that all Population Names differ.

// The vector<PopulationDS>getPopulationNames() method returns a vector of Population Names.
// The method getPopulation(const string& name) will return a particular Population..
// The method string getName() returns the region name as a string.  (may be "")

// The method addNewPopulation(const Population& pop) is Present
// This method will add the new population if it doesn't exist.  If it does already exist,
// it will merge individual information.  (For example, if it has an individual named jerry,
// and the current population in the DS doesn't, it will be added.

// Again, this can throw if the sequences within don't match the sequences of existing populations
// in the region.

// Please note that I'm not putting in functions that do things like remove a population or anything.
// This is possible, even easy, its just that I have no need for that type of thing right now.
// Feel free to add these types of functions if you like.

#ifndef CONVERTER_REGIONDS_H
#define CONVERTER_REGIONDS_H

#include <vector>
#include <map>

#include "Converter_DataSourceIf.h"
#include "Converter_PopulationDS.h"
#include "Converter_ModelDS.h"
#include "Converter_SpacingDS.h"
#include "Converter_types.h" // for map typedefs

using std::string;

//  Include a structure that will allow ordering of the Population Map.
//  Should this be inside the class?

struct ltstr
{
    bool operator()(const string& s1, const string& s2)
    {
        return (s1 > s2 ? 0 : 1);
    }
};

class RegionDS : public DataSourceIf
{
  private:
    string m_regionName;
    ModelDS m_model;
    SpacingDS m_spacing;
    PopMap m_pops;

    RegionDS();                                // undefined always starts with something...
    //  getUniqueName will provide unique names for populations that are provided without a name.
    //  The Name will look like 'Population XXXX' where X is a capital letter tween A and Z.
    string getUniqueName() const;

    bool doesPopulationNameExist(const string& name) const;

    // Validation for RegionDS
    void validateNewPopulation(PopulationDS& population);
    void validateFreqs(double a, double g, double c, double t);

    // validatePopulationName will make sure the population name is not just whitespace or empty
    // string.  If it is, a unique name is provided for the population.
    void validatePopulationName(PopulationDS& pop) const;

    // Validation for RegionDS
  public:
    //  Note.  Constructors may throw ConverterBaseError's
    RegionDS(const string& regionName,
             const ModelDS& model);
    RegionDS(const string& regionName,
             const ModelDS& model,
             PopulationDS& population);

    virtual ~RegionDS();

    // Use the Default Copy Constructor.  Will this be a bug?

    //  get an iterator to the populations contained by this region.
    popmapiterator getFirstPopulation();
    PopMap::const_iterator getFirstPopulation() const;

    //  get an iterator to the end of the population list.
    popmapiterator getLastPopulation();
    PopMap::const_iterator getLastPopulation() const;

    //  get the name of the region
    string getName() const;

    //  get the number of markers for the region
    //  when loci show up, this function will take an argument
    //  identifying which locus is being asked for
    long getNmarkers() const;

    //  get the number of populations for the region
    long GetNPopulations() const;

    //  get the number of non-ghost populations for the region
    long GetNRealPopulations() const;

    //  get all the sequence names from the region, in the order they
    //  "appear" within the populations
    std::vector<string> GetAllSeqNames() const;

    //  get the data model for the region
    const ModelDS& getDataModel() const { return m_model; };

    //  set the name of the region
    //  TODO, make this private and a friend of LamarcDS.
    void setName(const string& name);

    //  set the model.  This will allow the user to edit the
    //  region's basefreqs, ttratio, and categories parameters.
    void setModel(const ModelDS& model);

    //  set the spacing information
    void setSpacing (const SpacingDS& spacing);

    //  Get the number of populations held by this region
    unsigned int getNumPopulations() const;

    //  add an population (this may throw an InvalidSequenceLengthError)
    void addPopulation (PopulationDS& population);

    //  tries to add the argument to all the populations, return !success
    //  also sets the argument popname to the population name we'd like to
    //  add the individual to.
    bool FailToAdd(IndividualDS& individual, string& popname);

    //  is non-contiguous data present?
    bool HasNonContiguousData() const;

    //  are there SNPs present?
    bool HasSNPs() const;

    //  add an individual to the named population
    void AddIndividual(IndividualDS& ind, const string& popname);

    string getXML(unsigned int numTabs) const;   //  From DataSourceIf
};

#endif // CONVERTER_REGIONDS_H

//____________________________________________________________________________________
