// $Id: gc_datastore_readcmd.cpp,v 1.68 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 "errhandling.h"        // for unrecognized_tag_error // EWFIX.P4 -- factor out xml errors
#include "gc_cmdfile_err.h"
#include "gc_data.h"
#include "gc_datastore.h"
#include "gc_errhandling.h"
#include "gc_genotype_resolution.h"
#include "gc_region.h"
#include "gc_loci_match.h"
#include "gc_locus_err.h"
#include "gc_migrate.h"
#include "gc_phase.h"
#include "gc_phase_err.h"
#include "gc_phase_info.h"
#include "gc_phylip.h"
#include "gc_pop_match.h"
#include "gc_population.h"
#include "gc_locus.h"
#include "gc_strings.h"
#include "gc_strings_cmdfile.h"
#include "gc_structures_err.h"
#include "gc_trait.h"
#include "gc_trait_err.h"
#include "gc_types.h"
#include "cmdfileschema.h"
#include "front_end_warnings.h"
#include "tinyxml.h"
#include "tixml_util.h"
#include "xml.h"
#include "cnv_strings.h"
#include "wx/log.h"
#include "wx/string.h"
#include "wx/tokenzr.h"

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

void
GCDataStore::cmdParseBlock(TiXmlElement * blockElem, gcRegion & regionRef, size_t numBlocksInRegion)
{
    try
    {
        ////////////////////////////////////////////////////////////////////
        // name -- required by all
        wxString name;
        TiXmlElement * nameElem = tiwx_requiredChild(blockElem,cnvstr::TAG_NAME);
        name = tiwx_nodeText(nameElem);
        assert(! name.IsEmpty());
        size_t lineNumber = blockElem->Row();
        gcCreationInfo creationInfo = gcCreationInfo::MakeCmdFileCreationInfo(lineNumber,m_commandFileCurrentlyBeingParsed);
        gcLocus & locusRef = GetStructures().FetchOrMakeLocus(regionRef,name,creationInfo);

        ////////////////////////////////////////////////////////////////////
        // data type -- required by all
        wxString dataTypeString = tiwx_attributeValue(blockElem,cnvstr::ATTR_DATATYPE);
        gcSpecificDataType dataType = ProduceSpecificDataTypeOrBarf(dataTypeString,false);
        assert (dataType != sdatatype_NONE_SET);
        locusRef.SetDataType(dataType);

        ////////////////////////////////////////////////////////////////////
        // markers -- required by all
        TiXmlElement * markersElem = tiwx_requiredChild(blockElem,cnvstr::TAG_MARKERS);

        try
        {
            size_t numMarkers = tiwx_size_t_from_text(markersElem);
            locusRef.SetNumMarkers(numMarkers);
        }
        catch (const incorrect_xml& x)
        {
            fileRejectingError(x.what(),markersElem->Row());
        }
        catch (const gc_locus_err& z)
        {
            fileRejectingError(z.what(),markersElem->Row());
        }

        ////////////////////////////////////////////////////////////////////
        // length -- required for SNP data
        TiXmlElement * lengthElem = tiwx_optionalChild(blockElem,cnvstr::TAG_SCANNED_LENGTH);
        assert(locusRef.HasNumMarkers());
        if(lengthElem == NULL)
        {
            if(locusRef.GetNumMarkers() > 1)
            {
                if(locusRef.GetDataType() == sdatatype_SNP)
                {
                    batchFileRejectGuiLog(cnvstr::ERR_DATA_LENGTH_REQUIRED,blockElem->Row());
                }
                if(locusRef.GetDataType() == sdatatype_KALLELE ||
                   locusRef.GetDataType() == sdatatype_MICROSAT )
                {
                    warnLog(wxString::Format(cnvstr::WARN_NO_LENGTH,name.c_str()),blockElem->Row());
                }
            }
        }
        else
        {
            try
            {
                size_t length = tiwx_size_t_from_text(lengthElem);
                locusRef.SetTotalLength(length);
            }
            catch (const incorrect_xml& x)
            {
                fileRejectingError(x.what(),lengthElem->Row());
            }
            catch (const gc_locus_err& z)
            {
                fileRejectingError(z.what(),lengthElem->Row());
            }
        }

        ////////////////////////////////////////////////////////////////////
        // offset -- required  when ?? // EWFIX
        TiXmlElement * offsetElem = tiwx_optionalChild(blockElem,cnvstr::TAG_FIRST_POSITION_SCANNED);
        if(offsetElem == NULL)
        {
            if (locusRef.HasLocations())
            {
                batchFileRejectGuiLog(cnvstr::ERR_OFFSET_REQUIRED,blockElem->Row());
            }
        }
        else
        {
            try
            {
                long offset = tiwx_long_from_text(offsetElem);
                locusRef.SetOffset(offset);
            }
            catch (const incorrect_xml& x)
            {
                fileRejectingError(x.what(),offsetElem->Row());
            }
            catch (const gc_locus_err& z)
            {
                fileRejectingError(z.what(),offsetElem->Row());
            }
        }

        ////////////////////////////////////////////////////////////////////
        // locations -- optional, but required for recombination on non-DNA data
        TiXmlElement *locationsElem = tiwx_optionalChild(blockElem,cnvstr::TAG_SCANNED_DATA_POSITIONS);
        assert(locusRef.HasNumMarkers());
        if(locationsElem == NULL)
        {
            if(locusRef.GetNumMarkers() > 1)
                // dna -- required if length > markers
                // snp -- required for recom
                // msat/kallele -- required for recom
            {
                if ( locusRef.HasTotalLength() && locusRef.GetDataType() != sdatatype_SNP)
                {
                    batchFileRejectGuiLog(cnvstr::ERR_LOCATIONS_REQUIRED_WITH_LENGTH,blockElem->Row());
                }
                if (locusRef.GetDataType() != sdatatype_DNA && (locusRef.HasNumMarkers() && (locusRef.GetNumMarkers() > 1)))
                {
                    warnLog(wxString::Format(cnvstr::WARN_NO_LOCATIONS,name.c_str()),blockElem->Row());
                }
            }
        }
        else
        {
            try
            {
                wxString locations = tiwx_nodeText(locationsElem);
                if(! locusRef.HasTotalLength())
                {
                    batchFileRejectGuiLog(cnvstr::ERR_LENGTH_REQUIRED_WITH_LOCATIONS,blockElem->Row());
                }
                locusRef.SetLocations(locations);

                if(!locusRef.HasOffset())
                {
                    batchFileRejectGuiLog(cnvstr::ERR_LOCATIONS_REQUIRE_OFFSET,locationsElem->Row());
                }
            }
            catch (const incorrect_xml& x)
            {
                fileRejectingError(x.what(),locationsElem->Row());
            }
            catch (const gc_locus_err& z)
            {
                fileRejectingError(z.what(),locationsElem->Row());
            }
        }

        ////////////////////////////////////////////////////////////////////
        // proximity -- linked/unlinked
        // optional for microsat or kallele; must be linked for nucleotides
        wxString proximityString = tiwx_attributeValue(blockElem,cnvstr::ATTR_PROXIMITY);
        if (!proximityString.empty())
        {
            bool linked = ProduceBoolFromProximityOrBarf(proximityString);
            if (linked==false && (locusRef.GetDataType() == sdatatype_DNA || locusRef.GetDataType()==sdatatype_SNP))
            {
                fileRejectingError(wxString::Format(cnvstr::ERR_NO_NUCLEOTIDES_UNLINKED,
                                                    (ToWxString(locusRef.GetDataType())).c_str(),
                                                    proximityString.c_str()),
                                   blockElem->Row());
            }
            if (linked == false && numBlocksInRegion > 1)
            {
                warnLog(wxString::Format(gcstr::moveLocus,locusRef.GetName().c_str(),regionRef.GetName().c_str()));
                gcRegion & newRegion = GetStructures().MakeRegion();
                GetStructures().AssignLocus(locusRef,newRegion);
            }
            locusRef.SetLinkedUserValue(linked);
        }
        else
            // set appropriate defaults
        {
            if(locusRef.GetDataType() == sdatatype_KALLELE || locusRef.GetDataType() == sdatatype_MICROSAT)
                // kallele and msat data depend on number of segments in region
            {
                if(numBlocksInRegion > 1)
                {
                    //EWFIX.P3
                    locusRef.SetLinkedUserValue(true);
                }
                else
                {
                    //EWFIX.P3 locusRef.SetLinked(false);
                }
            }
            else
                // others are linked
            {
                //EWFIX.P3 //locusRef.SetLinked(true);
            }
        }

        ////////////////////////////////////////////////////////////////////
        // mapPosition
        TiXmlElement * mapPositionElem = tiwx_optionalChild(blockElem,cnvstr::TAG_MAP_POSITION);
        if(mapPositionElem == NULL)
        {
            if(numBlocksInRegion > 1 && locusRef.GetLinked())
            {
                batchFileRejectGuiLog(cnvstr::ERR_MAP_POSITION_REQUIRED,blockElem->Row());
            }
        }
        else
        {
            try
            {
                long mapPosition = tiwx_long_from_text(mapPositionElem);
                locusRef.SetMapPosition(mapPosition);
            }
            catch (const incorrect_xml& x)
            {
                fileRejectingError(x.what(),mapPositionElem->Row());
            }
            catch (const gc_locus_err& z)
            {
                fileRejectingError(z.what(),mapPositionElem->Row());
            }
        }

        ////////////////////////////////////////////////////////////////////
        TiXmlElement * phaseElem = tiwx_optionalChild(blockElem,cnvstr::TAG_UNRESOLVED_MARKERS);
        if(phaseElem != NULL)
        {
            try
            {
                wxString markerString = tiwx_nodeText(phaseElem);
                gcUnphasedMarkers markers;
                markers.ReadFromString(markerString);
                locusRef.SetUnphasedMarkers(markers);
            }
            catch (const incorrect_xml& x)
            {
                fileRejectingError(x.what(),phaseElem->Row());
            }
            catch (const gc_locus_err& z)
            {
                fileRejectingError(z.what(),phaseElem->Row());
            }
        }

    }
    catch (const incorrect_xml& x)
    {
        fileRejectingError(x.what(),blockElem->Row());
    }
    catch (const gc_cmdfile_err& y)
    {
        fileRejectingError(y.what(),blockElem->Row());
    }
    catch (const gc_locus_err& z)
    {
        fileRejectingError(z.what(),blockElem->Row());
    }
}

void
GCDataStore::cmdParseInfile(TiXmlElement * fileElem)
{
    try
    {
        // format -- required by all
        wxString formatString = tiwx_attributeValue(fileElem,cnvstr::ATTR_FORMAT);
        GCFileFormat format = ProduceGCFileFormatOrBarf(formatString,false);

        // data type -- required by all
        wxString dataTypeString = tiwx_attributeValue(fileElem,cnvstr::ATTR_DATATYPE);
        gcGeneralDataType dataType;
        gcSpecificDataType sDataType = ProduceSpecificDataTypeOrBarf(dataTypeString,false);
        dataType.insert(sDataType);

        // interleaving -- required by all
        wxString interleavingString = tiwx_attributeValue(fileElem,cnvstr::ATTR_SEQUENCEALIGNMENT);
        GCInterleaving interleaving = interleaving_NONE_SET;
        if(!interleavingString.IsEmpty())
        {
            interleaving = ProduceGCInterleavingOrBarf(interleavingString,false);
        }

        // name
        TiXmlElement * nameElem = tiwx_requiredChild(fileElem,cnvstr::TAG_NAME);
        wxString fileName = tiwx_nodeText(nameElem);

        // info about population matching
        TiXmlElement * popElem = tiwx_requiredChild(fileElem,cnvstr::TAG_POP_MATCHING);
        GCPopMatcher  popMatch = makePopMatcher(popElem);

        // info about loci matching
        TiXmlElement * lociElem = tiwx_requiredChild(fileElem,cnvstr::TAG_SEGMENTS_MATCHING);
        GCLocusMatcher  lociMatch = makeLocusMatcher(lociElem);

        GCFile & file = AddDataFile(fileName,format,dataType,interleaving);

        // this may throw
        const GCParse & parse = file.GetParse(format,dataType,interleaving);
        SetParseChoice(parse,popMatch,lociMatch);

        // EWFIX.P4.BUG.535 -- no individual matching tag
        TiXmlElement * indMatchElem = tiwx_optionalChild(fileElem,cnvstr::TAG_INDIVIDUALS_FROM_SAMPLES);
        if(indMatchElem != NULL)
        {
            wxString matchType = tiwx_attributeValue(indMatchElem,cnvstr::ATTR_TYPE);
            wxString numSamplesText = tiwx_nodeText(indMatchElem);
            try
            {
                if(CaselessStrCmp(cnvstr::ATTR_VAL_BYADJACENCY,matchType))
                {
                    long numSamples;
                    bool haveSamples = numSamplesText.ToLong(&numSamples);
                    if(!haveSamples || numSamples <= 1)
                    {
                        throw gc_bad_ind_match_adjacency_value(numSamplesText);
                    }
                    else
                    {
                        GetStructures().SetHapFileAdjacent(file.GetId(),numSamples);
                    }
                }
                else
                {
                    throw gc_bad_ind_match(matchType);
                }
            }
            catch (gc_ex& e)
            {
                if(!e.hasRow())
                {
                    e.setRow(indMatchElem->Row());
                }
                throw;
            }
        }
    }
    catch(gc_cmdfile_err& g)
    {
        if(!g.hasRow())
        {
            g.setRow((size_t)fileElem->Row());
        }
        throw;
    }
}

void
GCDataStore::cmdParseInfiles(TiXmlElement * filesElem)
{
    if(filesElem != NULL)
    {
        std::vector<TiXmlElement*> files = tiwx_optionalChildren(filesElem,cnvstr::TAG_INFILE);
        for(std::vector<TiXmlElement*>::iterator i = files.begin(); i != files.end(); i++)
        {
            cmdParseInfile(*i);
        }
    }
}

void
GCDataStore::cmdParsePopulation(std::map<wxString,int> & popNameToRow, TiXmlElement * populationElem)
{
    wxString popName = tiwx_nodeText(populationElem);

    std::map<wxString,int>::iterator iter = popNameToRow.find(popName);
    if(iter == popNameToRow.end())
    {
        popNameToRow[popName] = populationElem->Row();
    }
    else
    {
        throw gc_name_repeat_pop(popName,(*iter).second,populationElem->Row());
    }

    GetStructures().FetchOrMakePop(popName);
}

void
GCDataStore::cmdParsePopulations(TiXmlElement * populationsElem)
{
    if(populationsElem != NULL)
    {
        std::vector<TiXmlElement*> populations = tiwx_requiredChildren(populationsElem,cnvstr::TAG_POPULATION);
        std::map<wxString,int> popNameToRow;
        for(std::vector<TiXmlElement*>::iterator i = populations.begin(); i != populations.end(); i++)
        {
            cmdParsePopulation(popNameToRow,*i);
        }
    }
}

void
GCDataStore::cmdParsePanel(TiXmlElement * panelElem)
{
    if(panelElem != NULL)
    {
        TiXmlElement* panel_region = tiwx_requiredChild(panelElem,cnvstr::TAG_PANEL_REGION);
        wxString regionName = tiwx_nodeText(panel_region);
        size_t regionId = GetStructures().GetRegion(regionName).GetId();

        TiXmlElement* panel_pop = tiwx_requiredChild(panelElem,cnvstr::TAG_PANEL_POP);
        wxString popName = tiwx_nodeText(panel_pop);
        size_t popId = GetStructures().GetPop(popName).GetId();

        wxString panelName;
        TiXmlElement* panel_name = tiwx_optionalChild(panelElem,cnvstr::TAG_PANEL_NAME);
        if (panel_name != NULL)
        {
            panelName = tiwx_nodeText(panel_name);
        }
        else
        {
            panelName = "panel:";
            panelName += regionName;
            panelName += ":";
            panelName += popName;
        }

        TiXmlElement* panel_size = tiwx_requiredChild(panelElem,cnvstr::TAG_PANEL_SIZE);
        long panelSize = tiwx_long_from_text(panel_size);

        gcPanel & panel = GetStructures().MakePanel(panelName, true, regionId, popId);
        panel.SetNumPanels(panelSize);

        GetStructures().SetPanelsState(true);
    }
}

void
GCDataStore::cmdParsePanels(TiXmlElement * panelsElem)
{
    if(panelsElem != NULL)
    {
        std::vector<TiXmlElement*> panels = tiwx_requiredChildren(panelsElem,cnvstr::TAG_PANEL);
        for(std::vector<TiXmlElement*>::iterator i = panels.begin(); i != panels.end(); i++)
        {
            cmdParsePanel(*i);
        }
    }
}

gcPhaseRecord *
GCDataStore::cmdParseIndividual(TiXmlElement * individualElem, wxString fileName)
{
    TiXmlElement * nameElem = tiwx_requiredChild(individualElem,cnvstr::TAG_NAME);
    wxString indName = tiwx_nodeText(nameElem);

    wxArrayString sampleNames;
    std::vector<TiXmlElement*> samples = tiwx_requiredChildren(individualElem,cnvstr::TAG_SAMPLE);
    for(std::vector<TiXmlElement*>::iterator i = samples.begin(); i != samples.end(); i++)
    {
        TiXmlElement * sampleNameElem = tiwx_requiredChild((*i),cnvstr::TAG_NAME);
        wxString sampleName = tiwx_nodeText(sampleNameElem);
        sampleNames.Add(sampleName);
    }

    gcPhaseRecord * rec = gcPhaseRecord::MakeFullPhaseRecord(fileName,individualElem->Row(),indName,sampleNames);

    std::vector<TiXmlElement*> phaseInfo = tiwx_optionalChildren(individualElem,cnvstr::TAG_PHASE);
    for(std::vector<TiXmlElement*>::iterator i = phaseInfo.begin(); i != phaseInfo.end(); i++)
    {
        TiXmlElement * locusNameElem = tiwx_requiredChild((*i),cnvstr::TAG_SEGMENT_NAME);
        wxString locusName = tiwx_nodeText(locusNameElem);

        TiXmlElement * unphasedElem = tiwx_requiredChild((*i),cnvstr::TAG_UNRESOLVED_MARKERS);
        wxString unphasedString = tiwx_nodeText(unphasedElem);

        gcUnphasedMarkers markers;
        markers.ReadFromString(unphasedString);

        rec->AddUnphased(locusName,markers);
    }

    std::vector<TiXmlElement*> phenoInfo = tiwx_optionalChildren(individualElem,cnvstr::TAG_HAS_PHENOTYPE);
    for(std::vector<TiXmlElement*>::iterator i = phenoInfo.begin(); i != phenoInfo.end(); i++)
    {
        TiXmlElement * phenoNameElem = (*i);
        wxString phenoName = tiwx_nodeText(phenoNameElem);
        const gcPhenotype & pheno = GetStructures().GetPhenotype(phenoName);
        rec->AddPhenotypeId(pheno.GetId());
    }

    std::vector<TiXmlElement*> genoInfo = tiwx_optionalChildren(individualElem,cnvstr::TAG_GENO_RESOLUTIONS);
    for(std::vector<TiXmlElement*>::iterator i = genoInfo.begin(); i != genoInfo.end(); i++)
    {
        gcPhenotype & pheno = cmdParseGenoResolution(*i);
        rec->AddPhenotypeId(pheno.GetId());
    }

    return rec;
}

void
GCDataStore::cmdParseIndividuals(TiXmlElement * individualsElem,wxString fileName)
{
    // EWFIX.P3.BUG.524 -- need to find way to catch an error and remove adding of
    // all individuals if we're reading a separate phase file
    if(individualsElem != NULL)
    {
        std::vector<TiXmlElement*> individuals = tiwx_optionalChildren(individualsElem,cnvstr::TAG_INDIVIDUAL);
        for(std::vector<TiXmlElement*>::iterator i = individuals.begin(); i != individuals.end(); i++)
        {
            gcPhaseRecord * recP = cmdParseIndividual(*i,fileName);
            m_phaseInfo.AddRecord(*recP);
            delete recP;
        }
    }
}

gcPhenotype &
GCDataStore::cmdParsePhenotype(TiXmlElement * phenoElem)
{
    try
    {
        TiXmlElement * nameElem = tiwx_requiredChild(phenoElem,cnvstr::TAG_NAME);
        wxString nameString = tiwx_nodeText(nameElem);
        TiXmlElement * genoElem = tiwx_requiredChild(phenoElem,cnvstr::TAG_GENO_RESOLUTIONS);

        gcPhenotype & pheno = cmdParseGenoResolution(genoElem);
        GetStructures().Rename(pheno,nameString);
        return pheno;
    }
    catch (const incorrect_xml& x)
    {
        fileRejectingError(x.what(),phenoElem->Row());
    }
    catch(gc_ex & e)
    {
        if(!e.hasRow())
        {
            e.setRow(phenoElem->Row());
        }
        throw;
    }
    assert(false);
    throw implementation_error("Reached end of GCDataStore::cmdParsePhenotype without returning a value.");
}

void
GCDataStore::cmdParseTraitLocation(TiXmlElement * traitElem, gcRegion & regionRef)
{
    try
    {
        TiXmlElement * nameElem = tiwx_requiredChild(traitElem,cnvstr::TAG_TRAIT_NAME);
        wxString traitName = tiwx_nodeText(nameElem);
        gcTraitInfo & trait = GetStructures().GetTrait(traitName);
        GetStructures().AssignTrait(trait,regionRef);
    }
    catch(gc_ex & e)
    {
        if(!e.hasRow())
        {
            e.setRow(traitElem->Row());
        }
        throw;
    }
}

void
GCDataStore::cmdParseTrait(std::map<wxString,int> & traitNameToRow, TiXmlElement * traitElem)
{
    TiXmlElement * nameElem = tiwx_requiredChild(traitElem,cnvstr::TAG_NAME);
    wxString traitName = tiwx_nodeText(nameElem);

    std::map<wxString,int>::iterator iter = traitNameToRow.find(traitName);
    if(iter == traitNameToRow.end())
    {
        traitNameToRow[traitName] = nameElem->Row();
    }
    else
    {
        throw gc_name_repeat_trait(traitName,(*iter).second,nameElem->Row());
    }

    std::vector<TiXmlElement*> alleles = tiwx_optionalChildren(traitElem,cnvstr::TAG_ALLELE);
    gcTraitInfo & traitInfo = GetStructures().FetchOrMakeTrait(traitName);
    std::map<wxString,int> alleleInfo;

    for(std::vector<TiXmlElement*>::iterator i = alleles.begin(); i != alleles.end(); i++)
    {
        wxString alleleName = tiwx_nodeText(*i);
        std::map<wxString,int>::iterator alleleIter = alleleInfo.find(alleleName);
        if(alleleIter == alleleInfo.end())
        {
            alleleInfo[alleleName]=(*i)->Row();
        }
        else
        {
            throw gc_name_repeat_allele(alleleName,(*alleleIter).second,(*i)->Row());
        }
        gcTraitAllele & allele = GetStructures().FetchOrMakeAllele(traitInfo,alleleName);
        GetStructures().AssignAllele(allele,traitInfo);
    }

}

void
GCDataStore::cmdParseTraits(TiXmlElement * traitsElem)
{
    if(traitsElem != NULL)
    {
        std::vector<TiXmlElement*> traits = tiwx_optionalChildren(traitsElem,cnvstr::TAG_TRAIT_INFO);
        std::map<wxString,int> traitNameToRow;
        for(size_t i = 0; i < traits.size(); i++)
        {
            cmdParseTrait(traitNameToRow,traits[i]);
        }

        std::vector<TiXmlElement*> phenotypes = tiwx_optionalChildren(traitsElem,cnvstr::TAG_PHENOTYPE);
        for(std::vector<TiXmlElement*>::iterator i = phenotypes.begin(); i != phenotypes.end(); i++)
        {
            gcPhenotype & pheno = cmdParsePhenotype(*i);
            pheno.SetHasExplicitName();
        }
    }
}

void
GCDataStore::cmdParseRegion(TiXmlElement * regionElem)
{
    // name
    TiXmlElement * nameElem = tiwx_requiredChild(regionElem,cnvstr::TAG_NAME);
    wxString regionName = tiwx_nodeText(nameElem);
    gcRegion & regionRef = GetStructures().FetchOrMakeRegion(regionName);

    // effective popsize
    TiXmlElement * popSizeElem = tiwx_optionalChild(regionElem,cnvstr::TAG_EFFECTIVE_POPSIZE);
    if (popSizeElem != NULL)
    {
        double effectivePopSize = tiwx_double_from_text(popSizeElem);
        regionRef.SetEffectivePopulationSize(effectivePopSize);
    }

    // do spacing
    TiXmlElement * spacingElement = tiwx_requiredChild(regionElem,cnvstr::TAG_SEGMENTS);
    cmdParseSpacing(spacingElement,regionRef);

    // traits assigned here
    std::vector<TiXmlElement*> traits = tiwx_optionalChildren(regionElem,cnvstr::TAG_TRAIT_LOCATION);
    for(std::vector<TiXmlElement*>::iterator i = traits.begin(); i != traits.end(); i++)
    {
        cmdParseTraitLocation(*i,regionRef);
    }

}

void
GCDataStore::cmdParseRegions(TiXmlElement * regionsElem)
{
    if(regionsElem != NULL)
    {
        std::vector<TiXmlElement*> regions = tiwx_requiredChildren(regionsElem,cnvstr::TAG_REGION);
        for(std::vector<TiXmlElement*>::iterator i = regions.begin(); i != regions.end(); i++)
        {
            cmdParseRegion(*i);
        }
    }
}

void
GCDataStore::cmdParseSpacing(TiXmlElement * spacingElem, gcRegion & regionRef)
{
    std::vector<TiXmlElement*> blocks = tiwx_optionalChildren(spacingElem,cnvstr::TAG_SEGMENT);
    for(std::vector<TiXmlElement*>::iterator i = blocks.begin(); i != blocks.end(); i++)
    {
        cmdParseBlock(*i,regionRef,blocks.size());
    }
}

void
GCDataStore::cmdParseOutfile(TiXmlElement * outfileElem)
{
    if(outfileElem != NULL)
    {
        wxString outfileName = tiwx_nodeText(outfileElem);
        SetOutfileName(outfileName);
    }
}

void
GCDataStore::cmdParseComment(TiXmlElement * commentElem)
{
    if(commentElem != NULL)
    {
        wxString commentString = tiwx_nodeText(commentElem);
        SetLamarcCommentString(commentString);
    }
}

GCLocusMatcher
GCDataStore::makeLocusMatcher(TiXmlElement * lociMatchElem)
{
    // read type attribute
    wxString lociTypeString = tiwx_attributeValue(lociMatchElem,cnvstr::ATTR_TYPE);
    try
    {
        if(CaselessStrCmp(cnvstr::ATTR_VAL_SINGLE,lociTypeString))
        {
            wxString locusName = tiwx_nodeText(lociMatchElem);
            if(locusName.IsEmpty())
            {
                throw gc_locus_match_single_empty();
            }
            if(!GetStructures().HasLocus(locusName))
            {
                throw gc_missing_locus(locusName);
            }
            GCLocusMatcher matcher(locmatch_SINGLE,locusName);
            return matcher;
        }
        if(CaselessStrCmp(cnvstr::ATTR_VAL_BYLIST,lociTypeString))
        {
            wxArrayString nameArray;
            std::vector<TiXmlElement*> names = tiwx_optionalChildren(lociMatchElem,cnvstr::TAG_SEGMENT_NAME);
            for(std::vector<TiXmlElement*>::iterator i = names.begin(); i != names.end(); i++)
            {
                TiXmlElement * locusNameElem = *i;
                wxString locusName = tiwx_nodeText(locusNameElem);
                if(!GetStructures().HasLocus(locusName))
                {
                    throw gc_missing_locus(locusName);
                }
                nameArray.Add(locusName);
            }
            GCLocusMatcher matcher(locmatch_VECTOR,nameArray);
            return matcher;
        }
        if(CaselessStrCmp(cnvstr::ATTR_VAL_LINKED,lociTypeString))
        {
            GCLocusMatcher matcher(locmatch_LINKED);
            return matcher;
        }
        if(CaselessStrCmp(cnvstr::ATTR_VAL_UNLINKED,lociTypeString))
        {
            // this is currently the default
            GCLocusMatcher matcher;
            return matcher;
        }
        if(CaselessStrCmp(cnvstr::ATTR_VAL_DEFAULT,lociTypeString))
        {
            GCLocusMatcher matcher;
            return matcher;
        }

        throw gc_locus_match_unknown(lociTypeString);
    }
    catch (gc_ex& e)
    {
        if(!e.hasRow())
        {
            e.setRow(lociMatchElem->Row());
        }
        throw;
    }

    assert(false);
    return GCLocusMatcher(locmatch_DEFAULT);
}

GCPopMatcher
GCDataStore::makePopMatcher(TiXmlElement * popMatchElem)
{
    // read type attribute
    wxString popTypeString = tiwx_attributeValue(popMatchElem,cnvstr::ATTR_TYPE);

    try
    {
        if(CaselessStrCmp(cnvstr::ATTR_VAL_SINGLE,popTypeString))
        {
            wxString popName = tiwx_nodeText(popMatchElem);
            if(popName.IsEmpty())
            {
                throw gc_pop_match_single_empty();
            }
            if(!GetStructures().HasPop(popName))
            {
                throw gc_missing_population(popName);
            }
            GCPopMatcher popM(popmatch_SINGLE,popName);
            return popM;
        }

        if(CaselessStrCmp(cnvstr::ATTR_VAL_BYLIST,popTypeString))
        {
            wxArrayString nameArray;
            std::vector<TiXmlElement*> names = tiwx_optionalChildren(popMatchElem,cnvstr::TAG_POP_NAME);
            for(std::vector<TiXmlElement*>::iterator i = names.begin(); i != names.end(); i++)
            {
                TiXmlElement * popNameElem = *i;
                wxString popName = tiwx_nodeText(popNameElem);
                if(!GetStructures().HasPop(popName))
                {
                    throw gc_missing_population(popName);
                }
                nameArray.Add(popName);
            }
            GCPopMatcher matcher(popmatch_VECTOR,nameArray);
            return matcher;
        }

        if(CaselessStrCmp(cnvstr::ATTR_VAL_BYNAME,popTypeString))
        {
            wxString popName = tiwx_nodeText(popMatchElem);
            if(!popName.IsEmpty())
            {
                throw gc_pop_match_byname_not_empty();
            }
            GCPopMatcher matcher(popmatch_NAME);
            return matcher;
        }

        if(CaselessStrCmp(cnvstr::ATTR_VAL_DEFAULT,popTypeString))
        {
            GCPopMatcher matcher;
            return matcher;
        }

        throw gc_pop_match_unknown(popTypeString);
    }
    catch (gc_ex& e)
    {
        if(!e.hasRow())
        {
            e.setRow(popMatchElem->Row());
        }
        throw;
    }

    assert(false);
    GCPopMatcher matcher(popmatch_DEFAULT);
    return matcher;
}

gcPhenotype &
GCDataStore::cmdParseGenoResolution(TiXmlElement * genoElem)
{
    try
    {
        TiXmlElement * traitElem = tiwx_requiredChild(genoElem,cnvstr::TAG_TRAIT_NAME);
        wxString traitName = tiwx_nodeText(traitElem);

        gcTraitInfo & trait = GetStructures().GetTrait(traitName);
        gcPhenotype & pheno = GetStructures().MakePhenotype();
        GetStructures().AssignPhenotype(pheno,trait);

        std::vector<TiXmlElement*> reso = tiwx_optionalChildren(genoElem,cnvstr::TAG_HAPLOTYPES);
        for(std::vector<TiXmlElement*>::iterator i = reso.begin(); i != reso.end(); i++)
        {
            gcHapProbability hapProb;

            TiXmlElement * hapElem = *i;

            TiXmlElement * probElem = tiwx_requiredChild(hapElem,cnvstr::TAG_PENETRANCE);
            double penetrance = tiwx_double_from_text(probElem);
            hapProb.SetPenetrance(penetrance);

            TiXmlElement * allelesElem = tiwx_requiredChild(hapElem,cnvstr::TAG_ALLELES);
            wxString allelesString = tiwx_nodeText(allelesElem);
            wxStringTokenizer tokenizer(allelesString);
            while(tokenizer.HasMoreTokens())
            {
                wxString alleleName = tokenizer.GetNextToken();
                const gcTraitAllele & allele = GetStructures().GetAllele(trait,alleleName);

#if 0
                assert(allele.HasTraitId());
                if(allele.GetTraitId() != trait.GetId())
                {
                    throw gc_allele_trait_mismatch(allele,trait,pheno,allelesElem->Row());
                }
#endif
                hapProb.AddAlleleId(allele.GetId());
            }
            pheno.AddHapProbability(hapProb);
        }

        return pheno;
    }
    catch (const incorrect_xml& x)
    {
        fileRejectingError(x.what(),genoElem->Row());
    }
    catch(gc_ex & e)
    {
        if(!e.hasRow())
        {
            e.setRow(genoElem->Row());
        }
        throw;
    }
    assert(false);
    throw implementation_error("Reached end of GCDataStore::cmdParseGenoResolution without returning.");
}

void
GCDataStore::cmdParseDivergence(TiXmlElement * dvgElem)
{
    if(dvgElem != NULL)
    {
        TiXmlElement* child1 = tiwx_requiredChild(dvgElem,cnvstr::TAG_DIV_CHILD1);
        wxString child1Name = tiwx_nodeText(child1);
        size_t child1Id;
        if (GetStructures().IsPop(child1Name))
        {
            child1Id = GetStructures().GetPop(child1Name).GetId();
        }
        else
        {
            child1Id = GetStructures().GetParent(child1Name).GetId();
        }

        TiXmlElement* child2 = tiwx_requiredChild(dvgElem,cnvstr::TAG_DIV_CHILD2);
        wxString child2Name = tiwx_nodeText(child2);
        size_t child2Id;
        if (GetStructures().IsPop(child2Name))
        {
            child2Id = GetStructures().GetPop(child2Name).GetId();
        }
        else
        {
            child2Id = GetStructures().GetParent(child2Name).GetId();
        }

        wxString ancestorName;
        TiXmlElement* div_ancestor = tiwx_optionalChild(dvgElem,cnvstr::TAG_DIV_ANCESTOR);
        if (div_ancestor != NULL)
        {
            ancestorName = tiwx_nodeText(div_ancestor);
        }
        else
        {
            ancestorName = "parent:";
            ancestorName += child1Name;
            ancestorName += ":";
            ancestorName += child2Name;

        }
        gcParent & parent = GetStructures().MakeParent(ancestorName);
        parent.SetChild1Id(child1Id);
        parent.SetChild2Id(child2Id);
        parent.SetBlessed(true);

        if (GetStructures().IsPop(child1Name))
        {
            GetStructures().GetPop(child1Name).SetParentId(parent.GetId());
        }
        else
        {
            GetStructures().GetParent(child1Name).SetParentId(parent.GetId());
        }

        if (GetStructures().IsPop(child2Name))
        {
            GetStructures().GetPop(child2Name).SetParentId(parent.GetId());
        }
        else
        {
            GetStructures().GetParent(child2Name).SetParentId(parent.GetId());
        }

        GetStructures().SetDivergenceState(true);
    }
}

void
GCDataStore::cmdParseDivergences(TiXmlElement * panelsElem)
{
    if(panelsElem != NULL)
    {
        std::vector<TiXmlElement*> panels = tiwx_requiredChildren(panelsElem,cnvstr::TAG_DIVERGENCE);
        for(std::vector<TiXmlElement*>::iterator i = panels.begin(); i != panels.end(); i++)
        {
            cmdParseDivergence(*i);
        }
    }
}


int
GCDataStore::ProcessCmdFile(wxString fileName)
{
    try
    {
        m_commandFileCurrentlyBeingParsed = fileName;

        CmdFileSchema schema;
        FrontEndWarnings warnings;
        XmlParser parser(schema,warnings);

        //parser.ParseFileData(fileName.c_str());
        parser.ParseFileData((const char *)fileName.mb_str());// JRM hack
        TiXmlElement * topElem = parser.GetRootElement();

        const char * value = topElem->Value();
        std::string topTag(value);
        bool matches = CaselessStrCmp(cnvstr::TAG_CONVERTER_CMD,topTag);
        if(!matches)
        {
            gc_data_error e((wxString::Format(cnvstr::ERR_BAD_TOP_TAG,topTag.c_str())).c_str());
            throw e;
        }

        cmdParseTraits        (tiwx_optionalChild(topElem,cnvstr::TAG_TRAITS));
        cmdParseRegions       (tiwx_optionalChild(topElem,cnvstr::TAG_REGIONS));
        cmdParsePopulations   (tiwx_optionalChild(topElem,cnvstr::TAG_POPULATIONS));
        cmdParsePanels        (tiwx_optionalChild(topElem,cnvstr::TAG_PANELS));
        cmdParseIndividuals   (tiwx_optionalChild(topElem,cnvstr::TAG_INDIVIDUALS),fileName);
        cmdParseInfiles       (tiwx_optionalChild(topElem,cnvstr::TAG_INFILES));
        cmdParseOutfile       (tiwx_optionalChild(topElem,cnvstr::TAG_OUTFILE));
        cmdParseComment       (tiwx_optionalChild(topElem,cnvstr::TAG_ADDCOMMENT));
        cmdParseDivergences   (tiwx_optionalChild(topElem,cnvstr::TAG_DIVERGENCES));

        std::vector<std::string> warningStrings = warnings.GetAndClearWarnings();
        if(!warningStrings.empty())
        {
            wxString combinedMsg = "";
            for(size_t i=0; i < warningStrings.size(); i++)
            {
                combinedMsg += wxString((warningStrings[i]).c_str());
            }
            GCWarning(combinedMsg);
        }

    }
    catch(const unrecognized_tag_error& e)
    {
        m_commandFileCurrentlyBeingParsed = wxEmptyString;
        wxString msg = wxString::Format(cnvstr::ERR_UNRECOGNIZED_TAG,
                                        e.what(),
                                        e.where());
        GCFatalBatchWarnGUI(msg);
    }
    catch(const data_error& e)
        // this is here to catch errors that come from the xml
        // processing
        // EWFIX.P4 -- make xml errors into their own error type
        // instead of just data_error type
    {
        m_commandFileCurrentlyBeingParsed = wxEmptyString;
        GCFatalBatchWarnGUI(e.what());
    }
    catch(const gc_ex& e)
    {
        m_commandFileCurrentlyBeingParsed = wxEmptyString;
        wxString msg = wxString::Format(gcerr_cmdfile::inCmdFile,fileName.c_str());
        if(e.hasRow())
        {
            msg += wxString::Format(gcerr_cmdfile::atRow,(int)e.getRow());
        }
        if(e.hasFile())
        {
            msg += wxString::Format(gcerr_cmdfile::inFile,e.getFile().c_str());
        }
        msg += wxString::Format(gcerr_cmdfile::messageIs,e.what());
        GCFatalBatchWarnGUI(msg);
        return 1;
    }
    catch(const std::exception& e)
    {
        m_commandFileCurrentlyBeingParsed = wxEmptyString;
        GCFatalBatchWarnGUI(wxString::Format(gcerr::uncaughtException,e.what()));
        return 1;
    }
    m_commandFileCurrentlyBeingParsed = wxEmptyString;
    return 0;
}

//____________________________________________________________________________________
