// $Id: gc_datastore_export.cpp,v 1.81 2018/01/03 21:32:55 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: Mary Kuhner, Jon Yamato, and Joseph Felsenstein */


#include <cassert>

#include "gc_data.h"
#include "gc_data_missing_err.h"
#include "gc_datastore.h"
#include "gc_default.h"
#include "gc_errhandling.h"
#include "gc_exportable.h"
#include "gc_genotype_resolution.h"
#include "gc_individual.h"
#include "gc_individual_err.h"
#include "gc_parse_block.h"
#include "gc_parse_sample.h"
#include "gc_phase.h"
#include "gc_population.h"
#include "gc_locus.h"
#include "gc_locus_err.h"
#include "gc_region.h"
#include "gc_sequential_data.h"
#include "gc_strings.h"
#include "gc_strings_locus.h"
#include "gc_structure_maps.h"
#include "gc_structures.h"
#include "gc_trait.h"
#include "gc_types.h"
#include "tinyxml.h"
#include "xml_strings.h"
#include "wx/log.h"
#include "wx/string.h"

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

gcUnphasedMarkers *
GCDataStore::CheckPhaseMarkers( const gcUnphasedMarkers * phaseInfo,
                                wxString            indName,
                                const gcLocus &     locusRef,
                                bool                anyZeroes) const
{
    assert(locusRef.HasLength());

    if(phaseInfo->NumMarkers() > 0)
    {
        long smallest = anyZeroes ? 0 : 1;
        if(locusRef.HasOffset()) smallest = locusRef.GetOffset();

        long largest = smallest + locusRef.GetLength() - 1;
        if(smallest < 0 && largest >= 0 && (!anyZeroes)) largest++;

        if(phaseInfo->Smallest() < smallest)
        {
            throw gc_phase_too_small(   phaseInfo->Smallest(),
                                        indName,
                                        smallest,
                                        locusRef.GetName());
        }

        if(phaseInfo->Largest() > largest)
        {
            throw gc_phase_too_large(   phaseInfo->Largest(),
                                        indName,
                                        largest,
                                        locusRef.GetName());
        }
    }

    phaseInfo->CheckAgainstLocations(locusRef);

    gcUnphasedMarkers * newMarkers = new gcUnphasedMarkers(*phaseInfo);
    if(!anyZeroes)
    {
        newMarkers->ShiftNegsUp();
    }
    return newMarkers;
}

void
GCDataStore::AddDefaultPhaseElem(TiXmlElement * individualElement) const
{
    TiXmlElement * phaseElement = new TiXmlElement( xmlstr::XML_TAG_PHASE.c_str());
    individualElement->LinkEndChild(phaseElement);
    phaseElement->SetAttribute( xmlstr::XML_ATTRTYPE_TYPE.c_str(),
                                xmlstr::XML_ATTRVALUE_UNKNOWN.c_str());
    TiXmlText * phaseText = new TiXmlText("");
    phaseElement->LinkEndChild(phaseText);
}

void
GCDataStore::AddPhaseElem(TiXmlElement * individualElem, const GCIndividual & indi, const gcLocus & locusRef) const
// EWFIX.P3.BUG.537 -- FIXED ?? need to do this at the locus level as well ??
// EWFIX.P4 -- add a locus name attribute in a later release ??
{
    const gcUnphasedMarkers * phaseInfo = indi.GetUnphased(locusRef);
    if(phaseInfo == NULL || phaseInfo->NumMarkers() == 0)
    {
        if (locusRef.HasUnphasedMarkers())
        {
            phaseInfo = locusRef.GetUnphasedMarkers();
        }
    }

    if(phaseInfo == NULL || phaseInfo->NumMarkers() == 0)
    {
        AddDefaultPhaseElem(individualElem);
    }
    else
    {
        // can throw
        bool anyZeroes = GetStructures().AnyZeroes();   // EWFIX.P4 -- wasteful
        gcUnphasedMarkers * newPhaseInfo = CheckPhaseMarkers(phaseInfo,
                                                             indi.GetName(),
                                                             locusRef,
                                                             anyZeroes);

        TiXmlElement * phaseElement = new TiXmlElement( xmlstr::XML_TAG_PHASE.c_str());
        individualElem->LinkEndChild(phaseElement);

        phaseElement->SetAttribute( xmlstr::XML_ATTRTYPE_TYPE.c_str(),
                                    xmlstr::XML_ATTRVALUE_UNKNOWN.c_str());

        TiXmlText * phaseText = new TiXmlText(newPhaseInfo->AsString().c_str());
        delete newPhaseInfo;
        phaseElement->LinkEndChild(phaseText);
    }
}

void
GCDataStore::AddDataBlockElem(TiXmlElement * sampleElement, const GCSequentialData & seqData, const gcLocus & locusRef, size_t siteIndex) const
{
    gcSpecificDataType dataType = locusRef.GetDataType();
    if(dataType == sdatatype_NONE_SET)
    {
        wxString msg = wxString::Format(gcerr::missingDataTypeForLocus,locusRef.GetName().c_str());
        gui_error g(msg.c_str());
        throw g;
    }

    wxString dataTypeString = ToWxString(dataType);
    wxString dataString = " "+seqData.GetData(siteIndex)+" ";

    TiXmlElement * dataBlockElement = new TiXmlElement( xmlstr::XML_TAG_DATABLOCK.c_str());
    sampleElement->LinkEndChild(dataBlockElement);
    dataBlockElement->SetAttribute( xmlstr::XML_ATTRTYPE_TYPE.c_str(),
                                    dataTypeString.c_str());

    TiXmlText * data = new TiXmlText(dataString);
    dataBlockElement->LinkEndChild(data);

}

void
GCDataStore::AddDataBlockElem(TiXmlElement * sampleElem, const GCSequentialData & seqData, const gcLocus & locusRef ) const
{
    gcSpecificDataType dataType = locusRef.GetDataType();
    if(dataType == sdatatype_NONE_SET)
    {
        wxString msg = wxString::Format(gcerr::missingDataTypeForLocus,locusRef.GetName().c_str());
        gui_error g(msg.c_str());
        throw g;
    }

    TiXmlElement * dataBlockElement = new TiXmlElement( xmlstr::XML_TAG_DATABLOCK.c_str());
    sampleElem->LinkEndChild(dataBlockElement);

    wxString dataTypeString = ToWxString(dataType);
    dataBlockElement->SetAttribute( xmlstr::XML_ATTRTYPE_TYPE.c_str(),
                                    dataTypeString.c_str());

    wxString dataString = wxString::Format(" %s ",seqData.GetData().c_str());
    TiXmlText * data = new TiXmlText(dataString);
    dataBlockElement->LinkEndChild(data);

}

void
GCDataStore::AddSampleElem(TiXmlElement * individualElem, const GCIndividual & individual, const gcRegion & regionRef, const gcLocus & locusRef, size_t siteIndex, size_t hapIndex) const
{

    const gcSample * sample = individual.GetSample(hapIndex);
    assert(!locusRef.GetLinked());
    const GCSequentialData & data = sample->GetData(&locusRef);

    TiXmlElement * sampleElement = new TiXmlElement( xmlstr::XML_TAG_SAMPLE.c_str());
    individualElem->LinkEndChild(sampleElement);
    sampleElement->SetAttribute(xmlstr::XML_ATTRTYPE_NAME.c_str(),
                                sample->GetLabel().c_str());

    AddDataBlockElem(sampleElement,data,locusRef,siteIndex);
}

void
GCDataStore::AddSampleElem(TiXmlElement * individualElem, const GCIndividual & individual, const gcRegion & regionRef, size_t hapIndex) const
{
    const gcSample * sample = individual.GetSample(hapIndex);

    TiXmlElement * sampleElement = new TiXmlElement( xmlstr::XML_TAG_SAMPLE.c_str());
    individualElem->LinkEndChild(sampleElement);
    sampleElement->SetAttribute(xmlstr::XML_ATTRTYPE_NAME.c_str(),
                                sample->GetLabel().c_str());

    constObjVector loci = m_structures.GetConstDisplayableLinkedLociInMapOrderFor(regionRef.GetId());
    for(constObjVector::const_iterator i = loci.begin(); i != loci.end(); i++)
    {
        const gcLocus * locP = dynamic_cast<const gcLocus*>(*i);
        assert(locP != NULL);

        const gcLocus & locusRef = *locP;
        if(locusRef.GetLinked())
        {
            try
            {
                const GCSequentialData & data = sample->GetData(locP);
                AddDataBlockElem(sampleElement,data,locusRef);
            }
            catch (const gc_data_error& e)
                // for gc_sample_missing_locus_data
            {
                // EWFIX.P3 -- bad formatting because string e.what()
                // contains quote characters
                wxString msg = wxString::Format(gcerr::inIndividual,
                                                individual.GetName().c_str(),
                                                e.what());
                throw gc_data_error (msg.c_str());
            }
        }
    }
}

void
GCDataStore::AddGenoResolutionElem(TiXmlElement * individualElem, const gcPhenotype & pheno) const
{
    size_t traitId = pheno.GetTraitId();
    const gcTraitInfo & trait = GetStructures().GetTrait(traitId);
    wxString traitName = trait.GetName();

    TiXmlElement * genoResoElement = new TiXmlElement( xmlstr::XML_TAG_GENOTYPE_RESOLUTIONS.c_str());
    individualElem->LinkEndChild(genoResoElement);

    TiXmlElement * traitNameElement = new TiXmlElement( xmlstr::XML_TAG_TRAIT_NAME.c_str());
    genoResoElement->LinkEndChild(traitNameElement);

    TiXmlText * traitNameTextElement = new TiXmlText(traitName.c_str());
    traitNameElement->LinkEndChild(traitNameTextElement);

    const std::vector<gcHapProbability> & haps = pheno.GetHapProbabilities();
    std::vector<gcHapProbability>::const_iterator i;
    for(i = haps.begin(); i != haps.end(); i++)
    {
        const gcHapProbability & hapProb = *i;
        double relProb = hapProb.GetPenetrance();
        const gcIdVec & alleleIds = hapProb.GetAlleleIds();

        TiXmlElement * hapElement = new TiXmlElement( xmlstr::XML_TAG_HAPLOTYPES.c_str());
        genoResoElement->LinkEndChild(hapElement);

        TiXmlElement * relProbElem = new TiXmlElement( xmlstr::XML_TAG_PENETRANCE.c_str());
        hapElement->LinkEndChild(relProbElem);

        TiXmlText * probText = new TiXmlText(wxString::Format("%f",relProb).c_str());
        relProbElem->LinkEndChild(probText);

        TiXmlElement * allelesElement = new TiXmlElement( xmlstr::XML_TAG_ALLELES.c_str());
        hapElement->LinkEndChild(allelesElement);

        wxString alleles = wxEmptyString;
        for(size_t j=0; j < alleleIds.size(); j++)

        {
            if(j == 0)
            {
                alleles += " ";
            }
            const gcTraitAllele & traitAllele = GetStructures().GetAllele(alleleIds[j]);
            alleles += traitAllele.GetName();
            alleles += " ";
        }

        TiXmlText * alleleText = new TiXmlText(alleles.c_str());
        allelesElement->LinkEndChild(alleleText);
    }
}

void
GCDataStore::AddIndividualElem(TiXmlElement * popElem, const GCIndividual & individual, const gcRegion & regionRef, const gcLocus & locusRef, size_t siteIndex) const
{
    wxString indName = individual.GetName();

    TiXmlElement * individualElement = new TiXmlElement( xmlstr::XML_TAG_INDIVIDUAL.c_str());
    popElem->LinkEndChild(individualElement);
    individualElement->SetAttribute(    xmlstr::XML_ATTRTYPE_NAME.c_str(),
                                        indName.c_str());

    // NOTE : we don't put phase info in because this is a single unlinked site
    for(size_t hapIndex=0; hapIndex < individual.GetNumSamples(); hapIndex++)
    {
        AddSampleElem(individualElement,individual,regionRef,locusRef,siteIndex,hapIndex);
    }
}

void
GCDataStore::AddIndividualElem(TiXmlElement * popElem, const GCIndividual & individual, const gcRegion & regionRef) const
{
    wxString indName = individual.GetName();

    TiXmlElement * individualElement = new TiXmlElement( xmlstr::XML_TAG_INDIVIDUAL.c_str());
    popElem->LinkEndChild(individualElement);
    individualElement->SetAttribute(    xmlstr::XML_ATTRTYPE_NAME.c_str(),
                                        indName.c_str());

    const gcIdSet & phenoIds = individual.GetPhenotypeIds();
    for(gcIdSet::const_iterator i=phenoIds.begin(); i!=phenoIds.end(); i++)
    {
        const gcPhenotype & pheno = GetStructures().GetPhenotype(*i);
        AddGenoResolutionElem(individualElement,pheno);
    }

    if(individual.GetNumSamples() > 1)
        // to put in phase elem only
    {
        gcIdVec locIds = GetStructures().GetLocusIdsForRegionByMapPosition(regionRef.GetId());

        for(gcIdVec::const_iterator i = locIds.begin(); i != locIds.end(); i++)
        {
            size_t locusId = (*i);
            const gcLocus & locusRef = m_structures.GetLocus(locusId);
            if(locusRef.GetLinked())
            {
                AddPhaseElem(individualElement,individual,locusRef);
            }
        }

    }

    for(size_t hapIndex=0; hapIndex < individual.GetNumSamples(); hapIndex++)
    {
        AddSampleElem(individualElement,individual,regionRef,hapIndex);
    }
}

void
GCDataStore::AddPanelElem(TiXmlElement * popElem, const gcPanel & panel) const
{
    TiXmlElement * panelElement = new TiXmlElement( xmlstr::XML_TAG_PANEL.c_str());
    popElem->LinkEndChild(panelElement);
    panelElement->SetAttribute(xmlstr::XML_ATTRTYPE_NAME.c_str(), (panel.GetName()).c_str());

    TiXmlElement * sizeElement = new TiXmlElement( xmlstr::XML_TAG_PANELSIZE.c_str());
    panelElement->LinkEndChild(sizeElement);
    wxString sizeString = wxString::Format(" %ld ",panel.GetNumPanels());
    TiXmlText * sizeText = new TiXmlText(sizeString);
    sizeElement->LinkEndChild(sizeText);
}

TiXmlElement *
GCDataStore::MakeBlockElem(const gcLocus & locusRef, bool requireMapPosition) const
{
    TiXmlElement * blockElement  = new TiXmlElement( xmlstr::XML_TAG_BLOCK.c_str());
    blockElement->SetAttribute( xmlstr::XML_ATTRTYPE_NAME.c_str(),
                                locusRef.GetName().c_str());

    if(locusRef.HasOffset())
    {
        wxString offsetString = wxString::Format(" %ld ",locusRef.GetOffset());

        TiXmlElement * offsetElement  = new TiXmlElement( xmlstr::XML_TAG_OFFSET.c_str());
        TiXmlText * offsetText = new TiXmlText(offsetString);
        offsetElement->LinkEndChild(offsetText);
        blockElement->LinkEndChild(offsetElement);
    }

    long mapPosition = 1;
    if(requireMapPosition && !locusRef.HasMapPosition())
    {
        wxString msg = wxString::Format(gcerr::locusWithoutMapPosition,
                                        locusRef.GetName().c_str());
        gui_error g(msg);
        throw g;
    }
    if(locusRef.HasMapPosition())
    {
        mapPosition = locusRef.GetMapPosition();
    }
    wxString mapPosString = wxString::Format(" %ld ",mapPosition);
    TiXmlText * mapPosText = new TiXmlText(mapPosString);
    TiXmlElement * mapPositionElement  = new TiXmlElement( xmlstr::XML_TAG_MAP_POSITION.c_str());
    mapPositionElement->LinkEndChild(mapPosText);
    blockElement->LinkEndChild(mapPositionElement);

    TiXmlElement * lengthElement  = new TiXmlElement( xmlstr::XML_TAG_LENGTH.c_str());
    if(locusRef.HasTotalLength())
    {
        if( ! (locusRef.GetDataType() == sdatatype_DNA && locusRef.GetLength() == locusRef.GetNumMarkers()))
        {
            wxString lengthString = wxString::Format(" %ld ",(long)locusRef.GetLength());
            TiXmlText * lengthText = new TiXmlText(lengthString);
            lengthElement->LinkEndChild(lengthText);
            blockElement->LinkEndChild(lengthElement);
        }
    }
    else
    {
        if(locusRef.GetDataType() != sdatatype_DNA && locusRef.GetNumMarkers() > 1)
        {
            delete lengthElement;
            wxString msg = wxString::Format(gcerr::missingLengthForLocus,locusRef.GetName().c_str());
            gui_error g(msg.c_str());
            throw g;
        }
    }

    if(locusRef.HasLocations())
    {
        TiXmlElement * locationsElement  = new TiXmlElement( xmlstr::XML_TAG_LOCATIONS.c_str());
        wxString locationsString = locusRef.GetLocationsAsString();
        TiXmlText * locationsText = new TiXmlText(locationsString);
        locationsElement->LinkEndChild(locationsText);
        blockElement->LinkEndChild(locationsElement);
    }

    return blockElement;
}

TiXmlElement *
GCDataStore::MakeBlockElemWithMapPosition(const gcLocus & locusRef) const
{
    return MakeBlockElem(locusRef,true);
}

TiXmlElement *
GCDataStore::MakeBlockElemWithoutMapPosition(const gcLocus & locusRef) const
{
    return MakeBlockElem(locusRef,false);
}

void
GCDataStore::AddEffectivePopSizeElem(TiXmlElement * regionElem, const gcRegion & regionRef) const
{
    if(regionRef.HasEffectivePopulationSize())
    {
        double psize = regionRef.GetEffectivePopulationSize();
        TiXmlElement * effPopSizeElem = new TiXmlElement( xmlstr::XML_TAG_EFFECTIVE_POPSIZE.c_str());
        regionElem->LinkEndChild(effPopSizeElem);
        TiXmlText * popSizeText = new TiXmlText(wxString::Format("%f",psize).c_str());
        effPopSizeElem->LinkEndChild(popSizeText);
    }
}

#if 0
void
GCDataStore::AddSpacingElemSingle(TiXmlElement * regionElem, const gcLocus & locusRef) const
{
    TiXmlElement * spacingElement = new TiXmlElement( xmlstr::XML_TAG_SPACING.c_str());
    regionElem->LinkEndChild(spacingElement);
    TiXmlElement * blockElement = MakeBlockElemWithoutMapPosition(locusRef);
    spacingElement->LinkEndChild(blockElement);
}
#endif

#if 0 // EWFIX.REMOVE
TiXmlElement *
GCDataStore::MakeSpacingElemMulti(const constObjVector & loci, const gcRegion & regionRef) const
{
    // check that loci don't overlap
    GetStructures().VerifyLocusSeparations(regionRef);

    TiXmlElement * spacingElement = new TiXmlElement( xmlstr::XML_TAG_SPACING.c_str());

    long nextAllowable = gcdefault::badMapPosition;
    wxString lastLocusName;

    for(constObjVector::const_iterator iter = loci.begin(); iter != loci.end(); iter++)
    {
        const gcLocus * locP = dynamic_cast<const gcLocus*>(*iter);
        if(locP == NULL)
        {
            wxString msg = wxString::Format(gcerr::corruptedDisplayableLociInMapOrder);
            gc_implementation_error g(msg.c_str());
            throw g;
        }

        if(!locP->HasMapPosition())
        {
            wxString msg = wxString::Format(gcerr::locusWithoutMapPosition,
                                            locP->GetName().c_str());
            gui_error g(msg);
            throw g;
        }
        if(!locP->HasTotalLength() && ! (locP->GetDataType() == sdatatype_DNA))
        {
            wxString msg = wxString::Format(gcerr::locusWithoutLength,
                                            locP->GetName().c_str());
            gui_error g(msg);
            throw g;
        }
        long mapPosition = locP->GetMapPosition();
        size_t length = locP->GetLength();

        if(iter != loci.begin())
        {
            if(mapPosition < nextAllowable)
            {
                wxString msg = wxString::Format(gcerr::locusOverlap,
                                                lastLocusName.c_str(),
                                                nextAllowable-1,
                                                locP->GetName().c_str(),
                                                mapPosition);
                gui_error g(msg);
                throw g;
            }
        }

        nextAllowable = mapPosition + length;
        lastLocusName = locP->GetName();

        TiXmlElement * blockElement = MakeBlockElemWithMapPosition(*locP);
        spacingElement->LinkEndChild(blockElement);
    }

    return spacingElement;
}
#endif

void
GCDataStore::AddSpacingElem(TiXmlElement * regionElem, const gcRegion & regionRef) const
{
    constObjVector loci = GetStructures().GetConstDisplayableLinkedLociInMapOrderFor(regionRef.GetId());

    bool needMapInfo = (loci.size() > 1);

    bool anyZeroes = GetStructures().AnyZeroes();   // EWFIX.P4 -- wasteful

    TiXmlElement * spacingElement = new TiXmlElement( xmlstr::XML_TAG_SPACING.c_str());
    regionElem->LinkEndChild(spacingElement);

    bool havePrevious = false;
    long lastExtent = 0;
    size_t lastLocusId = 0;

    for(constObjVector::const_iterator i=loci.begin(); i != loci.end(); i++)
    {
        const gcLocus * locusP = dynamic_cast<const gcLocus *>(*i);
        assert(locusP != NULL);

        assert(locusP->GetLinked());

        TiXmlElement * blockElement  = new TiXmlElement( xmlstr::XML_TAG_BLOCK.c_str());
        blockElement->SetAttribute( xmlstr::XML_ATTRTYPE_NAME.c_str(),
                                    locusP->GetName().c_str());

        long start = gcdefault::badMapPosition;
        //////////////////////////////////////////////////////
        // map position
        if(! locusP->HasMapPosition())
        {
            if(needMapInfo)
            {
                // EWFIX.P3 -- should use specific error
                throw gc_locus_err(wxString::Format(gcerr::locusWithoutMapPosition,
                                                    locusP->GetName().c_str()));
            }
        }
        else
        {
            start = locusP->GetMapPosition();
            if(!anyZeroes && (start < 0))
            {
                start++;
            }
            TiXmlElement * mapPositionElement  = new TiXmlElement( xmlstr::XML_TAG_MAP_POSITION.c_str());
            wxString mapPosString = wxString::Format(" %ld ",start);
            TiXmlText * mapPosText = new TiXmlText(mapPosString);
            mapPositionElement->LinkEndChild(mapPosText);
            blockElement->LinkEndChild(mapPositionElement);
        }

        //////////////////////////////////////////////////////
        // length
        if(locusP->HasTotalLength())
        {
            TiXmlElement * lengthElement  = new TiXmlElement( xmlstr::XML_TAG_LENGTH.c_str());
            wxString lengthString = wxString::Format(" %ld ",(long)locusP->GetTotalLength());
            TiXmlText * lengthText = new TiXmlText(lengthString);
            lengthElement->LinkEndChild(lengthText);
            blockElement->LinkEndChild(lengthElement);
        }

        if(havePrevious)
        {
            if(start <= lastExtent)
            {
                long end = locusP->GetMapPosition()+locusP->GetLength()-1;
                long lastStart = GetStructures().GetLocus(lastLocusId).GetMapPosition();
                throw gc_locus_overlap(locusP->GetName(),start,end,
                                       GetStructures().GetLocus(lastLocusId).GetName(),lastStart,lastExtent);
            }
        }

        if(needMapInfo)
        {
            havePrevious = true;
            size_t length = locusP->GetLength();
            long stop = start + length;
            stop -= 1;    // EWFIX explain why
            lastExtent = stop;
            lastLocusId = locusP->GetId();
        }

        //////////////////////////////////////////////////////
        // offset
        long offset = 1;
        if(locusP->HasOffset())
        {
            offset = locusP->GetOffset();

            if(!anyZeroes && (offset < 1))
            {
                offset++;
            }
            start += offset;
            TiXmlElement * offsetElement  = new TiXmlElement( xmlstr::XML_TAG_OFFSET.c_str());
            wxString offsetString = wxString::Format(" %ld ",offset);
            TiXmlText * offsetText = new TiXmlText(offsetString);
            offsetElement->LinkEndChild(offsetText);
            blockElement->LinkEndChild(offsetElement);
        }
        else
        {
            if(locusP->GetDataType() == sdatatype_SNP && locusP->HasLocations())
            {
                // EWFIX.P3 -- should use specific error
                throw gc_locus_err(wxString::Format(gcerr_locus::offsetMissingSnpLocations,locusP->GetName().c_str()));
            }

            if(needMapInfo)
            {
                bool lengthIsMarkers = false;
                if(locusP->HasNumMarkers())
                {
                    if(locusP->HasLength())
                    {
                        lengthIsMarkers = (locusP->GetNumMarkers() == locusP->GetLength());
                    }
                    else
                    {
                        lengthIsMarkers = (locusP->GetNumMarkers() == 1);
                    }
                }
                if(!lengthIsMarkers)
                {
                    throw gc_locus_err(wxString::Format(gcerr_locus::offsetMissingMultiSegment,locusP->GetName().c_str()));
                }
            }

        }

        //////////////////////////////////////////////////////
        // locations
        if(locusP->HasLocations())
        {
            std::vector<long> locations = locusP->GetLocations();
            wxString locationString = " ";

            if(locusP->HasNumMarkers())
            {
                size_t numLocs = locations.size();
                size_t numMarkers = locusP->GetNumMarkers();
                if(numLocs != numMarkers)
                {
                    throw gc_wrong_location_count(  (long)numLocs,
                                                    (long)numMarkers,
                                                    locusP->GetName());
                }
            }

            for(size_t i = 0; i < locations.size(); i++)
            {
                long loc = locations[i];

                // EWFIX.P3 -- mouse cut and paste from similar
                // calculations in phase item
                long smallest = anyZeroes ? 0 : 1;
                if(locusP->HasOffset()) smallest = locusP->GetOffset();

                long largest = smallest + locusP->GetLength() - 1;
                if(smallest < 0 && largest >= 0 && (!anyZeroes)) largest++;

                if(loc < smallest)
                {
                    throw gc_location_too_small(loc,
                                                smallest,
                                                locusP->GetName());
                }

                if(loc > largest)
                {
                    throw gc_location_too_large(loc,
                                                largest,
                                                locusP->GetName());
                }

                if(!anyZeroes && loc < 1)
                {
                    loc++;
                }
                locationString += wxString::Format("%ld ",loc);
            }

            TiXmlElement * locationElement  = new TiXmlElement( xmlstr::XML_TAG_LOCATIONS.c_str());
            TiXmlText * locationText = new TiXmlText(locationString);
            locationElement->LinkEndChild(locationText);
            blockElement->LinkEndChild(locationElement);
        }
        else
            // when num markers > 1
            // dna -- required if length > markers
            // snp -- required for recom
            // msat/kallele -- required for recom
        {
            if(locusP->GetNumMarkers() > 1)
            {
                if(locusP->GetDataType() == sdatatype_DNA)
                {
                    if(locusP->HasTotalLength())
                    {
                        if(locusP->GetTotalLength() > locusP->GetNumMarkers())
                        {
                            throw gc_locus_err(wxString::Format(gcerr_locus::dnaBigLengthNeedsLocations,locusP->GetName().c_str()));
                        }
                    }
                }
                else
                {
                    bool continueOn = guiQuestionBatchLog(wxString::Format(gcstr::locationsForRecom,locusP->GetName().c_str()),gcstr::abandonExport,gcstr::continueExport);
                    if(!continueOn)
                    {
                        throw gc_abandon_export();
                    }
                }
            }
        }

        spacingElement->LinkEndChild(blockElement);
    }
}

void
GCDataStore::AddTraitsElem(TiXmlElement * regionElem, const GCTraitInfoSet & traits) const
{
    if(!traits.empty())
    {
        TiXmlElement * traitsElement = new TiXmlElement( xmlstr::XML_TAG_TRAITS.c_str());
        regionElem->LinkEndChild(traitsElement);

        for(GCTraitInfoSet::const_iterator i=traits.begin(); i!=traits.end(); i++)
        {
            const gcTraitInfo & traitInfo = m_structures.GetTrait(*i);
            TiXmlText * name = new TiXmlText(traitInfo.GetName());

            TiXmlElement * nameElement = new TiXmlElement( xmlstr::XML_TAG_NAME.c_str());
            nameElement->LinkEndChild(name);

            TiXmlElement * traitElement = new TiXmlElement( xmlstr::XML_TAG_TRAIT.c_str());
            traitElement->LinkEndChild(nameElement);

            traitsElement->LinkEndChild(traitElement);
        }
    }
}

TiXmlElement *
GCDataStore::MakePopulationElemForUnlinkedLocus(const gcNameResolvedInfo & info, const gcPopulation & popRef, const gcRegion & regionRef, const gcLocus & locusRef, size_t siteIndex) const
{
    TiXmlElement * popElement = new TiXmlElement( xmlstr::XML_TAG_POPULATION.c_str());
    popElement->SetAttribute(   xmlstr::XML_ATTRTYPE_NAME.c_str(),
                                popRef.GetName().c_str());

    std::vector<const GCIndividual*> individuals = info.GetIndividuals();
    for(size_t individualIndex=0; individualIndex < individuals.size(); individualIndex++)
    {
        assert(individuals[individualIndex] != NULL);
        const GCIndividual & individual = *(individuals[individualIndex]);
        AddIndividualElem(popElement,individual,regionRef,locusRef,siteIndex);
    }

    return popElement;
}

void
GCDataStore::AddPopulationElemForRegion(TiXmlElement * regionElem, const gcNameResolvedInfo & info, const gcRegion & regionRef, const gcPopulation & popRef) const
{
    TiXmlElement * popElement = new TiXmlElement( xmlstr::XML_TAG_POPULATION.c_str());
    regionElem->LinkEndChild(popElement);
    popElement->SetAttribute(   xmlstr::XML_ATTRTYPE_NAME.c_str(),
                                popRef.GetName().c_str());

    std::vector<const GCIndividual*> individuals = info.GetIndividuals();
    for(size_t index=0; index < individuals.size(); index++)
    {
        assert(individuals[index] != NULL);
        const GCIndividual & individual = *(individuals[index]);
        AddIndividualElem(popElement,individual,info.GetRegionRef());
    }

    if (m_structures.GetPanelsState())
    {
        if (m_structures.HasPanel(regionRef.GetId(), popRef.GetId()))
        {
            const gcPanel &panelRef =  m_structures.GetPanel(regionRef.GetId(), popRef.GetId());
            if (panelRef.GetBlessed() && (panelRef.GetNumPanels() > 0))
            {
                // only add > 0  panel counts
                AddPanelElem(popElement, panelRef);
            }
        }
    }
}

void
GCDataStore::AddRegionElem(TiXmlElement * data, const gcExportable & expo, const gcRegion & regionRef) const
{
    // create the named region element
    TiXmlElement * regionElement = new TiXmlElement( xmlstr::XML_TAG_REGION.c_str());
    data->LinkEndChild(regionElement);
    regionElement->SetAttribute(    xmlstr::XML_ATTRTYPE_NAME.c_str(),
                                    regionRef.GetName().c_str());

    AddEffectivePopSizeElem(regionElement,regionRef);
    AddSpacingElem(regionElement,regionRef);
    AddTraitsElem(regionElement,regionRef.GetTraitInfoSet());

#if 0
    TiXmlElement * effPopSizeElem = MakeEffectivePopSizeElem(regionRef);
    if(effPopSizeElem != NULL)
    {
        regionElement->LinkEndChild(effPopSizeElem);
    }

    TiXmlElement * spacingElement = MakeSpacingElem(regionRef);
    regionElement->LinkEndChild(spacingElement);

    const GCTraitInfoSet & traits = regionRef.GetTraitInfoSet();
    if(!traits.empty())
    {
        TiXmlElement * traitsElement = MakeTraitsElem(traits);
        regionElement->LinkEndChild(traitsElement);
    }
#endif

    // write out info for each population
    constObjVector pops = m_structures.GetConstDisplayablePops();
    for(constObjVector::const_iterator iter = pops.begin(); iter != pops.end(); iter++)
    {
        const gcPopulation * popP = dynamic_cast<const gcPopulation*>(*iter);
        assert(popP != NULL);
        const gcNameResolvedInfo & info = expo.GetInfo(*popP,regionRef);
        AddPopulationElemForRegion(regionElement,info,regionRef,*popP);
    }
}

void
GCDataStore::AddDivergenceElem(TiXmlElement * forces) const
{
    wxLogVerbose("In AddDivergenceElem");  // JMDBG
    // create divergence element
    TiXmlElement * divergence = new TiXmlElement( xmlstr::XML_TAG_DIVERGENCE.c_str());
    forces->LinkEndChild(divergence);

    // boilerplate
    TiXmlElement * prior = new TiXmlElement( xmlstr::XML_TAG_PRIOR.c_str());
    prior->SetAttribute(xmlstr::XML_ATTRTYPE_TYPE.c_str(), "linear");
    divergence->LinkEndChild(prior);

    TiXmlElement * paramindex = new TiXmlElement( xmlstr::XML_TAG_PARAMINDEX.c_str());
    wxString dataString = " default ";
    TiXmlText * data = new TiXmlText(dataString);
    paramindex->LinkEndChild(data);
    prior->LinkEndChild(paramindex);

    TiXmlElement * lower = new TiXmlElement( xmlstr::XML_TAG_PRIORLOWERBOUND.c_str());
    dataString = " 0.0 ";
    data = new TiXmlText(dataString);
    lower->LinkEndChild(data);
    prior->LinkEndChild(lower);

    TiXmlElement * upper = new TiXmlElement( xmlstr::XML_TAG_PRIORUPPERBOUND.c_str());
    dataString = " 0.01 ";
    data = new TiXmlText(dataString);
    upper->LinkEndChild(data);
    prior->LinkEndChild(upper);
    wxLogVerbose("    boilerplate out");  // JMDBG

    // variable values
    gcDisplayOrder parids = m_structures.GetParentIds();

    TiXmlElement * method = new TiXmlElement( xmlstr::XML_TAG_METHOD.c_str());
    dataString = " ";
    for(gcDisplayOrder::const_iterator iter = parids.begin(); iter != parids.end(); iter++)
    {
        dataString += " USER ";
    }
    data = new TiXmlText(dataString);
    method->LinkEndChild(data);
    divergence->LinkEndChild(method);

    TiXmlElement * startval = new TiXmlElement( xmlstr::XML_TAG_START_VALUES.c_str());
    float sval = 0;
    dataString = " ";
    for(gcDisplayOrder::const_iterator iter = parids.begin(); iter != parids.end(); iter++)
    {
        sval += 0.002;
        dataString += wxString::Format(_T(" %f "),sval);
    }
    data = new TiXmlText(dataString);
    startval->LinkEndChild(data);
    divergence->LinkEndChild(startval);
    wxLogVerbose("    start values out");  // JMDBG

    TiXmlElement * poptree = new TiXmlElement( xmlstr::XML_TAG_POPTREE.c_str());
    divergence->LinkEndChild(poptree);

    for(gcDisplayOrder::const_iterator iter = parids.begin(); iter != parids.end(); iter++)
    {
        TiXmlElement * epochb = new TiXmlElement( xmlstr::XML_TAG_EPOCH_BOUNDARY.c_str());

        TiXmlElement * newpop = new TiXmlElement( xmlstr::XML_TAG_NEWPOP.c_str());
        const gcParent & parent = m_structures.GetParent(*iter);
        wxLogVerbose("    parentid: %i child1id: %i child2id: %i",(int)*iter, (int)parent.GetChild1Id(), (int)parent.GetChild2Id());  // JMDBG

        dataString = " ";
        if (m_structures.IsPop(parent.GetChild1Id()))
        {
            dataString += m_structures.GetPop(parent.GetChild1Id()).GetName().c_str();
        }
        else
        {
            dataString += m_structures.GetParent(parent.GetChild1Id()).GetName().c_str();
        }

        dataString += " ";
        if (m_structures.IsPop(parent.GetChild2Id()))
        {
            dataString += m_structures.GetPop(parent.GetChild2Id()).GetName().c_str();
        }
        else
        {
            dataString += m_structures.GetParent(parent.GetChild2Id()).GetName().c_str();
        }
        dataString += " ";

        data = new TiXmlText(dataString);
        newpop->LinkEndChild(data);
        epochb->LinkEndChild(newpop);

        TiXmlElement * ancestor = new TiXmlElement( xmlstr::XML_TAG_ANCESTOR.c_str());
        dataString = " ";
        dataString += parent.GetName().c_str();
        dataString += " ";
        data = new TiXmlText(dataString);
        ancestor->LinkEndChild(data);
        epochb->LinkEndChild(ancestor);

        poptree->LinkEndChild(epochb);
    }
    wxLogVerbose("    done");  // JMDBG
}

void
GCDataStore::AddMigrationElem(TiXmlElement * forces)
{
    wxLogVerbose("In AddMigrationElem");  // JMDBG
    //wxLogMessage("In AddMigrationElem");  // JMDBG
    //cout << "In AddMigrationElem" << endl;


    // create migration element
    TiXmlElement * mig;
    if (m_structures.GetDivMigMatrixDefined())
    {
        wxLogVerbose("Divergence Migration Matrix being output");  // JMDBG
        //cout << "Divergence Migration Matrix being output" << endl;
        mig = new TiXmlElement( xmlstr::XML_TAG_DIVMIG.c_str());
    }
    else if (m_structures.GetMigMatrixDefined())
    {
        wxLogVerbose("Migration Matrix being output");  // JMDBG
        //cout << "Migration Matrix being output" << endl;
        mig = new TiXmlElement( xmlstr::XML_TAG_MIGRATION.c_str());
    }
    else
    {
        // neither a migration or divergence migration matrix exist (batch converter case)
        // so create the default migration matrix
        wxLogVerbose("No Migration Matrix exists so creating it");  // JMDBG
        //cout << "No Migration Matrix exists so creating it" << endl;

        if ( m_structures.GetDivergenceState())
        {
            mig = new TiXmlElement( xmlstr::XML_TAG_DIVMIG.c_str());
            //cout << "Divergence Migration Matrix being output" << endl;
        }
        else
        {
            mig = new TiXmlElement( xmlstr::XML_TAG_MIGRATION.c_str());
            //cout << "Migration Matrix being output" << endl;
        }
        m_structures.MakeMigrationMatrix();
#if 0
        gcDisplayOrder popids = m_structures.GetDisplayablePopIds();

        for(gcDisplayOrder::const_iterator iter = popids.begin(); iter != popids.end(); iter++)
        {
            for(gcDisplayOrder::const_iterator jter = popids.begin(); jter != popids.end(); jter++)
            {
                if (*jter!=*iter)
                {
                    gcMigration Mig1 = m_structures.MakeMigration(true, *iter, *jter );
                    gcMigration Mig2 = m_structures.MakeMigration(true, *jter, *iter );
                }
            }
        }
#endif
    }
    forces->LinkEndChild(mig);

    wxString startString = " ";
    wxString methodString = " ";
    wxString profilesString = " ";
    wxString constraintsString = " ";

    constObjVector popsToDisplay =  m_structures.GetConstDisplayablePops();
    if (popsToDisplay.size() > 0)
    {

        // build full migration matrix data in a row by row manner
        const gcDisplayOrder popids = m_structures.GetDisplayablePopIds();
        const gcDisplayOrder parids = m_structures.GetParentIds();

        // populations first
        for(gcDisplayOrder::const_iterator iter = popids.begin(); iter != popids.end(); iter++)
        {
            for(gcDisplayOrder::const_iterator jter = popids.begin(); jter != popids.end(); jter++)
            {
                if(m_structures.HasMigration(*iter, *jter))
                {
                    // population / population pairs
                    const gcMigration mig = m_structures.GetMigration(*iter, *jter);
                    startString += mig.GetStartValueString();
                    methodString += mig.GetMethodString();
                    profilesString += mig.GetProfileAsString();
                    constraintsString += mig.GetConstraintString();
                }
                else
                {
                    // doesn't exist so not allowed
                    startString += "0";
                    methodString += gcstr_mig::migmethodUser;
                    profilesString += gcstr_mig::migprofileNone ;
                    constraintsString += gcstr_mig::migconstraintInvalid;
                }
                startString += " ";
                methodString += " ";
                profilesString += " ";
                constraintsString += " ";
            }

            if (parids.size() > 0)
            {
                // population / parent pairs
                for(gcDisplayOrder::const_iterator kter = parids.begin(); kter != parids.end(); kter++)
                {
                    if(m_structures.HasMigration(*iter, *kter))
                    {
                        // migration exists, get values
                        const gcMigration mig = m_structures.GetMigration(*iter, *kter);
                        startString += mig.GetStartValueString();
                        methodString += mig.GetMethodString();
                        profilesString += mig.GetProfileAsString();
                        constraintsString += mig.GetConstraintString();
                    }
                    else
                    {
                        // doesn't exist so not allowed
                        startString += "0";
                        methodString += gcstr_mig::migmethodUser;
                        profilesString += gcstr_mig::migprofileNone ;
                        constraintsString += gcstr_mig::migconstraintInvalid;
                    }
                    startString += " ";
                    methodString += " ";
                    profilesString += " ";
                    constraintsString += " ";
                }
            }

            // spacers
        }

        // now parents
        if (parids.size() > 0)
        {
            for(gcDisplayOrder::const_iterator iter = parids.begin(); iter != parids.end(); iter++)
            {
                for(gcDisplayOrder::const_iterator jter = popids.begin(); jter != popids.end(); jter++)
                {
                    if(m_structures.HasMigration(*iter, *jter))
                    {
                        // parent / population pairs
                        const gcMigration mig = m_structures.GetMigration(*iter, *jter);
                        startString += mig.GetStartValueString();
                        methodString += mig.GetMethodString();
                        profilesString += mig.GetProfileAsString();
                        constraintsString += mig.GetConstraintString();
                    }
                    else
                    {
                        // doesn't exist so not allowed
                        startString += "0";
                        methodString += gcstr_mig::migmethodUser;
                        profilesString += gcstr_mig::migprofileNone ;
                        constraintsString += gcstr_mig::migconstraintInvalid;
                    }
                    startString += " ";
                    methodString += " ";
                    profilesString += " ";
                    constraintsString += " ";
                }
                // parent / parent pairs
                for(gcDisplayOrder::const_iterator kter = parids.begin(); kter != parids.end(); kter++)
                {
                    if(m_structures.HasMigration(*iter, *kter))
                    {
                        // migration exists, get values
                        const gcMigration mig = m_structures.GetMigration(*iter, *kter);
                        startString += mig.GetStartValueString();
                        methodString += mig.GetMethodString();
                        profilesString += mig.GetProfileAsString();
                        constraintsString += mig.GetConstraintString();
                    }
                    else
                    {
                        // doesn't exist so not allowed
                        startString += "0";
                        methodString += gcstr_mig::migmethodUser;
                        profilesString += gcstr_mig::migprofileNone ;
                        constraintsString += gcstr_mig::migconstraintInvalid;
                    }
                    startString += " ";
                    methodString += " ";
                    profilesString += " ";
                    constraintsString += " ";
                }
            }
        }
    }

    TiXmlElement * stvals = new TiXmlElement( xmlstr::XML_TAG_START_VALUES.c_str());
    TiXmlText * data = new TiXmlText(startString);
    stvals->LinkEndChild(data);
    mig->LinkEndChild(stvals);

    TiXmlElement * method = new TiXmlElement( xmlstr::XML_TAG_METHOD.c_str());
    data = new TiXmlText(methodString);
    method->LinkEndChild(data);
    mig->LinkEndChild(method);

    TiXmlElement * maxevts = new TiXmlElement( xmlstr::XML_TAG_MAX_EVENTS.c_str());
    wxString dataString = " 10000 ";
    data = new TiXmlText(dataString);
    maxevts->LinkEndChild(data);
    mig->LinkEndChild(maxevts);

    TiXmlElement * profiles = new TiXmlElement( xmlstr::XML_TAG_PROFILES.c_str());
    data = new TiXmlText(profilesString);
    profiles->LinkEndChild(data);
    mig->LinkEndChild(profiles);

    TiXmlElement * constraints = new TiXmlElement( xmlstr::XML_TAG_CONSTRAINTS.c_str());
    data = new TiXmlText(constraintsString);
    constraints->LinkEndChild(data);
    mig->LinkEndChild(constraints);

    TiXmlElement * prior = new TiXmlElement( xmlstr::XML_TAG_PRIOR.c_str());
    prior->SetAttribute(xmlstr::XML_ATTRTYPE_TYPE.c_str(), "linear");
    mig->LinkEndChild(prior);

    TiXmlElement * paramindex = new TiXmlElement( xmlstr::XML_TAG_PARAMINDEX.c_str());
    dataString = " default ";
    data = new TiXmlText(dataString);
    paramindex->LinkEndChild(data);
    prior->LinkEndChild(paramindex);

    TiXmlElement * lower = new TiXmlElement( xmlstr::XML_TAG_PRIORLOWERBOUND.c_str());
    dataString = " 0.0 ";
    data = new TiXmlText(dataString);
    lower->LinkEndChild(data);
    prior->LinkEndChild(lower);

    TiXmlElement * upper = new TiXmlElement( xmlstr::XML_TAG_PRIORUPPERBOUND.c_str());
    dataString = " 100.0 ";
    data = new TiXmlText(dataString);
    upper->LinkEndChild(data);
    prior->LinkEndChild(upper);
    wxLogVerbose("    xml out");  // JMDBG
}

wxString
GCDataStore::MakeUnlinkedName(wxString regionName, size_t index) const
{
    return wxString::Format(gcstr::regionNameUnlinked,regionName.c_str(),(int)index);
}

TiXmlElement *
GCDataStore::MakeUnlinkedSite(const gcExportable & expo, const gcRegion & regionRef, const gcLocus & locus, size_t index) const
{
    TiXmlElement * regionElement = new TiXmlElement( xmlstr::XML_TAG_REGION.c_str());
    regionElement->SetAttribute(    xmlstr::XML_ATTRTYPE_NAME.c_str(),
                                    MakeUnlinkedName(regionRef.GetName(),index).c_str());

    // EWFIX.P3 -- need to fix spacing info ??

    constObjVector pops = m_structures.GetConstDisplayablePops();
    for(constObjVector::const_iterator iter=pops.begin(); iter != pops.end(); iter++)
    {
        const gcPopulation * popP = dynamic_cast<const gcPopulation*>(*iter);
        assert(popP != NULL);

        const gcNameResolvedInfo & info = expo.GetInfo(*popP,regionRef);
        TiXmlElement * popElement =
            MakePopulationElemForUnlinkedLocus(info,*popP,regionRef,locus,index);
        if(popElement != NULL)
        {
            regionElement->LinkEndChild(popElement);
        }
    }
    return regionElement;
}

std::vector<TiXmlElement*>
GCDataStore::MakeUnlinkedRegionElems(const gcExportable & expo, const gcRegion & regionRef) const
{
    std::vector<TiXmlElement*> elements;

    // make sure that we are accessing these by order of creation -- a good choice for
    // batch converter and for loci within a single input file. Less clear how good
    // this is if users have been munging about in the gui
    gcIdVec locIds = GetStructures().GetLocusIdsForRegionByCreation(regionRef.GetId());

    for(gcIdVec::const_iterator i = locIds.begin(); i != locIds.end(); i++)
    {
        size_t locusId = *i;
        const gcLocus & locus = m_structures.GetLocus(locusId);
        if(!locus.GetLinked())
        {
            for(size_t index=0; index < locus.GetNumMarkers(); index++)
            {
                TiXmlElement * unlinkedElem = MakeUnlinkedSite(expo,regionRef,locus,index);
                elements.push_back(unlinkedElem);
            }
        }
    }

    return elements;
}


TiXmlDocument *
GCDataStore::ExportFile()
{
    TiXmlDocument * docP = new TiXmlDocument();
    try
    {
        TiXmlDeclaration * decl = new TiXmlDeclaration( "1.0", "", "" );
        docP->LinkEndChild( decl );

        if(!(m_commentString.IsEmpty()))
        {
            TiXmlComment * comment = new TiXmlComment();
            comment->SetValue(m_commentString.c_str());
            docP->LinkEndChild(comment);
        }

        TiXmlElement * lamarc = new TiXmlElement( xmlstr::XML_TAG_LAMARC.c_str() );
        docP->LinkEndChild( lamarc );
        lamarc->SetAttribute( xmlstr::XML_ATTRTYPE_VERSION.c_str(),VERSION);

        TiXmlElement * format = new TiXmlElement( xmlstr::XML_TAG_FORMAT.c_str() );
        lamarc->LinkEndChild( format );
        TiXmlElement * coordMunge = new TiXmlElement( xmlstr::XML_TAG_CONVERT_OUTPUT.c_str() );
        format->LinkEndChild( coordMunge );
        bool anyZeroes = GetStructures().AnyZeroes();   // EWFIX.P4 -- wasteful
        TiXmlText * doConvertText = new TiXmlText(anyZeroes ? "false" : "true");
        coordMunge->LinkEndChild(doConvertText);

        // add migration if more than one population
        // if (m_structures.GetPopCount() > 1)  MDEBUG removed this, added following
        if (m_structures.GetConstDisplayablePops().size() > 1)
        {
            TiXmlElement * forces = new TiXmlElement( xmlstr::XML_TAG_FORCES.c_str() );
            lamarc->LinkEndChild( forces );
            AddMigrationElem(forces);

            if (m_structures.GetDivergenceState())
            {
                AddDivergenceElem(forces);
            }
        }

        TiXmlElement * data = new TiXmlElement( xmlstr::XML_TAG_DATA.c_str() );
        lamarc->LinkEndChild( data );

        const gcExportable exportable = BuildExportableData();

        // do all regions that contain linked data
        constObjVector regions = m_structures.GetConstDisplayableRegions();
        for(constObjVector::const_iterator gIter = regions.begin();
            gIter != regions.end(); gIter++)
        {
            const gcRegion * regionP = dynamic_cast<const gcRegion*>(*gIter);
            assert(regionP != NULL);
            if(m_structures.RegionHasAnyLinkedLoci(regionP->GetId()))
            {
                AddRegionElem(data,exportable,*regionP);
            }
        }

        // do all regions that contain unlinked data
        for(constObjVector::const_iterator gIter = regions.begin();
            gIter != regions.end(); gIter++)
        {
            const gcRegion * regionP = dynamic_cast<const gcRegion*>(*gIter);
            assert(regionP != NULL);
            if(m_structures.RegionHasAnyUnLinkedLoci(regionP->GetId()))
            {
                std::vector<TiXmlElement *> regionElems = MakeUnlinkedRegionElems(exportable,*regionP);
                for(std::vector<TiXmlElement*>::iterator i=regionElems.begin(); i != regionElems.end(); i++)
                {
                    data->LinkEndChild( *i );
                }
            }
        }
    }
    catch(const gc_ex & e)
    {
        delete docP;
        throw;
    }

    return docP;
}

void
GCDataStore::WriteExportedData(TiXmlDocument * docP)
{
    docP->SaveFile( m_outfileName.c_str() );
}

void
GCDataStore::ThrowLocusWithoutDataType(wxString locusName, wxString regionName) const
{
    wxString msg = wxString::Format(gcerr::locusWithoutDataType,
                                    locusName.c_str(),
                                    regionName.c_str());
    gui_error g(msg);
    throw g;
}

GCIndividual &
GCDataStore::makeOrFetchInd(    wxString indName,
                                std::map<wxString,GCIndividual*> & nameDict,
                                gcNameResolvedInfo & info) const
{
    if(nameDict.find(indName) == nameDict.end())
    {
        GCIndividual * ind = new GCIndividual(indName,info.GetRegionRef());
        info.AddIndividual(ind);
        nameDict[indName] = ind;
    }
    std::map<wxString,GCIndividual*>::iterator iter = nameDict.find(indName);
    assert(iter != nameDict.end());

    GCIndividual & ind = *((*iter).second);
    return ind;
}

bool
GCDataStore::AddLocusData(GCIndividual & ind,
                          const gcLocus & locus,
                          GCParseSample & sample) const
{
    size_t samples = sample.GetSequencesPerLabel();
    bool foundAnything = false;
    wxString sampleName = sample.GetLabel();
    for(size_t i=0; i < samples; i++)
    {
        if(samples > 1 || (sampleName == ind.GetName()))
        {
            // EWFIX.P4.BUG.564  -- need to preserve information about how we made
            // this name so we can do better error reporting
            sampleName = wxString::Format("%s_%d",sample.GetLabel().c_str(),(int)i);
        }
        ind.AddSample(sampleName,locus,&(sample.GetData(i)));
        foundAnything = true;
    }
    return foundAnything;
}

bool
GCDataStore::AddLocusData(GCIndividual & ind,
                          const gcLocus & locus,
                          GCParseSample & sample,
                          const wxArrayString & sampleNames) const
{
    size_t numNames = sampleNames.Count();
    size_t numSamples = sample.GetSequencesPerLabel();
    bool foundAnything = false;
    if((numNames != 0) && (numNames != numSamples))
    {
        // EWFIX.P4.BUG.564 -- use more data to make a better message
        throw gc_ind_wrong_sample_count(ind);
    }
    for(size_t i=0; i < numSamples; i++)
    {
        wxString sampleName = "";
        if(numNames != 0) sampleName = sampleNames[i];
        ind.AddSample(sampleName,locus,&(sample.GetData(i)));
        foundAnything = true;
    }
    return foundAnything;
}

bool
GCDataStore::FillExportInfo(gcNameResolvedInfo & info, gcPhaseInfo & phaseInfo) const
{
    bool regionFoundAnything = false;
    std::map<wxString,GCIndividual*> nameDict;

    size_t popId    = info.GetPopRef().GetId();
    size_t regionId = info.GetRegionRef().GetId();

    // get list of loci for region
    constObjVector loci = m_structures.GetConstDisplayableLociInMapOrderFor(regionId);

    for(constObjVector::const_iterator lIter = loci.begin();
        lIter != loci.end(); lIter++)
    {
        const gcLocus * locusP = dynamic_cast<const gcLocus*>(*lIter);
        assert(locusP != NULL);

        wxString locusName = locusP->GetName();

        gcSpecificDataType dtype = locusP->GetDataType();
        if(dtype == sdatatype_NONE_SET)
        {
            ThrowLocusWithoutDataType(locusP->GetName(),info.GetRegionRef().GetName());
        }

        size_t locusId = locusP->GetId();
        constBlockVector blocks = GetBlocks(popId,locusId);
        for(constBlockVector::const_iterator bIter = blocks.begin();
            bIter != blocks.end(); bIter++)
        {
            assert(*bIter != NULL);
            bool locusFoundAnything = false;

            const GCParseBlock & block = **bIter;

            const GCParseSamples & samples = block.GetSamples();

            // EWFIX.P3 --  what I really need to do here is
            // to have an individual name and a sample name for each
            // member data item within each sample
            for(size_t i=0; i < samples.size(); i++)
            {
                GCParseSample & sample = *(samples[i]);

                const wxString & thisName = sample.GetLabel();
                bool isInd = phaseInfo.HasIndividualRecord(thisName);
                bool isSam = phaseInfo.HasSampleRecord(thisName);

                if( (isInd) && (isSam) )
                {
                    const gcPhaseRecord & rec1 = phaseInfo.GetIndividualRecord(thisName);
                    const gcPhaseRecord & rec2 = phaseInfo.GetSampleRecord(thisName);
                    throw gc_both_individual_and_sample(thisName,rec1,rec2);
                }

                if (isInd)
                {
                    GCIndividual & ind = makeOrFetchInd(thisName,nameDict,info);
                    const gcPhaseRecord & rec = phaseInfo.GetIndividualRecord(thisName);

                    // get phenotype names
                    // find those that apply to this region info.GetRegionRef();
                    // get geno resolutions for them
                    const gcIdSet & phenoIds = rec.GetPhenotypeIds();
                    const GCTraitInfoSet & traits = info.GetRegionRef().GetTraitInfoSet();
                    for(gcIdSet::const_iterator i=phenoIds.begin(); i != phenoIds.end(); i++)
                    {
                        size_t phenotypeId = *i;
                        for(GCTraitInfoSet::const_iterator j=traits.begin(); j != traits.end(); j++)
                        {
                            const gcTraitInfo & traitRef = GetStructures().GetTrait(*j);
                            const gcPhenotype & phenoRef = GetStructures().GetPhenotype(phenotypeId);
                            if(traitRef.HasPhenotype(phenoRef))
                            {
                                ind.AddPhenotype(phenoRef);
                            }
                        }
                    }

                    if(rec.HasUnphased(locusName))
                    {
                        ind.AddPhase(*locusP,rec.GetUnphased(locusName));
                    }
                    if(rec.HasSamples())
                    {
                        const wxArrayString & sampleNames = rec.GetSamples();
                        locusFoundAnything = AddLocusData(ind,*locusP,sample,sampleNames) || locusFoundAnything;
                    }
                    else
                    {
                        locusFoundAnything = AddLocusData(ind,*locusP,sample) || locusFoundAnything;
                    }
                }

                if (isSam)
                {
                    const gcPhaseRecord & rec = phaseInfo.GetSampleRecord(thisName);
                    wxString indName;
                    if(rec.HasIndividual())
                    {
                        indName = rec.GetIndividual();
                    }
                    else
                    {
                        indName = gcdefault::createdIndividualPrefix;
                        assert(rec.HasSamples());
                        const wxArrayString & samples = rec.GetSamples();
                        for(size_t i = 0; i < samples.Count(); i++)
                        {
                            indName = wxString::Format("%s:%s",
                                                       indName.c_str(),
                                                       samples[i].c_str());
                        }
                    }
                    GCIndividual & ind = makeOrFetchInd(indName,nameDict,info);
                    // EWFIX.P3 -- refactor -- appears above
                    // get phenotype names
                    // find those that apply to this region info.GetRegionRef();
                    // get geno resolutions for them
                    const gcIdSet & phenoIds = rec.GetPhenotypeIds();
                    const GCTraitInfoSet & traits = info.GetRegionRef().GetTraitInfoSet();
                    for(gcIdSet::const_iterator i=phenoIds.begin(); i != phenoIds.end(); i++)
                    {
                        size_t phenotypeId = *i;
                        for(GCTraitInfoSet::const_iterator j=traits.begin(); j != traits.end(); j++)
                        {
                            const gcTraitInfo & traitRef = GetStructures().GetTrait(*j);
                            const gcPhenotype & phenoRef = GetStructures().GetPhenotype(phenotypeId);
                            if(traitRef.HasPhenotype(phenoRef))
                            {
                                ind.AddPhenotype(phenoRef);
                            }
                        }
                    }
                    locusFoundAnything = AddLocusData(ind,*locusP,sample) || locusFoundAnything;
                    if(rec.HasUnphased(locusName))
                    {
                        ind.AddPhase(*locusP,rec.GetUnphased(locusName));
                    }
                }

                if(!isInd && !isSam)
                    // doesn't have any phase information
                {
                    GCIndividual & ind = makeOrFetchInd(thisName,nameDict,info);
                    locusFoundAnything = AddLocusData(ind,*locusP,sample) || locusFoundAnything;
                }
            }

#if 0
            // At the moment, this cannot happen without
            // causing another error (either missing_pop_region or
            // missing_sample).
            if(!locusFoundAnything)
                // EWFIX.P3.BUG.511 -- eventually this restriction should
                // be relaxed, but at the moment we require all pop,locus
                // pairs have data
            {
                throw gc_data_missing_pop_locus(info.GetPopRef().GetName(),locusP->GetName());
            }
#endif
            regionFoundAnything = regionFoundAnything || locusFoundAnything;
        }
    }
    if(!regionFoundAnything)
        // EWFIX.P3.BUG.511 -- eventually this restriction should
        // be relaxed, but at the moment we require all pop,locus
        // pairs have data
    {
        throw gc_data_missing_pop_region(info.GetPopRef().GetName(),info.GetRegionRef().GetName());
    }
    return regionFoundAnything;
}

gcExportable
GCDataStore::BuildExportableData() const
{
    bool foundAnything = false;
    gcExportable exportable;

    constObjVector regions  = m_structures.GetConstDisplayableRegions();
    constObjVector pops     = m_structures.GetConstDisplayablePops();

    gcPhaseInfo * phaseInfo = BuildPhaseInfo();
    assert(phaseInfo != NULL);

    try
    {
        DiagnosePhaseInfoProblems(*phaseInfo);

        for(constObjVector::const_iterator gIter = regions.begin();
            gIter != regions.end(); gIter++)
        {
            const gcRegion * regionP = dynamic_cast<const gcRegion*>(*gIter);
            assert(regionP != NULL);
            bool regionFoundAnything = false;
            for(constObjVector::const_iterator iter = pops.begin(); iter != pops.end(); iter++)
            {
                const gcPopulation * popP = dynamic_cast<const gcPopulation*>(*iter);
                assert(popP != NULL);

                gcNameResolvedInfo * info = new gcNameResolvedInfo(*popP,*regionP);
                try
                {
                    bool localFoundAnything = FillExportInfo(*info,*phaseInfo);
                    foundAnything |= localFoundAnything;
                    regionFoundAnything |= localFoundAnything;
                    gcPopRegionPair infoPair(popP,regionP);
                    assert(exportable.find(infoPair) == exportable.end());
                    exportable[infoPair] = info;
                }
                catch(const gc_ex & e)
                {
                    delete info;
                    throw;
                }
            }
            if(!regionFoundAnything)
            {
                throw gui_error(wxString::Format(gcerr::regionNoData,regionP->GetName().c_str()));
            }
        }
    }
    catch(const gc_ex & e)
    {
        delete phaseInfo;
        throw;
    }

    delete phaseInfo;

    if(!foundAnything)
    {
        gui_error g(gcerr::noDataFound);
        throw g;
    }

    // EWFIX.P3 -- we should gather info about ghost populations,
    // and other unexpected data gaps

    return exportable;
}

//____________________________________________________________________________________
