// $Id: gc_region_dialogs.cpp,v 1.25 2018/01/03 21:33:00 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_datastore.h"
#include "gc_dialog.h"
#include "gc_errhandling.h"
#include "gc_layout.h"
#include "gc_region_dialogs.h"
#include "gc_strings.h"

#include "wx/checkbox.h"
#include "wx/log.h"
#include "wx/sizer.h"
#include "wx/statbox.h"
#include "wx/statline.h"
#include "wx/textctrl.h"

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

gcRegionRename::gcRegionRename(size_t regionId)
    :
    m_regionId(regionId)
{
}

gcRegionRename::~gcRegionRename()
{
}

wxString
gcRegionRename::FromDataStore(GCDataStore & dataStore)
{
    gcRegion & regionRef = dataStore.GetStructures().GetRegion(m_regionId);
    return regionRef.GetName();
}

void
gcRegionRename::ToDataStore(GCDataStore & dataStore, wxString newText)
{
    gcRegion & regionRef = dataStore.GetStructures().GetRegion(m_regionId);
    dataStore.GetStructures().Rename(regionRef,newText);
}

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

gcRegionEffPopSize::gcRegionEffPopSize(size_t regionId)
    :
    m_regionId(regionId)
{
}

gcRegionEffPopSize::~gcRegionEffPopSize()
{
}

wxString
gcRegionEffPopSize::FromDataStore(GCDataStore & dataStore)
{
    gcRegion & regionRef = dataStore.GetStructures().GetRegion(m_regionId);
    if(regionRef.HasEffectivePopulationSize())
    {
        return wxString::Format("%f",(double)regionRef.GetEffectivePopulationSize());
    }
    return gcstr::unsetValueRegionEffectivePopulationSize;
}

void
gcRegionEffPopSize::ToDataStore(GCDataStore & dataStore, wxString newText)
{
    double doubleVal;
    bool gotFloat = newText.ToDouble(&doubleVal);
    if(gotFloat)
    {
        gcRegion & regionRef = dataStore.GetStructures().GetRegion(m_regionId);
        regionRef.SetEffectivePopulationSize(doubleVal);
    }
}

const wxValidator &
gcRegionEffPopSize::GetValidator()
{
    return m_validator;
}

wxString
gcRegionEffPopSize::InitialString()
{
    return gcstr::unsetValueRegionEffectivePopulationSize;
}

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

gcRegionMergeChoice::gcRegionMergeChoice(   size_t  choiceRegionId,
                                            size_t  dialogRegionId)
    :
    m_choiceRegionId(choiceRegionId),
    m_dialogRegionId(dialogRegionId),
    m_box(NULL)
{
}

gcRegionMergeChoice::~gcRegionMergeChoice()
{
}

void
gcRegionMergeChoice::UpdateDisplayInitial(GCDataStore & dataStore)
{
    gcRegion & regionRef = dataStore.GetStructures().GetRegion(m_choiceRegionId);

    m_box->SetLabel(regionRef.GetName());
    m_box->SetValue(0);

    UpdateDisplayInterim(dataStore);
}

void
gcRegionMergeChoice::UpdateDisplayInterim(GCDataStore & dataStore)
{
    gcRegion & choiceRegion = dataStore.GetStructures().GetRegion(m_choiceRegionId);
    gcRegion & dialogRegion = dataStore.GetStructures().GetRegion(m_dialogRegionId);
    m_box->Enable(choiceRegion.CanMergeWith(dialogRegion));
}

void
gcRegionMergeChoice::UpdateDataInterim(GCDataStore & dataStore)
// nothing to do until the end at the enclosing level
{
}

void
gcRegionMergeChoice::UpdateDataFinal(GCDataStore & dataStore)
// nothing to do until the end at the enclosing level
{
}

wxWindow *
gcRegionMergeChoice::MakeWindow(wxWindow * parent)
{
    m_box = new wxCheckBox(parent,-1,wxEmptyString);
    return m_box;
}

wxWindow *
gcRegionMergeChoice::FetchWindow()
{
    assert(m_box != NULL);
    return m_box;
}

size_t
gcRegionMergeChoice::GetRelevantId()
{
    return m_choiceRegionId;
}

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

gcRegionMerge::gcRegionMerge(   wxWindow *                      parent,
                                size_t                          regionId,
                                std::vector<gcChoiceObject*>    choices)
    :
    gcUpdatingChooseMulti(parent,gcstr::mergeLinkGInstructions,choices),
    m_regionId(regionId)
{
}

gcRegionMerge::~gcRegionMerge()
{
}

void
gcRegionMerge::DoFinalForMulti(GCDataStore & dataStore, gcIdVec chosens)
{
    chosens.insert(chosens.begin(),m_regionId);
    dataStore.GetStructures().MergeRegions(chosens);
}

wxString
gcRegionMerge::NoChoicesText() const
{
    return gcstr::noChoiceRegion;
}

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

gcRegionFragmentChoice::gcRegionFragmentChoice(size_t regionId)
    :   m_regionId(regionId),
        m_box(NULL)
{
}

gcRegionFragmentChoice::~gcRegionFragmentChoice()
{
}

void
gcRegionFragmentChoice::UpdateDisplayInitial(GCDataStore & dataStore)
{
    m_box->SetLabel(gcstr::fragmentRegion);
    m_box->SetValue(0);
    UpdateDisplayInterim(dataStore);
}

void
gcRegionFragmentChoice::UpdateDisplayInterim(GCDataStore & dataStore)
{
    gcRegion & region = dataStore.GetStructures().GetRegion(m_regionId);
    m_box->Enable(region.GetLocusCount() > 1);
}

void
gcRegionFragmentChoice::UpdateDataInterim(GCDataStore & dataStore)
{
}

void
gcRegionFragmentChoice::UpdateDataFinal(GCDataStore & dataStore)
{
    if(m_box->GetValue() && m_box->IsEnabled())
    {
        dataStore.GetStructures().FragmentRegion(m_regionId);
    }
}

wxWindow *
gcRegionFragmentChoice::MakeWindow(wxWindow * parent)
{
    m_box = new wxCheckBox(parent,-1,wxEmptyString);
    return m_box;
}

wxWindow *
gcRegionFragmentChoice::FetchWindow()
{
    assert(m_box != NULL);
    return m_box;
}

size_t
gcRegionFragmentChoice::GetRelevantId()
{
    return m_regionId;
}

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

gcRegionFragmenter::gcRegionFragmenter( wxWindow *                      parent,
                                        size_t                          regionId,
                                        std::vector<gcChoiceObject*>    choices)

    :
    gcUpdatingChoose(parent,wxEmptyString,choices),
    m_regionId(regionId)
{
}

gcRegionFragmenter::~gcRegionFragmenter()
{
}

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

gcRegionEditDialog::gcRegionEditDialog( wxWindow *      parent,
                                        GCDataStore &   dataStore,
                                        size_t          regionId,
                                        bool            forJustCreatedObj)
    :
    gcUpdatingDialog(   parent,
                        dataStore,
                        forJustCreatedObj
                        ?
                        gcstr::addRegion
                        :
                        wxString::Format(gcstr::editRegion,
                                         dataStore.GetStructures().GetRegion(regionId).GetName().c_str()),
                        forJustCreatedObj),
    m_regionId(regionId)
{
}

gcRegionEditDialog::~gcRegionEditDialog()
{
}

void
gcRegionEditDialog::DoDelete()
{
    gcRegion & regionRef = m_dataStore.GetStructures().GetRegion(m_regionId);
    m_dataStore.GetStructures().RemoveRegion(regionRef);
}

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

bool DoDialogEditRegion(wxWindow *      parent,
                        GCDataStore &   dataStore,
                        size_t          regionId,
                        bool            forJustCreatedObj)
{
    gcRegionEditDialog dialog(parent,dataStore,regionId,forJustCreatedObj);

    gcDialogCreator creator;
    wxBoxSizer * contentSizer   = new wxBoxSizer(wxHORIZONTAL);
    wxBoxSizer * leftSizer      = new wxBoxSizer(wxVERTICAL);

    // for renaming
    gcTextHelper * regionRenameHelp = new gcRegionRename(regionId);
    gcUpdatingComponent * rename    = new gcUpdatingTextCtrl(&dialog,
                                                             forJustCreatedObj ? gcstr::regionNewName : gcstr::regionRename,
                                                             regionRenameHelp);

    gcTextHelper * effPopHelp = new gcRegionEffPopSize(regionId);
    gcUpdatingComponent * effPop    = new gcUpdatingTextCtrl(&dialog,
                                                             gcstr::regionEffPopSize,
                                                             effPopHelp);

    leftSizer->Add(rename,
                   0,
                   wxALL | wxALIGN_CENTER | wxEXPAND,
                   gclayout::borderSizeSmall);

    leftSizer->Add(effPop,
                   0,
                   wxALL | wxALIGN_CENTER | wxEXPAND,
                   gclayout::borderSizeSmall);

    creator.AddComponent(dialog,rename);
    creator.AddComponent(dialog,effPop);
    contentSizer->Add(leftSizer,
                      1,
                      wxALL | wxALIGN_CENTER | wxEXPAND,
                      gclayout::borderSizeSmall);

    if(!forJustCreatedObj)
    {
        wxBoxSizer * rightSizer    = new wxBoxSizer(wxVERTICAL);

        // everything for merging
        std::vector<gcChoiceObject*> regionChoices;
        gcDisplayOrder ids = dataStore.GetStructures().GetDisplayableRegionIds();
        for(gcDisplayOrder::iterator iter=ids.begin(); iter != ids.end(); iter++)
        {
            size_t id = *iter;
            if(id != regionId)
            {
                const gcRegion & regionChoice = dataStore.GetStructures().GetRegion(id);
                if(regionChoice.GetLocusCount() > 1  || dataStore.GetStructures().RegionHasAnyLinkedLoci(id))
                    // don't put up single locus regions containing unlinked data only
                {
                    gcRegionMergeChoice * choice = new gcRegionMergeChoice(id,regionId);
                    regionChoices.push_back(choice);
                }
            }
        }
        gcUpdatingComponent * merging = new gcRegionMerge(&dialog,regionId,regionChoices);

        // for fragmenting
        std::vector<gcChoiceObject*> fragmentChoices;
        fragmentChoices.push_back(new gcRegionFragmentChoice(regionId));
        gcUpdatingComponent * fragmenting = new gcRegionFragmenter(&dialog,regionId,fragmentChoices);

        rightSizer->Add(merging,
                        1,
                        wxALL | wxALIGN_CENTER | wxEXPAND,
                        gclayout::borderSizeSmall);
        rightSizer->Add(new wxStaticLine(&dialog,-1,wxDefaultPosition,wxDefaultSize,wxLI_HORIZONTAL),
                        0,
                        wxALL | wxALIGN_CENTER | wxEXPAND,
                        gclayout::borderSizeSmall);
        rightSizer->Add(fragmenting,
                        0,
                        wxALL | wxALIGN_CENTER | wxEXPAND,
                        gclayout::borderSizeSmall);
        creator.AddComponent(dialog,merging);
        creator.AddComponent(dialog,fragmenting);

        contentSizer->Add(new wxStaticLine(&dialog,-1,wxDefaultPosition,wxDefaultSize,wxLI_VERTICAL),
                          0,
                          wxALL | wxALIGN_CENTER | wxEXPAND,
                          gclayout::borderSizeSmall);

        contentSizer->Add(rightSizer,
                          1,
                          wxALL | wxALIGN_CENTER | wxEXPAND,
                          gclayout::borderSizeSmall);
    }

    creator.PlaceContent(dialog,contentSizer);
    return dialog.Go();
}

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

bool
gcActor_RegionAdd::OperateOn(wxWindow * parent, GCDataStore & dataStore)
{
    gcRegion & regionRef = dataStore.GetStructures().MakeRegion("",true);
    return DoDialogEditRegion(parent,dataStore,regionRef.GetId(),true);
}

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

bool
gcActor_RegionEdit::OperateOn(wxWindow * parent, GCDataStore & dataStore)
{
    return DoDialogEditRegion(parent,dataStore,m_regionId,false);
}

//____________________________________________________________________________________
