// $Id: gc_parent_dialogs.cpp,v 1.4 2018/01/03 21:32:59 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_parent_dialogs.h"
#include "gc_strings.h"
#include "gc_structures_err.h"
#include "gc_event_ids.h"
#include "gc_event_publisher.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"

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

gcUpdatingChooseTwo::gcUpdatingChooseTwo(
    wxWindow *                      parent,
    wxString                        instructions,
    std::vector<gcChoiceObject*>    choices)
    :
    gcUpdatingChooseMulti(parent,instructions,choices)
{
}

gcUpdatingChooseTwo::~gcUpdatingChooseTwo()
{
}

void
gcUpdatingChooseTwo::BuildDisplay(GCDataStore & dataStore)
{
    gcUpdatingChoose::BuildDisplay(dataStore);

    wxBoxSizer * buttonSizer = new wxBoxSizer(wxHORIZONTAL);
    wxBoxSizer * leftSizer      = new wxBoxSizer(wxVERTICAL);
    buttonSizer->AddStretchSpacer(1);
    wxLogVerbose("gcUpdatingChooseTwo::BuildDisplay m_choices.size(): %i", (int)m_choices.size());  // JMDBG
    if (m_choices.size() > 2)
    {
        buttonSizer->Add(new wxButton(this,GC_UnselectAll,gcstr::buttonUnselectAll),
                         0,
                         wxALL | wxALIGN_CENTER ,
                         gclayout::borderSizeSmall);
    }
    else
    {
        for(size_t index=0; index < m_choices.size(); index++)
        {
            gcChoiceObject * choice = m_choices[index];
            wxWindow * window = choice->FetchWindow();
            wxCheckBox * cb = dynamic_cast<wxCheckBox*>(window);
            if(cb->IsEnabled())
            {
                cb->SetValue(1);
            }
        }
    }

    m_statBoxSizer->AddStretchSpacer(1);
    m_statBoxSizer->Add(buttonSizer,
                        0,
                        wxALL | wxALIGN_LEFT | wxALIGN_TOP | wxEXPAND ,
                        gclayout::borderSizeSmall);
}

void
gcUpdatingChooseTwo::OnCheck(wxCommandEvent & event)
{
    wxLogVerbose("gcUpdatingChooseTwo::OnCheck called");  // JMDBG
    wxObject * obj = event.GetEventObject();
    int npicked = 0;
    for(size_t index=0; index < m_choices.size(); index++)
    {
        gcChoiceObject * choice = m_choices[index];
        wxWindow * window = choice->FetchWindow();
        wxCheckBox * cb = dynamic_cast<wxCheckBox*>(window);
        assert(cb != NULL);
        if(cb->GetValue() > 0)
        {
            npicked++;
        }
    }

    if(event.IsChecked())
    {
        if (npicked >= 2)
        {
            for(size_t index=0; index < m_choices.size(); index++)
            {
                gcChoiceObject * choice = m_choices[index];
                wxWindow * window = choice->FetchWindow();
                wxCheckBox * cb = dynamic_cast<wxCheckBox*>(window);
                assert(cb != NULL);
                if(cb->GetValue() <= 0)
                {
                    cb->Enable(false);
                }
            }
        }
    }
    else
    {
        if (npicked < 2)
        {
            for(size_t index=0; index < m_choices.size(); index++)
            {
                gcChoiceObject * choice = m_choices[index];
                wxWindow * window = choice->FetchWindow();
                wxCheckBox * cb = dynamic_cast<wxCheckBox*>(window);
                assert(cb != NULL);
                cb->Enable(true);
            }
        }
    }
}

void
gcUpdatingChooseTwo::UpdateDataFinal(GCDataStore & dataStore)
{
    gcUpdatingChoose::UpdateDataFinal(dataStore);

    gcIdVec checkedIds;
    for(size_t index=0; index < m_choices.size(); index++)
    {
        gcChoiceObject * choice = m_choices[index];
        wxWindow * window = choice->FetchWindow();
        wxCheckBox * cb = dynamic_cast<wxCheckBox*>(window);
        assert(cb != NULL);
        if(cb->GetValue() > 0)
        {
            checkedIds.push_back(choice->GetRelevantId());
        }
    }

    wxLogVerbose("****gcUpdatingChooseTwo::DoDataFinal checkedIds.size(): %i", (int)checkedIds.size());  // JMDBG
    if(checkedIds.size() != 2)
    {
        throw gc_wrong_divergence_error();
    }
    else
    {
        DoFinalForMulti(dataStore, checkedIds);
    }
}

void
gcUpdatingChooseTwo::OnUnselectAll(wxCommandEvent & event)
{
    wxLogVerbose("gcUpdatingChooseTwo::OnUnselectAll m_choices.size(): %i", (int)m_choices.size());  // JMDBG
    for(size_t index=0; index < m_choices.size(); index++)
    {
        gcChoiceObject * ckbox = m_choices[index];
        wxWindow * window = ckbox->FetchWindow();
        wxCheckBox * cb = dynamic_cast<wxCheckBox*>(window);
        cb->SetValue(0);
        cb->Enable(true);
    }
}

void
gcUpdatingChooseTwo::DoFinalForMulti(GCDataStore & dataStore, gcIdVec checkedIds)
{
    wxLogVerbose("****gcUpdatingChooseTwo::DoFinalForMulti parentId: %i", (int)m_childParentId);  // JMDBG
    gcParent & parent = dataStore.GetStructures().GetParent(m_childParentId);

    // find how many populations already have display order set
    int nPopsHaveOrder = 0;
    gcDisplayOrder popids = dataStore.GetStructures().GetDisplayablePopIds();
    for(gcDisplayOrder::iterator iter=popids.begin(); iter != popids.end(); iter++)
    {
        size_t id = *iter;
        if (dataStore.GetStructures().GetPop(*iter).HasDispOrder())
        {
            nPopsHaveOrder++;
        }
    }

    // set child 1 parent
    gcIdVec::iterator iter = checkedIds.begin();
    size_t child1Id = *iter;
    //wxLogVerbose("    Child1Id: %i", (int)child1Id);  // JMDBG
    parent.SetChild1Id(child1Id);
    int nPopsFound = 0;
    if (dataStore.GetStructures().IsPop(child1Id))
    {
        nPopsFound++;
        dataStore.GetStructures().GetPop(child1Id).SetParentId(m_childParentId);
        nPopsHaveOrder++;
        dataStore.GetStructures().GetPop(child1Id).SetDispOrder(nPopsHaveOrder);
    }

    // set child 2 parent
    iter++;
    size_t child2Id = *iter;
    //wxLogVerbose("    Child2Id: %i", (int)child2Id);  // JMDBG
    parent.SetChild2Id(child2Id);
    if (dataStore.GetStructures().IsPop(child2Id))
    {
        nPopsFound++;
        dataStore.GetStructures().GetPop(child2Id).SetParentId(m_childParentId);
        nPopsHaveOrder++;
        dataStore.GetStructures().GetPop(child2Id).SetDispOrder(nPopsHaveOrder);
    }

    // some of children are parent levels, so recurse down the tree
    if (nPopsFound < 2)
    {
        //wxLogVerbose("Call ParentLevelUp");
        ParentLevelUp(dataStore, m_childParentId);
    }

    wxLogVerbose("****gcUpdatingChooseTwo::DoFinalForMulti parent: %s", parent.GetName().c_str());  // JMDBG
    wxLogVerbose("    child1: %s child2: %s parentId: %i level: %i", parent.GetChild1IdString().c_str(), parent.GetChild2IdString().c_str(), (int)parent.GetParentId(), parent.GetDispLevel());  // JMDBG

    int nunused = dataStore.GetStructures().GetUnusedPopCount() + dataStore.GetStructures().GetUnusedParentCount();
    //wxLogVerbose("*==*gcUpdatingChooseTwo::DoFinalForMulti nunused = %i", nunused);  // JMDBG
}

void
gcUpdatingChooseTwo::ParentLevelUp(GCDataStore & dataStore, size_t parentId)
{
    if (!dataStore.GetStructures().IsPop(dataStore.GetStructures().GetParent(parentId).GetChild1Id()))
    {
        size_t child1Id = dataStore.GetStructures().GetParent(parentId).GetChild1Id();
        dataStore.GetStructures().GetParent(child1Id).SetParentId(parentId);
        dataStore.GetStructures().GetParent(child1Id).SetDispLevel( dataStore.GetStructures().GetParent(child1Id).GetDispLevel() + 1);
        //wxLogVerbose("  ParentLevelUp Child 1 parent: %s new level: %i",  dataStore.GetStructures().GetParent(child1Id).GetName().c_str(), dataStore.GetStructures().GetParent(child1Id).GetDispLevel());  // JMDBG
        ParentLevelUp(dataStore, child1Id);
    }

    if (!dataStore.GetStructures().IsPop(dataStore.GetStructures().GetParent(parentId).GetChild2Id()))
    {
        size_t child2Id = dataStore.GetStructures().GetParent(parentId).GetChild2Id();
        dataStore.GetStructures().GetParent(child2Id).SetParentId(parentId);
        dataStore.GetStructures().GetParent(child2Id).SetDispLevel( dataStore.GetStructures().GetParent(child2Id).GetDispLevel() + 1);
        //wxLogVerbose("  ParentLevelUp Child 2 parent: %s new level: %i", dataStore.GetStructures().GetParent(child2Id).GetName().c_str(), dataStore.GetStructures().GetParent(child2Id).GetDispLevel());  // JMDBG
        ParentLevelUp(dataStore, child2Id);
    }
    return;
}

void
gcUpdatingChooseTwo::SetChildParentId(size_t parentId)
{
    m_childParentId = parentId;
}

size_t
gcUpdatingChooseTwo::GetChildParentId ()
{
    return m_childParentId;

}

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

gcParentChildChoice::gcParentChildChoice(size_t popId, size_t parId)
    :
    m_popId(popId),
    m_parId(parId)
{
}

gcParentChildChoice::~gcParentChildChoice()
{
}

wxString
gcParentChildChoice::GetLabel(GCDataStore & dataStore)
{
    gcPopulation & popRef = dataStore.GetStructures().GetPop(m_popId);
    return popRef.GetName();
}

bool
gcParentChildChoice::GetEnabled(GCDataStore & dataStore)
{
    return true;
}

void
gcParentChildChoice::SetEnabled(GCDataStore & dataStore, bool value)
{
}

bool
gcParentChildChoice::GetSelected(GCDataStore & dataStore)
{
    return true;
}

void
gcParentChildChoice::SetSelected(GCDataStore & dataStore, bool value)
{
}

void
gcParentChildChoice::UpdateDisplayInitial(GCDataStore & dataStore)
{
    assert(m_box != NULL);

    // if it parsed, it's a legal choice
    m_box->Enable(true);

    // display settings next to check box
    if (m_popId != gcdefault::badIndex)
    {
        const gcPopulation & popRef = dataStore.GetStructures().GetPop(m_popId);
        m_box->SetLabel(popRef.GetName());
    }

    if (m_parId != gcdefault::badIndex)
    {
        const gcParent & parRef = dataStore.GetStructures().GetParent(m_parId);
        m_box->SetLabel(parRef.GetName());
    }

    int nbox = dataStore.GetStructures().GetUnusedPopCount() + dataStore.GetStructures().GetUnusedParentCount();
    //int nbox = dataStore.GetStructures().GetPopCount();
    //wxLogVerbose("UpdateDisplayInitial Unused npop: %i",dataStore.GetStructures().GetUnusedPopCount());  // JMDBG
    //wxLogVerbose("UpdateDisplayInitial Unused npar: %i",dataStore.GetStructures().GetUnusedParentCount());  // JMDBG
    wxLogVerbose("UpdateDisplayInitial nbox: %i",nbox);  // JMDBG

    // we start with nothing checked
    if (nbox > 2)
    {
        m_box->SetValue(0);
    }
    else
    {
        m_box->SetValue(1);
    }
}

void
gcParentChildChoice::UpdateDisplayInterim(GCDataStore & dataStore)
// no changes needed since all pop merges are always legal
{
    wxLogVerbose("gcParentChildChoice::UpdateDisplayInterim called");  // JMDBG
}

void
gcParentChildChoice::UpdateDataInterim(GCDataStore & dataStore)
// all action happens at the enclosing set of choices
// and only at final update
{
    wxLogVerbose("gcParentChildChoice::UpdateDataInterim called");  // JMDBG
}

void
gcParentChildChoice::UpdateDataFinal(GCDataStore & dataStore)
// all action happens at the enclosing set of choices
{
    wxLogVerbose("gcParentChildChoice::UpdateDataFinal called");  // JMDBG
}

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

wxWindow *
gcParentChildChoice::FetchWindow()
{
    return m_box;
}

size_t
gcParentChildChoice::GetRelevantId()
{
    if (m_popId != gcdefault::badIndex)
    {
        return m_popId;
    }
    return m_parId;
}

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

gcParentChild::gcParentChild( wxWindow *                    parent,
                              size_t                          parentId,
                              std::vector<gcChoiceObject*>    choices)
    :
    gcUpdatingChooseTwo(parent, gcstr::divergeInstructions, choices)
{
    SetChildParentId(parentId);
}

gcParentChild::~gcParentChild()
{
}

wxString
gcParentChild::NoChoicesText() const
{
    return gcstr::noChoicePopulation;
}

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

gcParentRename::gcParentRename(size_t parentId)
    :
    m_parentId(parentId)
{
}

gcParentRename::~gcParentRename()
{
}

wxString
gcParentRename::FromDataStore(GCDataStore & dataStore)
{
    gcParent & parentRef = dataStore.GetStructures().GetParent(m_parentId);
    return parentRef.GetName();
}

void
gcParentRename::ToDataStore(GCDataStore & dataStore, wxString newText)
{
    newText.Replace(" ","_");
    gcParent & parentRef = dataStore.GetStructures().GetParent(m_parentId);
    wxLogVerbose("gcParentRename::ToDataStore: %i",(int)m_parentId);  // JMDBG
    wxLogVerbose("old name: %s \nnew name: %s", parentRef.GetName().c_str(), newText.c_str());  // JMDBG
    dataStore.GetStructures().Rename(parentRef,newText);
    wxLogVerbose("after rename: %s ", parentRef.GetName().c_str());  // JMDBG

}
//------------------------------------------------------------------------------------

//BEGIN_EVENT_TABLE(gcParentEditDialog, gcUpdatingDialog)
//EVT_BUTTON( wxID_APPLY,        gcParentEditDialog::OnApply )
//END_EVENT_TABLE()

gcParentEditDialog::gcParentEditDialog( wxWindow *      parent,
                                        GCDataStore &   dataStore,
                                        size_t          parentId,
                                        wxString        title,
                                        bool            forJustCreatedObj)
    :
    gcUpdatingDialog(   parent,
                        dataStore,
                        forJustCreatedObj
                        ?
                        title
                        :
                        gcstr::editParent,
                        forJustCreatedObj),
    m_parentId(parentId),
    m_dataStore(dataStore)
{
}

gcParentEditDialog::~gcParentEditDialog()
{
}

#if 0
void
gcParentEditDialog::OnApply(wxCommandEvent & event)
{
    int eventId = event.GetId();
    wxLogVerbose("*==*in gcParentEditDialog::OnApply event: %i", eventId);  // JMDBG

    assert(m_datastore != NULL);
    int nunused = m_dataStore.GetStructures().GetUnusedPopCount() + m_dataStore.GetStructures().GetUnusedParentCount();
    wxLogVerbose("*==*gcUpdatingChooseTwo::OnApply nunused = %i", nunused);  // JMDBG

    event.Skip();
}
#endif

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

bool DoDialogEditParent(wxWindow *      parent,
                        GCDataStore &   dataStore,
                        size_t          parentId,
                        bool            forJustCreatedObj)
{
    wxLogVerbose("DoDialogEditParent: %i",(int)parentId);  // JMDBG
    gcParentEditDialog dialog(parent,dataStore,parentId,gcstr::createParent2Child,forJustCreatedObj);

    // build the dialog
    gcDialogCreator creator;
    wxBoxSizer * contentSizer   = new wxBoxSizer(wxHORIZONTAL);
    //wxBoxSizer * leftSizer      = new wxBoxSizer(wxVERTICAL);

    // for renaming
    gcTextHelper * parentRenameHelp  = new gcParentRename(parentId);
    gcUpdatingComponent * rename    = new gcUpdatingTextCtrl(&dialog,
                                                             gcstr::parentRename,
                                                             parentRenameHelp);
    contentSizer->Add(rename,
                      1,
                      wxALL | wxALIGN_CENTER | wxEXPAND,
                      gclayout::borderSizeSmall);
    creator.AddComponent(dialog,rename);

    if (forJustCreatedObj)
    {
        // show potential children from populations
        std::vector<gcChoiceObject*> parChoices;
        gcDisplayOrder popids = dataStore.GetStructures().GetDisplayablePopIds();
        for(gcDisplayOrder::iterator iter=popids.begin(); iter != popids.end(); iter++)
        {
            size_t id = *iter;
            if (!dataStore.GetStructures().GetPop(id).HasParent())
            {
                wxLogVerbose("DoDialogEditParent add to list popid: %i",(int)id);  // JMDBG
                gcParentChildChoice * choice = new gcParentChildChoice(id, gcdefault::badIndex);
                parChoices.push_back(choice);
            }
        }

        gcDisplayOrder parids = dataStore.GetStructures().GetParentIds();
        for(gcDisplayOrder::iterator iter=parids.begin(); iter != parids.end(); iter++)
        {
            size_t id = *iter;
            if (id != parentId)
            {
                if (!dataStore.GetStructures().GetParent(id).HasParent())
                {
                    wxLogVerbose("DoDialogEditParent add to list parid: %i",(int)id);  // JMDBG
                    gcParentChildChoice * choice = new gcParentChildChoice(gcdefault::badIndex, id);
                    parChoices.push_back(choice);
                }
            }
        }


        // add potential children from parents
        gcUpdatingComponent * right = new gcParentChild( &dialog,parentId,parChoices);
        contentSizer->Add(new wxStaticLine(&dialog,-1,wxDefaultPosition,wxDefaultSize,wxLI_VERTICAL),
                          0,
                          wxALL | wxALIGN_CENTER | wxEXPAND,
                          gclayout::borderSizeSmall);
        contentSizer->Add(right,
                          1,
                          wxALL | wxALIGN_CENTER | wxEXPAND,
                          gclayout::borderSizeSmall);
        creator.AddComponent(dialog,right);
    }

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

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

bool
gcActor_ParentNew::OperateOn(wxWindow * parent, GCDataStore & dataStore)
{
    wxLogVerbose("gcActor_ParentNew::OperateOn");  // JMDBG
    bool state = true;
    int nunused = dataStore.GetStructures().GetUnusedPopCount() + dataStore.GetStructures().GetUnusedParentCount();

    if (dataStore.GetStructures().GetDivergenceState())
    {
        if (nunused < 2)
        {
            wxLogVerbose("gcActor_ParentNew::OperateOn Divergence State = true");  // JMDBG
            // delete all parents
            dataStore.GetStructures().SetDivergenceState(false);
            dataStore.GetStructures().RemoveParents();
            dataStore.GetStructures().ClearPopDisplayOrder();
            return state;
        }
        else
        {
            wxString parName = wxString::Format("Parent_%i",dataStore.GetStructures().GetParentCount()+1);
            gcParent nextParent = dataStore.GetStructures().MakeParent(parName);
            wxLogVerbose("repeat gcActor_ParentNew::OperateOn nunused: %i Calling DoDialogEditParent parent: %s ParId: %i window: %i", nunused, parName.c_str(), (int)nextParent.GetId(), parent->GetId());  // JMDBG

            return DoDialogEditParent(parent,dataStore,nextParent.GetId(),true);
        }
    }
    else
    {
        wxLogVerbose("gcActor_ParentNew::OperateOn Divergence State = false");  // JMDBG
        dataStore.GetStructures().SetDivergenceState(true);
        // make parent
        wxString parName = wxString::Format("Parent_%i",dataStore.GetStructures().GetParentCount()+1);
        gcParent nextParent = dataStore.GetStructures().MakeParent(parName);
        wxLogVerbose("first gcActor_ParentNew::OperateOn nunused: %i Calling DoDialogEditParent parent: %s ParId: %i window: %i", nunused, parName.c_str(), (int)nextParent.GetId(), parent->GetId());
        return DoDialogEditParent(parent,dataStore,nextParent.GetId(),true);

    }
    return state;
}

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

bool
gcActor_ParentEdit::OperateOn(wxWindow * parent, GCDataStore & dataStore)
{
    wxLogVerbose("gcActor_ParentEdit::OperateOn m_parentId: %i",(int)m_parentId);  // JMDBG
    return DoDialogEditParent(parent,dataStore,m_parentId,false);
}

//____________________________________________________________________________________
