// $Id: gc_parse.cpp,v 1.24 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_default.h"
#include "gc_file.h"
#include "gc_file_util.h"
#include "gc_infile_err.h"
#include "gc_parse.h"
#include "gc_parse_block.h"
#include "gc_parse_locus.h"
#include "gc_parse_pop.h"
#include "gc_parse_sample.h"
#include "gc_strings.h"
#include "gc_types.h"
#include "wx/log.h"

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

GCParse::GCParse(   GCFile &            file,
                    GCFileFormat        format,
                    gcGeneralDataType   dtype,
                    GCInterleaving      interleaving,
                    wxString            delim)
    :
    m_filePointer(&file),
    m_format(format),
    m_dataType(dtype),
    m_interleaving(interleaving),
    m_delimiter(delim),
    m_multiLineSeenInFile(false),
    m_hasSpacesInNames(false)
{
}

GCParse::~GCParse()
{
    for(GCParseLoci::iterator i=m_loci.begin(); i != m_loci.end(); i++)
    {
        delete *i;
    }
    for(GCParsePops::iterator i=m_pops.begin(); i != m_pops.end(); i++)
    {
        delete *i;
    }
    for(GCParseBlocks::iterator i=m_blocks.begin(); i != m_blocks.end(); i++)
    {
        delete *i;
    }
}

wxString
GCParse::GetName() const
{
    assert(m_filePointer != NULL);
    return wxString::Format(gcstr::parseSettingsForFile,
                            m_filePointer->GetShortName().c_str(),
                            GetSettings().c_str());
}

wxString
GCParse::GetSettings() const
{
    wxString desc =
        wxString::Format(gcstr::parseSettings,
                         GetFormatString().c_str(),
                         GetDataTypeString().c_str(),
                         GetInterleavingString().c_str());

    return desc;
}

gcGeneralDataType
GCParse::GetDataType() const
{
    return m_dataType;
}

GCFileFormat
GCParse::GetFormat() const
{
    return m_format;
}

bool
GCParse::GetHasSpacesInNames() const
{
    return m_hasSpacesInNames;
}

GCInterleaving
GCParse::GetInterleaving() const
{
    return m_interleaving;
}

wxString
GCParse::GetDelimiter() const
{
    return m_delimiter;
}

bool
GCParse::GetMultiLineSeenInFile() const
{
    return m_multiLineSeenInFile;
}

void
GCParse::SetDataTypeFromFile(gcSpecificDataType dtype)
{
    if(dtype != sdatatype_NONE_SET)
    {
        gcGeneralDataType::iterator i = m_dataType.find(dtype);
        if(i == m_dataType.end())
        {
            throw gc_parse_data_type_spec_mismatch(dtype,GetDataType());
        }
        m_dataType.clear();
        m_dataType.insert(dtype);
    }
}

const GCParseLocus &
GCParse::GetParseLocus(size_t locusIndex) const
{
    assert(locusIndex < m_loci.size());
    return *(m_loci[locusIndex]);
}

GCParseLocus &
GCParse::GetParseLocus(size_t locusIndex)
{
    assert(locusIndex < m_loci.size());
    return *(m_loci[locusIndex]);
}

const GCParsePop &
GCParse::GetParsePop(size_t popIndex) const
{
    assert(popIndex < m_pops.size());
    return *(m_pops[popIndex]);
}

constBlockVector
GCParse::GetBlocks() const
{
    constBlockVector retVal;
    for(GCParseBlocks::const_iterator i = m_blocks.begin();
        i != m_blocks.end(); i ++)
    {
        const GCParseBlock * blockP = *i;
        retVal.push_back(blockP);
    }
    return retVal;
}

const GCParseBlock &
GCParse::GetBlock(size_t popId, size_t locusId) const
{
    // rather wasteful, but correct
    for(GCParseBlocks::const_iterator i = m_blocks.begin();
        i != m_blocks.end(); i ++)
    {
        const GCParseBlock & block = **i;
        size_t blockPopId = block.GetPopRef().GetIndexInParse();
        size_t blockLocId = block.GetLocusRef().GetIndexInParse();
        if((popId == blockPopId) && (locusId == blockLocId))
        {
            return block;
        }
    }
    wxString msg = wxString::Format(gcerr::noBlockForPopLocus,(int)popId,(int)locusId);
    gc_implementation_error e(msg.c_str());
    throw e;
}

const GCFile &
GCParse::GetFileRef() const
{
    return *m_filePointer;
}

size_t
GCParse::GetPopCount() const
{
    return m_pops.size();
}

size_t
GCParse::GetLociCount() const
{
    return m_loci.size();
}

void
GCParse::DebugDump(wxString prefix) const
{
    wxLogDebug("%sGCParse:%s",prefix.c_str(),GetSettings().c_str());    // EWDUMPOK
    wxLogDebug("%sPopulations:",(prefix+gcstr::indent).c_str());    // EWDUMPOK
    for(size_t i = 0; i < m_pops.size() ; i++)
    {
        const GCParsePop popRef = GetParsePop(i);
        wxLogDebug("%s%5d:\"%s\"",  // EWDUMPOK
                   (prefix+gcstr::indent+gcstr::indent).c_str(),
                   (int)(popRef.GetIndexInParse()),
                   (popRef.GetName()).c_str());
    }
    wxLogDebug("%sLoci:",(prefix+gcstr::indent).c_str());   // EWDUMPOK
    for(size_t i = 0; i < m_loci.size() ; i++)
    {
        const GCParseLocus locRef = GetParseLocus(i);
        wxLogDebug("%s%5d:%5d markers of type %s",    // EWDUMPOK
                   (prefix+gcstr::indent+gcstr::indent).c_str(),
                   (int)(locRef.GetIndexInParse()),
                   (int)(locRef.GetNumMarkers()),
                   (ToWxString(locRef.GetDataType())).c_str());
    }
    wxLogDebug("%sBlocks:",(prefix+gcstr::indent).c_str()); // EWDUMPOK
    for(size_t i = 0; i < m_blocks.size() ; i++)
    {
        const GCParseBlock & blockRef = *(m_blocks[i]);
        blockRef.DebugDump(prefix+gcstr::indent+gcstr::indent);
    }
}

gcIdSet
GCParse::IdsOfAllBlocks() const
{
    gcIdSet blockIds;
    for(size_t i=0; i < m_blocks.size(); i++)
    {
        const GCParseBlock & block = *(m_blocks[i]);
        blockIds.insert(block.GetId());
    }
    return blockIds;
}

wxString
GCParse::GetFormatString() const
{
    return wxString::Format(gcstr::parseFormat,ToWxString(GetFormat()).c_str());
}

wxString
GCParse::GetDataTypeString() const
{
    return wxString::Format(gcstr::parseDataType,ToWxString(GetDataType()).c_str());
}

wxString
GCParse::GetInterleavingString() const
{
    GCInterleaving il = GetInterleaving();
    if(il == interleaving_MOOT) il = interleaving_SEQUENTIAL; // EWFIX.P3 -- make blank ??
    return wxString::Format(gcstr::parseInterleaving,ToWxString(il).c_str());
}

gcPhaseInfo *
GCParse::GetDefaultPhaseRecords() const
{
    gcPhaseInfo * phaseRecords = new gcPhaseInfo();

    const wxString & fileName = GetFileRef().GetName();
    for(GCParseBlocks::const_iterator i = m_blocks.begin(); i != m_blocks.end(); i++)
    {
        const GCParseBlock * pbP = *i;
        const GCParseSamples & samples = pbP->GetSamples();
        for(GCParseSamples::const_iterator j=samples.begin(); j != samples.end(); j++)
        {
            const GCParseSample * sampP = *j;
            if(sampP->GetSequencesPerLabel() > 1)
            {
                gcPhaseRecord rec
                    = gcPhaseRecord::MakeAllelicPhaseRecord(fileName,
                                                            sampP->GetLine(),
                                                            sampP->GetLabel(),
                                                            sampP->GetSequencesPerLabel());
                phaseRecords->AddRecord(rec);
            }
        }
    }
    return phaseRecords;
}

gcPhaseInfo *
GCParse::GetPhaseRecordsForAdjacency(size_t adj) const
{
    gcPhaseInfo * phaseRecords = new gcPhaseInfo();

    const wxString & fileName = GetFileRef().GetName();
    for(GCParseBlocks::const_iterator i = m_blocks.begin(); i != m_blocks.end(); i++)
    {
        const GCParseBlock * pbP = *i;
        const GCParseSamples & samples = pbP->GetSamples();
        wxArrayString holdingArray;
        const GCParseSample * sampP = NULL;
        for(GCParseSamples::const_iterator j=samples.begin(); j != samples.end(); j++)
        {
            sampP = *j;
            if(sampP->GetSequencesPerLabel() > 1)
            {
                delete phaseRecords;
                throw gc_adjacent_phase_resolution_for_multisample_input(GetFileRef().GetName());
            }
            else
            {
                holdingArray.Add(sampP->GetLabel());
                if(holdingArray.Count() == adj)
                {
                    gcPhaseRecord rec
                        = gcPhaseRecord::MakeAdjacentPhaseRecord(fileName,sampP->GetLine(),holdingArray);
                    phaseRecords->AddRecord(rec);
                    holdingArray.Empty();
                }
            }
        }
        if(! holdingArray.IsEmpty())
        {
            assert(sampP != NULL);
            size_t lineNum = sampP->GetLine();
            wxString fname = GetFileRef().GetName();
            size_t numSamples = samples.size();
            delete phaseRecords;
            throw gc_individual_sample_adj_mismatch(lineNum,fname,numSamples,adj);
        }
    }
    return phaseRecords;
}

void
GCParse::SetCannotBeMsat()
{
    m_dataType.Disallow(sdatatype_MICROSAT);
}

void
GCParse::SetHasSpacesInNames()
{
    m_hasSpacesInNames = true;
}

GCParseVec::GCParseVec()
    :
    std::vector<GCParse*>()
{
}

GCParseVec::~GCParseVec()
{
}

void
GCParseVec::NukeContents()
{
    for(iterator i=begin(); i != end(); i++)
    {
        delete *i;
    }
}

bool
GCParseVec::MungeParses(GCParseVec::iterator i1, GCParseVec::iterator i2)
{
    GCParse & p1 = **(i1);
    GCParse & p2 = **(i2);
    if(p1.GetFormat() != p2.GetFormat()) return false;
    if(p1.GetDataType() != p2.GetDataType()) return false;

    if(p1.GetMultiLineSeenInFile()) return false;
    if(p2.GetMultiLineSeenInFile()) return false;

    p1.m_interleaving = interleaving_MOOT;
    return true;
}

bool
GCParseVec::MungeParses()
{
    bool mungedAnything = false;
    std::vector<GCParse*>::iterator outerIter = begin();
    while(outerIter != end())
    {
        std::vector<GCParse*>::iterator innerIter = outerIter;
        innerIter++;
        while(innerIter != end())
        {
            if(MungeParses(outerIter,innerIter))
            {
                mungedAnything = true;
                delete *innerIter;
                erase(innerIter);
            }
            else
            {
                innerIter++;
            }
        }
        outerIter++;
    }
    return mungedAnything;
}

//____________________________________________________________________________________
