// $Id: gc_dialog.cpp,v 1.31 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_data.h"
#include "gc_datastore.h"
#include "gc_dialog.h"
#include "gc_errhandling.h"
#include "gc_event_ids.h"
#include "gc_event_publisher.h"
#include "gc_layout.h"
#include "gc_locus_err.h"
#include "gc_strings.h"

#include "wx/button.h"
#include "wx/checkbox.h"
#include "wx/choice.h"
#include "wx/log.h"
#include "wx/radiobox.h"
#include "wx/sizer.h"
#include "wx/statbox.h"
#include "wx/statline.h"
#include "wx/stattext.h"
#include "wx/textctrl.h"

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

gcUpdatingComponent::gcUpdatingComponent(   wxWindow *  parent,
                                            wxString    instructions)
    :
    wxPanel(parent,-1)
{
    m_statBoxSizer = new wxStaticBoxSizer(wxVERTICAL,this,instructions);
    SetSizer(m_statBoxSizer);
}

gcUpdatingComponent::~gcUpdatingComponent()
{
}

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

gcPlainTextHelper::gcPlainTextHelper()
{
}

gcPlainTextHelper::~gcPlainTextHelper()
{
}

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

gcUpdatingPlainText::gcUpdatingPlainText(   wxWindow *          parent,
                                            wxString            instructions,
                                            gcPlainTextHelper * helper)
    :
    gcUpdatingComponent(parent,instructions),
    m_textHelper(helper)
{
    m_statText = new wxStaticText(this,-1,"");
    m_statBoxSizer->Add(m_statText,
                        0,
                        wxALL | wxALIGN_LEFT | wxALIGN_TOP | wxEXPAND ,
                        gclayout::borderSizeSmall);
}

gcUpdatingPlainText::~gcUpdatingPlainText()
{
    delete m_textHelper;
}

void
gcUpdatingPlainText::BuildDisplay(GCDataStore & dataStore)
{
}

void
gcUpdatingPlainText::UpdateDisplay(GCDataStore & dataStore)
{
    m_statText->SetLabel(m_textHelper->FromDataStore(dataStore));
}

void
gcUpdatingPlainText::UpdateDataInterim(GCDataStore & dataStore)
// no updates to dataStore -- this is for display only
{
}

void
gcUpdatingPlainText::UpdateDataFinal(GCDataStore & dataStore)
// no updates to dataStore -- this is for display only
{
}

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

gcTextHelper::gcTextHelper()
{
}

gcTextHelper::~gcTextHelper()
{
}

const wxValidator &
gcTextHelper::GetValidator()
{
    return wxDefaultValidator;
}

wxString
gcTextHelper::InitialString()
{
    return wxEmptyString;
}

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

BEGIN_EVENT_TABLE(gcTextCtrlWithInstructions,wxTextCtrl)
EVT_MOUSE_EVENTS(gcTextCtrlWithInstructions::OnMouse)
END_EVENT_TABLE()

gcTextCtrlWithInstructions::gcTextCtrlWithInstructions( wxWindow *          parent,
                                                        wxString            instructions,
                                                        const wxValidator & validator)
:
wxTextCtrl(parent,-1,instructions,wxDefaultPosition,wxDefaultSize,
           gcdata::noStyle,validator),
    m_instructions(instructions)
{
}

gcTextCtrlWithInstructions::~gcTextCtrlWithInstructions()
{
}

void
gcTextCtrlWithInstructions::OnMouse(wxMouseEvent & event)
{
    // according to the wxWidgets documentation, calling event.Skip()
    // here might be necessary because it might be handling more basic
    // fuctionality (such as bringing the window to the front.
    event.Skip();

    if( !event.Moving())
    {
        if( event.LeftDown() )
        {
            if( GetValue() == m_instructions)
            {
                ChangeValue(wxEmptyString);
            }
        }
    }

}

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

gcUpdatingTextCtrl::gcUpdatingTextCtrl( wxWindow *          parent,
                                        wxString            instructions,
                                        gcTextHelper *      helper)
    :
    gcUpdatingComponent(parent,instructions),
    m_textCtrl(NULL),
    m_textHelper(helper)
{
    m_textCtrl = new gcTextCtrlWithInstructions(this,
                                                m_textHelper->InitialString(),
                                                m_textHelper->GetValidator());

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

gcUpdatingTextCtrl::~gcUpdatingTextCtrl()
{
    delete m_textHelper;
}

void
gcUpdatingTextCtrl::BuildDisplay(GCDataStore & dataStore)
{
}

void
gcUpdatingTextCtrl::UpdateDisplay(GCDataStore & dataStore)
{
    m_textCtrl->SetValue(m_textHelper->FromDataStore(dataStore));
}

void
gcUpdatingTextCtrl::UpdateDataInterim(GCDataStore & dataStore)
{
    m_textHelper->ToDataStore(dataStore,m_textCtrl->GetValue());
}

void
gcUpdatingTextCtrl::UpdateDataFinal(GCDataStore & dataStore)
{
    UpdateDataInterim(dataStore);
}

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

gcChoiceObject::gcChoiceObject()
{
}

gcChoiceObject::~gcChoiceObject()
{
}

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

BEGIN_EVENT_TABLE(gcUpdatingChoose, gcUpdatingComponent)
EVT_CHECKBOX( wxID_ANY,        gcUpdatingChoose::OnCheck )
END_EVENT_TABLE()

gcUpdatingChoose::gcUpdatingChoose( wxWindow *                      parent,
                                    wxString                        instructions,
                                    std::vector<gcChoiceObject*>    choices)
:
gcUpdatingComponent(parent,instructions),
    m_choices(choices)
{
}

gcUpdatingChoose::~gcUpdatingChoose()
{
    for(std::vector<gcChoiceObject*>::iterator i = m_choices.begin(); i != m_choices.end(); i++)
    {
        delete *i;
    }
}

void
gcUpdatingChoose::BuildDisplay(GCDataStore & dataStore)
{
    // add new check boxes
    for(size_t index = 0; index < m_choices.size(); index++)
    {
        gcChoiceObject * choice = m_choices[index];
        //wxLogVerbose("****in gcUpdatingDialog::BuildDisplay event: %i", eventId);  // JMDBG

        m_statBoxSizer->Add(choice->MakeWindow(this),
                            0,
                            wxALL | wxALIGN_LEFT | wxALIGN_TOP | wxEXPAND ,
                            gclayout::borderSizeSmall);

        choice->UpdateDisplayInitial(dataStore);
    }
}

void
gcUpdatingChoose::UpdateDisplay(GCDataStore & dataStore)
{
    for(size_t index=0; index < m_choices.size(); index++)
    {
        gcChoiceObject * choice = m_choices[index];
        choice->UpdateDisplayInterim(dataStore);
    }
}

void
gcUpdatingChoose::UpdateDataInterim(GCDataStore & dataStore)
{
    for(size_t index=0; index < m_choices.size(); index++)
    {
        gcChoiceObject * choice = m_choices[index];
        choice->UpdateDataInterim(dataStore);
    }
}

void
gcUpdatingChoose::UpdateDataFinal(GCDataStore & dataStore)
{
    for(size_t index=0; index < m_choices.size(); index++)
    {
        gcChoiceObject * choice = m_choices[index];
        choice->UpdateDataFinal(dataStore);
    }
}

void
gcUpdatingChoose::OnCheck(wxCommandEvent & event)
{
    if(event.IsChecked())
    {
        wxObject * obj = event.GetEventObject();
        ProcessPositiveCheck(obj);
    }
}

void
gcUpdatingChoose::ProcessPositiveCheck(wxObject * obj)
// enforces one check per set of choices
{
    for(size_t index=0; index < m_choices.size(); index++)
    {
        wxWindow * choice = m_choices[index]->FetchWindow();
        if(choice != obj)
        {
            wxCheckBox * cb = dynamic_cast<wxCheckBox*>(choice);
            assert(cb != NULL);
            cb->SetValue(0);
        }
    }
}

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

BEGIN_EVENT_TABLE(gcUpdatingChooseMulti, gcUpdatingChoose)
EVT_BUTTON( GC_SelectAll,    gcUpdatingChooseMulti::OnSelectAll )
EVT_BUTTON( GC_UnselectAll,  gcUpdatingChooseMulti::OnUnselectAll )
END_EVENT_TABLE()

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

gcUpdatingChooseMulti::~gcUpdatingChooseMulti()
{
}

wxString
gcUpdatingChooseMulti::NoChoicesText() const
{
    return gcstr::noChoice;
}

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

    if (m_choices.empty())
    {
        m_statBoxSizer->AddStretchSpacer(1);
        m_statBoxSizer->Add(new wxStaticText(this,-1,NoChoicesText()),
                            1,
                            wxALL | wxALIGN_CENTER | wxEXPAND ,
                            gclayout::borderSizeSmall);
        m_statBoxSizer->AddStretchSpacer(1);
    }
    else
    {
        wxBoxSizer * buttonSizer = new wxBoxSizer(wxHORIZONTAL);
        buttonSizer->AddStretchSpacer(1);
        buttonSizer->Add(new wxButton(this,GC_SelectAll,gcstr::buttonSelectAll),
                         0,
                         wxALL | wxALIGN_CENTER ,
                         gclayout::borderSizeSmall);
        buttonSizer->Add(new wxButton(this,GC_UnselectAll,gcstr::buttonUnselectAll),
                         0,
                         wxALL | wxALIGN_CENTER ,
                         gclayout::borderSizeSmall);

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

void
gcUpdatingChooseMulti::ProcessPositiveCheck(wxObject * obj)
// don't do anything
{
}

void
gcUpdatingChooseMulti::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());
        }
    }
    DoFinalForMulti(dataStore,checkedIds);
}

void
gcUpdatingChooseMulti::OnSelectAll(wxCommandEvent & event)
{
    for(size_t index=0; index < m_choices.size(); index++)
        // EWFIX.P3 -- should refactor to eliminate dynamic cast
    {
        gcChoiceObject * choice = m_choices[index];
        wxWindow * window = choice->FetchWindow();
        wxCheckBox * cb = dynamic_cast<wxCheckBox*>(window);
        if(cb->IsEnabled())
        {
            cb->SetValue(1);
        }
    }
}

void
gcUpdatingChooseMulti::OnUnselectAll(wxCommandEvent & event)
{
    for(size_t index=0; index < m_choices.size(); index++)
        // EWFIX.P3 -- should refactor to eliminate dynamic cast
    {
        gcChoiceObject * choice = m_choices[index];
        wxWindow * window = choice->FetchWindow();
        wxCheckBox * cb = dynamic_cast<wxCheckBox*>(window);
        cb->SetValue(0);
    }
}

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

#if 0

gcSelectObject::gcSelectObject()
{
}

gcSelectObject::~gcSelectObject()
{
}

gcUpdatingSelect::gcUpdatingSelect(
    wxWindow *                      parent,
    wxString                        instructions,
    std::vector<gcSelectObject*>    choices)
    :
    gcUpdatingComponent(parent,instructions)
{
}

gcUpdatingSelect::~gcUpdatingSelect()
{
}

void
gcUpdatingSelect::BuildDisplay(GCDataStore& ds)
{
}

void
gcUpdatingSelect::UpdateDisplay(GCDataStore& ds)
{
}

void
gcUpdatingSelect::UpdateDataInterim(GCDataStore& ds)
{
}

void
gcUpdatingSelect::UpdateDataFinal(GCDataStore& ds)
{
}

#endif

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

gcDialogCreator::gcDialogCreator()
{
}

gcDialogCreator::~gcDialogCreator()
{
}

void
gcDialogCreator::AddComponent(gcUpdatingDialog & dialog, gcUpdatingComponent * component)
{
    assert(component != NULL);
    dialog.m_components.push_back(component);
}

void
gcDialogCreator::PlaceContent(gcUpdatingDialog & dialog, wxSizer * sizerWithContent)
{
    assert(sizerWithContent != NULL);
    dialog.m_sizer->Insert(0,
                           sizerWithContent,
                           0,
                           wxALL | wxALIGN_CENTER | wxEXPAND,
                           gclayout::borderSizeSmall);
}

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

BEGIN_EVENT_TABLE(gcUpdatingDialog, wxDialog)
EVT_BUTTON( wxID_ANY,        gcUpdatingDialog::OnButton )
EVT_S2D   ( wxID_ANY,        gcUpdatingDialog::ScreenEvent)
END_EVENT_TABLE()

gcUpdatingDialog::gcUpdatingDialog( wxWindow *      parent,
                                    GCDataStore &   dataStore,
                                    wxString        title,
                                    bool            forJustCreatedObj)
:
wxDialog(parent,
         -1,
         title,
         wxDefaultPosition,wxDefaultSize,
         wxCAPTION | wxCLOSE_BOX | wxRESIZE_BORDER),
    m_dataStore(dataStore),
    m_sizer(NULL)
{

    m_sizer = new wxBoxSizer(wxVERTICAL);
    m_sizer->AddStretchSpacer(1);
    m_sizer->Add(new wxStaticLine(this,-1,wxDefaultPosition,wxDefaultSize,wxLI_HORIZONTAL),
                 0,
                 wxALL | wxALIGN_CENTER | wxEXPAND,
                 gclayout::borderSizeSmall);
    m_sizer->AddStretchSpacer(1);
    m_sizer->Add(new gcEditButtons(this,forJustCreatedObj),0,wxEXPAND);
    SetSizer(m_sizer);
}

gcUpdatingDialog::~gcUpdatingDialog()
{
    for(size_t i=0; i < m_components.size(); i++)
    {
        delete (m_components[i]);
    }
}

void
gcUpdatingDialog::DoUpdateData()
{
    for(size_t i=0; i < m_components.size(); i++)
    {
        (m_components[i])->UpdateDataFinal(m_dataStore);
    }
}

void
gcUpdatingDialog::DoBuildDisplay()
{
    for(size_t i=0; i < m_components.size(); i++)
    {
        (m_components[i])->BuildDisplay(m_dataStore);
        (m_components[i])->UpdateDisplay(m_dataStore);
    }
}

void
gcUpdatingDialog::OnButton(wxCommandEvent & event)
{
    int eventId = event.GetId();
    wxLogVerbose("****in gcUpdatingDialog::OnButton event: %i", eventId);  // JMDBG
    switch(eventId)
    {
        case wxID_APPLY:
            try
            {
                DoUpdateData();
                EndModal(eventId);
            }
            catch(const gc_data_error& e)
            {
                m_dataStore.GCError(e.what());
                EndModal(wxID_CANCEL);
            }
            return;
            break;
        case wxID_CANCEL:
            EndModal(eventId);
            return;
            break;
        case wxID_DELETE:
            DoDelete();
            EndModal(eventId);
            return;
            break;
        default:
            assert(false);
    }
}

void
gcUpdatingDialog::ScreenEvent(wxCommandEvent & event)
{
    wxLogDebug("ScreenEvent");
}

void
gcUpdatingDialog::DoDelete()
{
    gc_implementation_error e(gcerr::provideDoDelete.c_str());
    throw e;
}

bool
gcUpdatingDialog::Go()
{
    DoBuildDisplay();
    Layout();
    Fit();
    CentreOnParent();
    int retval = ShowModal();
    switch(retval)
    {
        case wxID_APPLY:
            // we should take all changes
            return true;
            break;
        case wxID_CANCEL:
            // we should not take any changes
            return false;
            break;
        case wxID_DELETE:
            // we should take all changes
            return true;
            break;
        default:
            m_dataStore.GCWarning(wxString::Format("saw unexpected dialog return code %d. Not taking your changes.",retval));   // EWFIX.P2
            return false;
            break;
    }

    assert(false);
    return false;

}

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

gcEditButtons::gcEditButtons(wxWindow * parent,bool forJustCreatedObj)
    :
    wxPanel(parent)
{
    wxBoxSizer * sizer = new wxBoxSizer(wxHORIZONTAL);
    if(!forJustCreatedObj)
    {
        sizer->Add(new wxButton(this,wxID_DELETE,gcstr::editDelete),
                   0,
                   wxALL | wxALIGN_CENTER ,
                   gclayout::borderSizeSmall);
    }
    sizer->AddStretchSpacer(1);
    sizer->Add(new wxButton(this,wxID_CANCEL,gcstr::editCancel),
               0,
               wxALL | wxALIGN_CENTER ,
               gclayout::borderSizeSmall);
    sizer->Add(new wxButton(this,wxID_APPLY,gcstr::editApply),
               0,
               wxALL | wxALIGN_CENTER ,
               gclayout::borderSizeSmall);
    SetSizer(sizer);
}

gcEditButtons::~gcEditButtons()
{
}

//____________________________________________________________________________________
