// $Id: gc_phase_info.cpp,v 1.14 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_phase_err.h"
#include "gc_phase_info.h"
#include "gc_strings.h"
#include "gc_strings_phase.h"
#include "wx/log.h"

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

gcPhaseRecord::gcPhaseRecord()
    :
    m_phaseSource(phaseSource_NONE_SET),
    m_fileName(wxEmptyString),
    m_hasLineNumber(false),
    m_lineNumber(0),
    m_individual(wxEmptyString),
    m_sampleCountIfNoSamples(0)
{
    assert(m_samples.IsEmpty());
    assert(m_unphasedInfo.empty());
}

gcPhaseRecord::~gcPhaseRecord()
{
}

void
gcPhaseRecord::AddPhenotypeId(size_t phenoId)
{
    m_phenotypeIds.insert(phenoId);
}

const gcIdSet &
gcPhaseRecord::GetPhenotypeIds() const
{
    return m_phenotypeIds;
}

void
gcPhaseRecord::MergePhenotypeIds(const gcPhaseRecord & otherRec)
{
    m_phenotypeIds.insert(otherRec.m_phenotypeIds.begin(),otherRec.m_phenotypeIds.end());
}

gcPhaseSource
gcPhaseRecord::GetPhaseSource() const
{
    return m_phaseSource;
}

wxString
gcPhaseRecord::GetDescriptiveName() const
{
    wxString indName = gcstr::unknown;
    wxString lineName = gcstr::unknown;
    wxString fileName = gcstr::unknown;
    wxString sizeName = wxString::Format("%ld",(long)GetSampleCount());
    if(HasIndividual())
    {
        indName = GetIndividual();
    }
    if(HasFileName())
    {
        fileName = GetFileName();
    }
    if(HasLineNumber())
    {
        lineName = wxString::Format("%ld",(long)(GetLineNumber()));
    }
    wxString samplesString = "";
    wxArrayString sampleNames = GetSamples();
    for(size_t i=0; i < sampleNames.Count(); i++)
    {
        if(i != 0)
        {
            samplesString += ", ";
        }
        samplesString += wxString::Format("\"%s\"",sampleNames[i].c_str());
    }

    switch(m_phaseSource)
    {
        case phaseSource_NONE_SET:
            // EWFIX -- better reply
            return gcstr::unknown;
            break;
        case phaseSource_PHASE_FILE:
            return wxString::Format(gcstr_phase::descPhaseFile,indName.c_str(),lineName.c_str(),fileName.c_str(),samplesString.c_str());
            break;
        case phaseSource_MULTI_PHASE_SAMPLE:
            return wxString::Format(gcstr_phase::descMultiPhase,sizeName.c_str(),lineName.c_str(),fileName.c_str(),samplesString.c_str());
            break;
        case phaseSource_FILE_ADJACENCY:
            return wxString::Format(gcstr_phase::descFileAdjacency,lineName.c_str(),fileName.c_str(),samplesString.c_str());
            break;
        case phaseSource_COUNT:
            assert(false);
            return gcstr::unknown;
            break;
    }
    assert(false);
    return wxT("");
}

bool
gcPhaseRecord::HasFileName() const
{
    return (! (m_fileName.IsEmpty()));
}

const wxString &
gcPhaseRecord::GetFileName() const
{
    assert(HasFileName());
    return m_fileName;
}

bool
gcPhaseRecord::HasLineNumber() const
{
    return m_hasLineNumber;
}

size_t
gcPhaseRecord::GetLineNumber() const
{
    return m_lineNumber;
}

void
gcPhaseRecord::SetLineNumber(size_t lineNumber)
{
    m_hasLineNumber = true;
    m_lineNumber = lineNumber;
}

bool
gcPhaseRecord::HasIndividual() const
{
    return (! (m_individual.IsEmpty()));
}

const wxString &
gcPhaseRecord::GetIndividual() const
{
    assert(HasIndividual());
    return m_individual;
}

bool
gcPhaseRecord::HasSamples() const
{
    return (! (m_samples.IsEmpty()));
}

const wxArrayString &
gcPhaseRecord::GetSamples() const
{
    assert(HasSamples());
    return m_samples;
}

size_t
gcPhaseRecord::GetSampleCount() const
{
    if(! HasSamples())
    {
        return m_sampleCountIfNoSamples;
    }
    return m_samples.Count();
}

bool
gcPhaseRecord::HasAnyZeroes() const
{
    for(gcIndPhaseInfo::const_iterator i = m_unphasedInfo.begin(); i != m_unphasedInfo.end(); i++)
    {
        const gcUnphasedMarkers & markers = (*i).second;
        if(markers.HasZero()) return true;
    }
    return false;
}

void
gcPhaseRecord::AddUnphased(wxString locusName, const gcUnphasedMarkers & unphased)
{
    gcIndPhaseInfo::iterator iter = m_unphasedInfo.find(locusName);
    if(iter == m_unphasedInfo.end())
    {
        m_unphasedInfo[locusName] = unphased;
    }
    else
    {
        gcUnphasedMarkers & oldUnphased = (*iter).second;
        oldUnphased.Merge(unphased);
    }
}

bool
gcPhaseRecord::HasUnphased(wxString locusName) const
{
    gcIndPhaseInfo::const_iterator iter = m_unphasedInfo.find(locusName);
    return (iter != m_unphasedInfo.end());
}

const gcUnphasedMarkers &
gcPhaseRecord::GetUnphased(wxString locusName) const
{
    assert(HasUnphased(locusName));
    gcIndPhaseInfo::const_iterator iter = m_unphasedInfo.find(locusName);
    const gcUnphasedMarkers & markers = (*iter).second;
    return markers;
}

wxArrayString
gcPhaseRecord::GetUnphasedLocusNames() const
{
    wxArrayString names;
    for(gcIndPhaseInfo::const_iterator i = m_unphasedInfo.begin(); i != m_unphasedInfo.end(); i++)
    {
        const wxString & name = (*i).first;
        names.Add(name);
    }
    return names;
}

bool
gcPhaseRecord::operator==(const gcPhaseRecord& rec) const
{
    if (GetPhaseSource() != rec.GetPhaseSource()) return false;

    if (HasFileName() != rec.HasFileName()) return false;
    if (GetFileName() != rec.GetFileName()) return false;

    if (HasIndividual() != rec.HasIndividual()) return false;
    if (GetIndividual() != rec.GetIndividual()) return false;

    if (HasSamples() != rec.HasSamples()) return false;
    if (GetSamples() != rec.GetSamples()) return false;
    if (GetSampleCount() != rec.GetSampleCount()) return false;

    wxArrayString locusNames = GetUnphasedLocusNames();
    wxArrayString recLocusNames = rec.GetUnphasedLocusNames();
    if (locusNames.Count() != recLocusNames.Count()) return false;
    for(size_t i=0; i < locusNames.Count(); i++)
    {
        wxString locusName = locusNames[i];

        if(HasUnphased(locusName) != rec.HasUnphased(locusName)) return false;
        if(GetUnphased(locusName) != rec.GetUnphased(locusName)) return false;
    }

    return true;
}

bool
gcPhaseRecord::operator!=(const gcPhaseRecord & rec) const
{
    return !(operator==(rec));
}

void
gcPhaseRecord::DebugDump(wxString prefix) const
{
    wxString indName = "";
    if(HasIndividual())
    {
        indName = GetIndividual();
    }
    wxLogDebug("%s%s:",prefix.c_str(),indName.c_str());

    wxLogDebug("%s%sphasesource: %s",prefix.c_str(),gcstr::indent.c_str(),
               ToWxString(GetPhaseSource()).c_str());

    if( !(HasSamples()) )
    {
        wxLogDebug("%s%ssampleCount: %d",prefix.c_str(),gcstr::indent.c_str(),
                   (int)GetSampleCount());
    }
    else
    {
        const wxArrayString & samples = GetSamples();
        for(size_t i = 0 ; i < samples.Count(); i++)
        {
            wxLogDebug("%s%ssample:%s", prefix.c_str(),gcstr::indent.c_str(),
                       samples[i].c_str());
        }
    }
    wxLogDebug("%s%sfilename: %s",prefix.c_str(),gcstr::indent.c_str(),
               GetFileName().c_str());

    wxLogDebug("%s%sphenoIds: %s",prefix.c_str(),gcstr::indent.c_str(),GetPhenotypeIds().AsString().c_str());

    wxArrayString locusNames = GetUnphasedLocusNames();
    for(size_t i=0; i < locusNames.Count(); i++)
    {
        wxString locusName = locusNames[i];

        if(HasUnphased(locusName))
        {
            wxLogDebug("%s%sunphased markers for %s: %s",
                       prefix.c_str(),
                       gcstr::indent.c_str(),
                       locusName.c_str(),
                       GetUnphased(locusName).AsString().c_str());
        }

    }

}

gcPhaseRecord
gcPhaseRecord::MakeAdjacentPhaseRecord( wxString        fileName,
                                        size_t          lineNumber,
                                        wxArrayString   samples)
{
    gcPhaseRecord newRec;
    newRec.m_phaseSource = phaseSource_FILE_ADJACENCY;
    newRec.m_fileName = fileName;
    newRec.SetLineNumber(lineNumber);
    newRec.m_samples = samples;
    return newRec;
}

gcPhaseRecord
gcPhaseRecord::MakeAllelicPhaseRecord(  wxString        fileName,
                                        size_t          lineNumber,
                                        wxString        individualName,
                                        size_t          numSamples)
{
    gcPhaseRecord newRec;
    newRec.m_phaseSource = phaseSource_MULTI_PHASE_SAMPLE;
    newRec.m_fileName = fileName;
    newRec.SetLineNumber(lineNumber);
    newRec.m_individual = individualName;
    newRec.m_sampleCountIfNoSamples = numSamples;
    assert(newRec.m_samples.IsEmpty());
    return newRec;
}

gcPhaseRecord *
gcPhaseRecord::MakeFullPhaseRecord(     wxString        fileName,
                                        size_t          lineNumber,
                                        wxString        individualName,
                                        wxArrayString   samples)
{
    gcPhaseRecord * newRec = new gcPhaseRecord();
    newRec->m_phaseSource = phaseSource_PHASE_FILE;
    newRec->m_fileName = fileName;
    newRec->SetLineNumber(lineNumber);
    newRec->m_individual = individualName;
    newRec->m_samples = samples;
    return newRec;
}

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

gcPhaseInfo::gcPhaseInfo()
{
};

gcPhaseInfo::~gcPhaseInfo()
{
};

bool
gcPhaseInfo::AddRecordIndividual(const gcPhaseRecord & rec)
{
    if(rec.HasIndividual())
        // nothing to do if it doesn't
    {
        const wxString & indName = rec.GetIndividual();
        if(HasIndividualRecord(indName))
            // need to merge info or complain if not possible
        {
            bool didReplace = MergeIndividualRecs(GetIndividualRecord(indName),rec);
            return didReplace;
        }
        else
        {
            m_fromIndividual.insert(recordPair(indName,rec));
            return true;
        }
    }
    return false;
}

bool
gcPhaseInfo::AddRecordSample(const gcPhaseRecord & rec)
{

    bool addedAnything = false;
    // the samples half
    if(rec.HasSamples())
    {
        wxArrayString samples = rec.GetSamples();
        bool anyPresent = false;

        for(size_t i=0; i < samples.Count(); i++)
        {
            if(HasSampleRecord(samples[i]))
                // checking that if this sample name already occurs, it
                // occurs in the same configuration
            {
                anyPresent = true;
                const gcPhaseRecord & oldRecord = GetSampleRecord(samples[i]);
                assert(oldRecord.HasSamples());
                if(oldRecord.GetSamples() != samples)
                {
                    throw gc_phase_mismatch(oldRecord,rec);
                }
            }
        }

        if(anyPresent == false)
        {
            for(size_t i=0; i < samples.Count(); i++)
            {
                m_fromSample[samples[i]] = rec;
                addedAnything = true;
            }
        }
    }
    return addedAnything;
}

bool
gcPhaseInfo::MergeIndividualRecs(   const gcPhaseRecord & oldRec,
                                    const gcPhaseRecord & newRec)
{
    assert(oldRec.HasIndividual());
    assert(newRec.HasIndividual());
    assert(oldRec.GetIndividual() == newRec.GetIndividual());

    if(oldRec.GetSampleCount() != newRec.GetSampleCount())
    {
        throw gc_phase_mismatch(oldRec,newRec);
    }

    if(oldRec.HasSamples())
    {
        if(newRec.HasSamples())
            // need to make sure they match
        {
            if(oldRec.GetSamples() != newRec.GetSamples())
            {
                throw gc_phase_mismatch(oldRec,newRec);
            }
        }
    }
    else
    {
        if(newRec.HasSamples())
            // need to replace old rec with this rec
        {
            gcPhaseRecord replacement = newRec;
            replacement.MergePhenotypeIds(oldRec);
            m_fromIndividual[newRec.GetIndividual()] = replacement;
            return true;
        }
    }
    gcPhaseRecord replacement = oldRec;
    replacement.MergePhenotypeIds(newRec);
    m_fromIndividual[oldRec.GetIndividual()] = replacement;
    return false;

}

void
gcPhaseInfo::AddRecord(const gcPhaseRecord & phaseRecord)
{
    bool addedI = AddRecordIndividual(phaseRecord);
    bool addedS = AddRecordSample(phaseRecord);

    assert(     (phaseRecord.GetPhaseSource() != phaseSource_PHASE_FILE)
                ||  (addedI == addedS) );

#ifdef NDEBUG  // Silence compiler warning if variables not used.
    (void)addedI;
    (void)addedS;
#endif // NDEBUG
}

void
gcPhaseInfo::AddRecords(const gcPhaseInfo & rs)
{
    for(stringToRecord::const_iterator i = rs.m_fromIndividual.begin();
        i != rs.m_fromIndividual.end();
        i++)
    {
        const gcPhaseRecord & rec = (*i).second;
        AddRecord(rec);
    }
    for(stringToRecord::const_iterator i = rs.m_fromSample.begin();
        i != rs.m_fromSample.end();
        i++)
    {
        const gcPhaseRecord & rec = (*i).second;
        AddRecord(rec);
    }
}

bool
gcPhaseInfo::HasIndividualRecord(wxString name) const
{
    stringToRecord::const_iterator iter = m_fromIndividual.find(name);
    return (iter != m_fromIndividual.end());
}

const gcPhaseRecord &
gcPhaseInfo::GetIndividualRecord(wxString name) const
{
    stringToRecord::const_iterator iter = m_fromIndividual.find(name);
    assert (iter != m_fromIndividual.end());
    return (*iter).second;
}

bool
gcPhaseInfo::HasSampleRecord(wxString name) const
{
    stringToRecord::const_iterator iter = m_fromSample.find(name);
    return (iter != m_fromSample.end());
}

const gcPhaseRecord &
gcPhaseInfo::GetSampleRecord(wxString name) const
{
    stringToRecord::const_iterator iter = m_fromSample.find(name);
    assert (iter != m_fromSample.end());
    return (*iter).second;
}

void
gcPhaseInfo::DebugDump(wxString prefix) const
{
    wxLogDebug("%sIndividual phase records:",prefix.c_str());
    for(stringToRecord::const_iterator i=m_fromIndividual.begin(); i != m_fromIndividual.end(); i++)
    {
        (*i).second.DebugDump(prefix+gcstr::indent);
    }

    wxLogDebug("%sSample phase records:",prefix.c_str());
    for(stringToRecord::const_iterator i=m_fromSample.begin(); i != m_fromSample.end(); i++)
    {
        (*i).second.DebugDump(prefix+gcstr::indent);
    }
}

const stringToRecord &
gcPhaseInfo::GetIndividualRecords() const
{
    return m_fromIndividual;
}

bool
gcPhaseInfo::HasAnyZeroes() const
{
    for(stringToRecord::const_iterator i=m_fromIndividual.begin(); i != m_fromIndividual.end(); i++)
    {
        const gcPhaseRecord & rec = (*i).second;
        if(rec.HasAnyZeroes()) return true;
    }

    for(stringToRecord::const_iterator i=m_fromSample.begin(); i != m_fromSample.end(); i++)
    {
        const gcPhaseRecord & rec = (*i).second;
        if(rec.HasAnyZeroes()) return true;
    }
    return false;
}

//____________________________________________________________________________________
