// $Id: gc_datastore_writebatch.cpp,v 1.11 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 "cnv_strings.h"
#include "gc_data.h"
#include "gc_datastore.h"
#include "gc_parse_block.h"
#include "gc_phase_info.h"
#include "gc_strings.h"
#include "tinyxml.h"
#include "wx/datetime.h"

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

TiXmlElement *
GCDataStore::CmdExportIndividuals() const
{
    const stringToRecord & indRecords = m_phaseInfo.GetIndividualRecords();
    if(indRecords.empty())
    {
        return NULL;
    }
    TiXmlElement * indsElem = new TiXmlElement(cnvstr::TAG_INDIVIDUALS.c_str());

    for(stringToRecord::const_iterator i=indRecords.begin(); i != indRecords.end(); i++)
    {
        const wxString & iName = (*i).first;
        const gcPhaseRecord & rec = (*i).second;

        TiXmlElement * iElem = new TiXmlElement(cnvstr::TAG_INDIVIDUAL.c_str());
        indsElem->LinkEndChild(iElem);

        TiXmlElement * name = new TiXmlElement(cnvstr::TAG_NAME.c_str());
        iElem->LinkEndChild(name);
        TiXmlText * iNameText = new TiXmlText(iName.c_str());
        name->LinkEndChild(iNameText);

        const gcIdSet & phenoIds = rec.GetPhenotypeIds();
        for(gcIdSet::const_iterator pIter = phenoIds.begin(); pIter != phenoIds.end(); pIter++)
        {
            const gcPhenotype & pheno = GetStructures().GetPhenotype(*pIter);
            if(pheno.HasExplicitName())
            {
                TiXmlElement * phenoRefE = new TiXmlElement(cnvstr::TAG_HAS_PHENOTYPE.c_str());
                iElem->LinkEndChild(phenoRefE);
                TiXmlText * phenoNameText = new TiXmlText(pheno.GetName().c_str());
                phenoRefE->LinkEndChild(phenoNameText);
            }
            else
            {
                iElem->LinkEndChild(CmdExportGenoReso(pheno));
            }
        }

        const wxArrayString & sampleNames = rec.GetSamples();
        for(size_t index=0; index < sampleNames.Count(); index++)
        {
            const wxString & sampleName = sampleNames[index];
            TiXmlElement * sampleE = new TiXmlElement(cnvstr::TAG_SAMPLE.c_str());
            iElem->LinkEndChild(sampleE);
            TiXmlElement * nameE = new TiXmlElement(cnvstr::TAG_NAME.c_str());
            sampleE->LinkEndChild(nameE);
            TiXmlText * nameText = new TiXmlText(sampleName);
            nameE->LinkEndChild(nameText);
        }
    }

    return indsElem;
}

TiXmlElement *
GCDataStore::CmdExportInfile(const GCFile& fileRef) const
{
    TiXmlElement * fileE = new TiXmlElement(cnvstr::TAG_INFILE.c_str());

    // file attributes
    GCFileFormat fform = fileRef.GetFormat();
    fileE->SetAttribute(cnvstr::ATTR_FORMAT.c_str(),ToWxString(fform).c_str());

    gcGeneralDataType dtype;

    if(GetStructures().HasParse(fileRef))
    {
        const GCParse & parseRef = GetStructures().GetParse(fileRef);
        constBlockVector blocks = parseRef.GetBlocks();
        if(!blocks.empty())
        {
            const GCParseBlock * blockP = blocks[0];
            assert(blockP != NULL);
            size_t locusId = GetStructures().GetLocusForBlock(blockP->GetId());
            const gcLocus & locusRef = GetStructures().GetLocus(locusId);
            dtype = locusRef.GetDataType();
        }
        else
        {
            dtype = parseRef.GetDataType();
        }
    }

    if(dtype.size() != 1)
    {
        gcGeneralDataType dtype = fileRef.GetGeneralDataType();
    }

    if(dtype.size() != 1)
        // put comment in to tell user what to do
    {
        TiXmlComment * comment = new TiXmlComment();
        comment->SetValue(gcstr::instructionsMultipleDataTypes.c_str());
        fileE->LinkEndChild(comment);

    }
    fileE->SetAttribute(cnvstr::ATTR_DATATYPE.c_str(),ToWxString(dtype).c_str());

    GCInterleaving inter = fileRef.GetInterleaving();
    if(inter == interleaving_MOOT)
        // sequences fit on one line, so assume
        // it's sequential
    {
        inter = interleaving_SEQUENTIAL;
    }
    fileE->SetAttribute(cnvstr::ATTR_SEQUENCEALIGNMENT.c_str(),ToWxString(inter));

    // name
    TiXmlElement * name = new TiXmlElement(cnvstr::TAG_NAME.c_str());
    TiXmlText * fname = new TiXmlText(fileRef.GetName().c_str());
    name->LinkEndChild(fname);
    fileE->LinkEndChild(name);

    const GCLocusMatcher & locMatch = GetStructures().GetLocusMatcher(fileRef);
    loc_match locMatchType = locMatch.GetLocMatchType();

    TiXmlElement * lMatch = new TiXmlElement(cnvstr::TAG_SEGMENTS_MATCHING.c_str());
    fileE->LinkEndChild(lMatch);
    lMatch->SetAttribute(cnvstr::ATTR_TYPE.c_str(),ToWxString(locMatchType).c_str());

    if(locMatchType != locmatch_DEFAULT)
    {
        if(locMatchType == locmatch_SINGLE)
        {
            const wxArrayString & locNames = locMatch.GetLociNames();
            TiXmlText * lname = new TiXmlText(locNames[0].c_str());
            lMatch->LinkEndChild(lname);
        }

        if(locMatchType == locmatch_VECTOR)
        {
            const wxArrayString & locNames = locMatch.GetLociNames();
            for(size_t i=0; i < locNames.Count(); i++)
            {
                TiXmlElement * locName = new TiXmlElement(cnvstr::TAG_SEGMENT_NAME.c_str());
                lMatch->LinkEndChild(locName);
                TiXmlText * lname = new TiXmlText(locNames[i].c_str());
                locName->LinkEndChild(lname);
            }
        }
    }

    const GCPopMatcher & popMatch = GetStructures().GetPopMatcher(fileRef);
    pop_match popMatchType = popMatch.GetPopMatchType();

    TiXmlElement * pMatch = new TiXmlElement(cnvstr::TAG_POP_MATCHING.c_str());
    fileE->LinkEndChild(pMatch);
    pMatch->SetAttribute(cnvstr::ATTR_TYPE.c_str(),ToWxString(popMatchType).c_str());

    if(popMatchType != popmatch_DEFAULT)
    {

        if(popMatchType == popmatch_SINGLE)
        {
            const wxArrayString & popNames = popMatch.GetPopNames();
            TiXmlText * pname = new TiXmlText(popNames[0].c_str());
            pMatch->LinkEndChild(pname);
        }

        if(popMatchType == popmatch_VECTOR)
        {
            const wxArrayString & popNames = popMatch.GetPopNames();
            for(size_t i=0; i < popNames.Count(); i++)
            {
                TiXmlElement * popName = new TiXmlElement(cnvstr::TAG_POP_NAME.c_str());
                pMatch->LinkEndChild(popName);
                TiXmlText * pname = new TiXmlText(popNames[i].c_str());
                popName->LinkEndChild(pname);
            }
        }
    }

    if(GetStructures().HasHapFileAdjacent(fileRef.GetId()))
    {
        size_t numAdj = GetStructures().GetHapFileAdjacent(fileRef.GetId());
        TiXmlElement * adj = new TiXmlElement(cnvstr::TAG_INDIVIDUALS_FROM_SAMPLES.c_str());
        adj->SetAttribute(cnvstr::ATTR_TYPE.c_str(),cnvstr::ATTR_VAL_BYADJACENCY.c_str());
        fileE->LinkEndChild(adj);
        TiXmlText * adjVal = new TiXmlText(wxString::Format("%d",(int)numAdj).c_str());
        adj->LinkEndChild(adjVal);
    }

    return fileE;
}

TiXmlElement *
GCDataStore::CmdExportLocus(const gcLocus& locusRef) const
{
    TiXmlElement * locE = new TiXmlElement(cnvstr::TAG_SEGMENT.c_str());
    TiXmlText * name = new TiXmlText(locusRef.GetName().c_str());
    locE->LinkEndChild(name);
    return locE;
}

TiXmlElement *
GCDataStore::CmdExportPhenotype(const gcPhenotype & pheno) const
{
    TiXmlElement * phenoE = new TiXmlElement(cnvstr::TAG_PHENOTYPE.c_str());

    // name
    TiXmlElement * name = new TiXmlElement(cnvstr::TAG_NAME.c_str());
    phenoE->LinkEndChild(name);
    TiXmlText * nameText = new TiXmlText(pheno.GetName());
    name->LinkEndChild(nameText);

    // geno reso
    phenoE->LinkEndChild(CmdExportGenoReso(pheno));
    return phenoE;
}

TiXmlElement *
GCDataStore::CmdExportGenoReso(const gcPhenotype & pheno) const
{
    // geno reso
    TiXmlElement * genoE = new TiXmlElement(cnvstr::TAG_GENO_RESOLUTIONS.c_str());

    assert(pheno.HasTraitId());
    const gcTraitInfo & trait = GetStructures().GetTrait(pheno.GetTraitId());
    TiXmlElement * tName = new TiXmlElement(cnvstr::TAG_TRAIT_NAME.c_str());
    genoE->LinkEndChild(tName);
    TiXmlText * tNameText = new TiXmlText(trait.GetName());
    tName->LinkEndChild(tNameText);

    const std::vector<gcHapProbability> & hapProbs = pheno.GetHapProbabilities();
    assert(!hapProbs.empty());
    for(size_t i=0; i < hapProbs.size(); i++)
    {
        const gcHapProbability & hprob = hapProbs[i];
        assert(hprob.HasPenetrance());

        TiXmlElement * hapE = new TiXmlElement(cnvstr::TAG_HAPLOTYPES.c_str());
        genoE->LinkEndChild(hapE);

        TiXmlElement * pen = new TiXmlElement(cnvstr::TAG_PENETRANCE.c_str());
        hapE->LinkEndChild(pen);
        TiXmlText * penText = new TiXmlText(wxString::Format("%f",hprob.GetPenetrance()));
        pen->LinkEndChild(penText);

        wxString alleleString = " ";
        const gcIdVec & alleleIds = hprob.GetAlleleIds();
        for(gcIdVec::const_iterator iter = alleleIds.begin(); iter != alleleIds.end(); iter++)
        {
            alleleString += GetStructures().GetAllele(*iter).GetName();
            alleleString += " ";
        }
        TiXmlElement * alleles = new TiXmlElement(cnvstr::TAG_ALLELES.c_str());
        hapE->LinkEndChild(alleles);
        TiXmlText * allelesText = new TiXmlText(alleleString);
        alleles->LinkEndChild(allelesText);
    }

    return genoE;
}

TiXmlElement *
GCDataStore::CmdExportPop(const gcPopulation & popRef) const
{
    TiXmlElement * popE = new TiXmlElement(cnvstr::TAG_POPULATION.c_str());
    TiXmlText * name = new TiXmlText(popRef.GetName().c_str());
    popE->LinkEndChild(name);
    return popE;
}

TiXmlElement *
GCDataStore::CmdExportTrait(const gcTraitInfo & traitRef) const
{
    TiXmlElement * traitE = new TiXmlElement(cnvstr::TAG_TRAIT_INFO.c_str());

    // one name
    TiXmlElement * traitName = new TiXmlElement(cnvstr::TAG_NAME.c_str());
    traitE->LinkEndChild(traitName);
    TiXmlText * nameText = new TiXmlText(traitRef.GetName().c_str());
    traitName->LinkEndChild(nameText);

    // many alleles
    const gcIdSet & alleleIds = traitRef.GetAlleleIds();
    for(gcIdSet::const_iterator i = alleleIds.begin(); i != alleleIds.end(); i++)
    {
        const gcTraitAllele & alleleRef = GetStructures().GetAllele(*i);
        TiXmlElement * alleleElem = new TiXmlElement(cnvstr::TAG_ALLELE.c_str());
        traitE->LinkEndChild(alleleElem);
        TiXmlText * alleleText = new TiXmlText(alleleRef.GetName().c_str());
        alleleElem->LinkEndChild(alleleText);
    }

    return traitE;
}

TiXmlElement *
GCDataStore::CmdExportRegion(const gcRegion & regRef) const
{
    TiXmlElement * regE = new TiXmlElement(cnvstr::TAG_REGION.c_str());

    // name
    TiXmlElement * name = new TiXmlElement(cnvstr::TAG_NAME.c_str());
    TiXmlText * rname = new TiXmlText(regRef.GetName().c_str());
    name->LinkEndChild(rname);
    regE->LinkEndChild(name);

    // effective pop size
    if(regRef.HasEffectivePopulationSize())
    {
        TiXmlElement * effPop = new TiXmlElement(cnvstr::TAG_EFFECTIVE_POPSIZE.c_str());
        TiXmlText * esize = new TiXmlText(wxString::Format("%f",regRef.GetEffectivePopulationSize()).c_str());
        effPop->LinkEndChild(esize);
        regE->LinkEndChild(effPop);
    }

    // spacing -- EWFIX -- may drop down to next level
    TiXmlElement * spacing = new TiXmlElement(cnvstr::TAG_SEGMENTS.c_str());
    regE->LinkEndChild(spacing);
    gcIdVec segIds = GetStructures().GetLocusIdsForRegionByMapPosition(regRef.GetId());
    for(gcIdVec::iterator i=segIds.begin(); i != segIds.end(); i++)
    {
        size_t locusId = *i;
        const gcLocus & locusRef = GetStructures().GetLocus(locusId);
        spacing->LinkEndChild(CmdExportSegment(locusRef));
    }

    // trait location
    const GCTraitInfoSet & traits = regRef.GetTraitInfoSet();
    for(GCTraitInfoSet::const_iterator i=traits.begin(); i != traits.end(); i++)
    {
        const gcTraitInfo & traitInfo = GetStructures().GetTrait(*i);
        TiXmlElement * traitElem = new TiXmlElement(cnvstr::TAG_TRAIT_LOCATION.c_str());
        regE->LinkEndChild(traitElem);
        TiXmlElement * traitName = new TiXmlElement(cnvstr::TAG_TRAIT_NAME.c_str());
        traitElem->LinkEndChild(traitName);
        TiXmlText * traitNameText = new TiXmlText(traitInfo.GetName().c_str());
        traitName->LinkEndChild(traitNameText);
    }

    return regE;
}

TiXmlElement *
GCDataStore::CmdExportSegment(const gcLocus& locusRef) const
{
    TiXmlElement * locusE = new TiXmlElement(cnvstr::TAG_SEGMENT.c_str());

    locusE->SetAttribute(cnvstr::ATTR_DATATYPE.c_str(),locusRef.GetDataTypeString().c_str());
    if(locusRef.HasLinkedUserValue())
    {
        locusE->SetAttribute(cnvstr::ATTR_PROXIMITY.c_str(),locusRef.GetLinkedUserValueString().c_str());
    }

    TiXmlElement * nameE = new TiXmlElement(cnvstr::TAG_NAME.c_str());
    TiXmlText * nameText = new TiXmlText(locusRef.GetName().c_str());
    nameE->LinkEndChild(nameText);
    locusE->LinkEndChild(nameE);

    if(locusRef.HasNumMarkers())
    {
        TiXmlElement * markersE = new TiXmlElement(cnvstr::TAG_MARKERS.c_str());
        TiXmlText * markersText = new TiXmlText(locusRef.GetNumMarkersString().c_str());
        markersE->LinkEndChild(markersText);
        locusE->LinkEndChild(markersE);
    }

    if(locusRef.HasOffset())
    {
        TiXmlElement * offsetE = new TiXmlElement(cnvstr::TAG_FIRST_POSITION_SCANNED.c_str());
        TiXmlText * offsetText = new TiXmlText(locusRef.GetOffsetString().c_str());
        offsetE->LinkEndChild(offsetText);
        locusE->LinkEndChild(offsetE);
    }

    if(locusRef.HasMapPosition())
    {
        TiXmlElement * mapE = new TiXmlElement(cnvstr::TAG_MAP_POSITION.c_str());
        TiXmlText * mapText = new TiXmlText(locusRef.GetMapPositionString().c_str());
        mapE->LinkEndChild(mapText);
        locusE->LinkEndChild(mapE);
    }

    if(locusRef.HasTotalLength())
    {
        TiXmlElement * lengthE = new TiXmlElement(cnvstr::TAG_SCANNED_LENGTH.c_str());
        TiXmlText * lengthText = new TiXmlText(locusRef.GetTotalLengthString().c_str());
        lengthE->LinkEndChild(lengthText);
        locusE->LinkEndChild(lengthE);
    }

    if(locusRef.HasLocations())
    {
        TiXmlElement * locationsE = new TiXmlElement(cnvstr::TAG_SCANNED_DATA_POSITIONS.c_str());
        TiXmlText * locationsText = new TiXmlText(locusRef.GetLocationsAsString().c_str());
        locationsE->LinkEndChild(locationsText);
        locusE->LinkEndChild(locationsE);
    }

    if(locusRef.HasUnphasedMarkers())
    {
        TiXmlElement * unphasedE = new TiXmlElement(cnvstr::TAG_UNRESOLVED_MARKERS.c_str());
        TiXmlText * unphasedText = new TiXmlText(locusRef.GetUnphasedMarkersAsString().c_str());
        unphasedE->LinkEndChild(unphasedText);
        locusE->LinkEndChild(unphasedE);
    }

    return locusE;
}

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

    TiXmlComment * comment = new TiXmlComment();
    wxDateTime now = wxDateTime::Now();
    comment->SetValue(wxString::Format(gcstr::batchOutComment,now.Format().c_str()).c_str());
    docP->LinkEndChild(comment);

    TiXmlElement * top = new TiXmlElement( cnvstr::TAG_CONVERTER_CMD.c_str() );
    docP->LinkEndChild( top );
    top->SetAttribute(cnvstr::ATTR_VERSION.c_str(),VERSION);

    constObjVector traits = GetStructures().GetConstTraits();
    if(!traits.empty())
    {
        TiXmlElement * traitsElem = new TiXmlElement( cnvstr::TAG_TRAITS.c_str() );
        top->LinkEndChild( traitsElem );

        for(constObjVector::const_iterator iter = traits.begin(); iter != traits.end(); iter++)
        {
            const gcTraitInfo * traitP = dynamic_cast<const gcTraitInfo*>(*iter);
            assert (traitP != NULL);
            traitsElem->LinkEndChild(CmdExportTrait(*traitP));
        }

        const gcPhenoMap & phenos = GetStructures().GetPhenotypeMap();
        for(gcPhenoMap::const_iterator i = phenos.begin(); i != phenos.end(); i++)
        {
            const gcPhenotype & phenoRef = (*i).second;
            if(phenoRef.HasExplicitName())
            {
                traitsElem->LinkEndChild(CmdExportPhenotype(phenoRef));
            }
        }

    }

    constObjVector regs = GetStructures().GetConstDisplayableRegions();
    if(!regs.empty())
    {
        TiXmlElement * regsElem = new TiXmlElement( cnvstr::TAG_REGIONS.c_str() );
        top->LinkEndChild( regsElem );
        for(constObjVector::const_iterator iter = regs.begin(); iter != regs.end(); iter++)
        {
            const gcRegion * regP = dynamic_cast<const gcRegion*>(*iter);
            assert (regP != NULL);
            regsElem->LinkEndChild(CmdExportRegion(*regP));
        }
    }

    constObjVector pops = GetStructures().GetConstDisplayablePops();
    if(!pops.empty())
    {
        TiXmlElement * popsElem = new TiXmlElement( cnvstr::TAG_POPULATIONS.c_str() );
        top->LinkEndChild( popsElem );
        for(constObjVector::const_iterator iter = pops.begin(); iter != pops.end(); iter++)
        {
            const gcPopulation * popP = dynamic_cast<const gcPopulation*>(*iter);
            assert (popP != NULL);
            popsElem->LinkEndChild(CmdExportPop(*popP));
        }
    }

    TiXmlElement * individualsElem = CmdExportIndividuals();
    if(individualsElem != NULL)
    {
        top->LinkEndChild( individualsElem );
    }

    const dataFileSet & files = GetDataFiles();
    if(!files.empty())
    {
        TiXmlElement * infElem = new TiXmlElement( cnvstr::TAG_INFILES.c_str() );
        top->LinkEndChild( infElem );
        for(dataFileSet::const_iterator iter= files.begin(); iter != files.end(); iter++)
        {
            const GCFile & fileRef = *(*iter);
            infElem->LinkEndChild(CmdExportInfile(fileRef));
        }
    }

    if(!(m_outfileName.IsEmpty()))
    {
        TiXmlElement * outf = new TiXmlElement( cnvstr::TAG_OUTFILE.c_str() );
        top->LinkEndChild( outf );
        TiXmlText * outName = new TiXmlText(m_outfileName.c_str());
        outf->LinkEndChild(outName);
    }

    if(!(m_commentString.IsEmpty()))
    {
        TiXmlElement * commentElem = new TiXmlElement( cnvstr::TAG_ADDCOMMENT.c_str() );
        top->LinkEndChild( commentElem );
        TiXmlText * commentText = new TiXmlText(m_commentString.c_str());
        commentElem->LinkEndChild(commentText);
    }

    return docP;

}

void
GCDataStore::WriteBatchFile(TiXmlDocument * docP, wxString fileName)
{
    docP->SaveFile( fileName.c_str());
}

//____________________________________________________________________________________
