// $Id: gc_frame.cpp,v 1.110 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 <iostream>
#include <stdio.h>

#include "errhandling.h"
#include "gc_assigntab.h"
#include "gc_data.h"
//#include "gc_divtab.h"
#include "gc_errhandling.h"
#include "gc_event_ids.h"
#include "gc_event_publisher.h"
#include "gc_file_list.h"
#include "gc_frame.h"
#include "gc_layout.h"
#include "gc_file_dialogs.h"
#include "gc_loci_match.h"
#include "gc_locus_dialogs.h"
#include "gc_logic.h"
#include "gc_menu_actors.h"
#include "gc_migtab.h"
#include "gc_pop_match.h"
#include "gc_population_dialogs.h"
#include "gc_region_dialogs.h"
#include "gc_strings.h"

#include "wx/artprov.h"
#include "wx/imaglist.h"
#include "wx/log.h"
#include "wx/sizer.h"
#include "wx/splitter.h"
#include "wx/textctrl.h"

//using namespace std;

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

// events the bottom level construct should respond to
BEGIN_EVENT_TABLE(GCFrame,wxFrame)
EVT_MENU        (wxID_ANY,              GCFrame::DispatchMenuEvent)
EVT_D2S         (wxID_ANY,              GCFrame::DispatchDataEvent)
EVT_S2D         (wxID_ANY,              GCFrame::DispatchScreenEvent)
EVT_NOTEBOOK_PAGE_CHANGED(wxID_ANY,     GCFrame::OnNotebookCtrl)
EVT_NOTEBOOK_PAGE_CHANGING(wxID_ANY,    GCFrame::OnNotebookCtrl)
END_EVENT_TABLE()

void
GCFrame::EnableMenus()
{
    ///////////////////
    m_fileMenu->Enable( gcEvent_File_Export, true);     // EWFIX.P3 -- disable when not exportable
    m_fileMenu->Enable( gcEvent_Batch_Export, true);    // EWFIX.P3 -- disable when not exportable

    m_verbose->Check(wxLog::GetVerbose());
}

void
GCFrame::SetUpMenus()
{
    // Setting up menu items within File Menu
    m_fileMenu = new wxMenu;
    m_fileMenu->Append( gcEvent_File_Add,       "Read &Data File\tCTRL-O");
    m_fileMenu->Append( gcEvent_CmdFile_Read,   "Read &Command File\tCTRL+SHIFT-O");
    m_fileMenu->AppendSeparator();              //////////////////////////
    m_fileMenu->Append( gcEvent_File_Export,    "Write &Lamarc File\tCTRL-L");
    m_fileMenu->Append( gcEvent_Batch_Export,   "Write &Batch Command File (alpha test -- use with caution)\tCTRL-B");
    m_fileMenu->AppendSeparator();              //////////////////////////
    m_fileMenu->Append( wxID_EXIT,              "&Quit" );

    m_insertMenu    = new wxMenu;
    m_insertMenu->Append( gcEvent_LinkG_Add,    "New &Region");
    m_insertMenu->Append( gcEvent_Locus_Add,    "New &Segment");
    m_insertMenu->Append( gcEvent_Pop_Add,      "New &Population");

    m_viewMenu      = new wxMenu;
    m_verbose = m_viewMenu->AppendCheckItem(    gcEvent_ToggleVerbose, "&Log Verbosely");
    m_verbose->Check(wxLog::GetVerbose());

    // Setting up menu items within Help menu
    wxMenu * GCHelpMenu = new wxMenu;
    GCHelpMenu->Append( wxID_ABOUT,         "&About..." );

    // Setting up menu items within Debug menu
    wxMenu * GCDebugMenu = new wxMenu;
    GCDebugMenu->Append(gcEvent_Debug_Dump, "&Dump" );

    // Placing top-level items on Menu Bar
    wxMenuBar *GCMenuBar = new wxMenuBar;
    GCMenuBar->Append( m_fileMenu,      "&File" );
    GCMenuBar->Append( m_insertMenu,    "&Insert" );
    GCMenuBar->Append( m_viewMenu,      "&View" );
    GCMenuBar->Append( GCHelpMenu,      "&Help" );
#ifndef NDEBUG
    GCMenuBar->Append( GCDebugMenu,     "&Debug" );
#endif

    EnableMenus();

    // Installing Menu Bar
    SetMenuBar( GCMenuBar );
}

GCFrame::GCFrame(   const wxString& title, GCLogic & logic)
    :   wxFrame(    NULL,
                    -1,
                    title,
                    wxDefaultPosition,
                    wxDefaultSize,
                    wxTAB_TRAVERSAL | wxDEFAULT_FRAME_STYLE
        ),
        m_logic(logic),
        m_logPanel(NULL),
        m_logText(NULL),
        m_filePanel(NULL),
        m_gridPanel(NULL),
        m_migPanel(NULL),
        m_fileMenu(NULL),
        m_insertMenu(NULL),
        m_viewMenu(NULL)
{
    m_sizerFrame = new wxBoxSizer(wxVERTICAL);

    m_basePanel = new wxPanel(this);

    m_bookCtrl = new wxNotebook(m_basePanel, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxBK_TOP);

    // UGH! The log text must have the wxNotebook as it's parent BUT, you must
    // create this wxTextCtrl object and set up the logging BEFORE you add anything
    // to the wxNotebook !!!
    m_logText   = new wxTextCtrl(m_bookCtrl,wxID_ANY,wxEmptyString,
                                 wxDefaultPosition,wxDefaultSize,
                                 wxTE_MULTILINE | wxTE_READONLY);
    m_logText->SetEditable(false);
    m_oldLog = wxLog::SetActiveTarget(new wxLogTextCtrl(m_logText));

    // the order of these AddPage calls is the order of the tabs
    m_gridPanel = new GCAssignmentTab(m_bookCtrl,m_logic);

    m_bookCtrl->AddPage( m_gridPanel, DATA_PARTITIONS, true );
    //wxLogMessage("create panel tab");  // JMDBG


    m_migPanel = new gcMigTab(m_bookCtrl,m_logic);
    m_bookCtrl->AddPage( m_migPanel, MIGRATION_MATRIX, false );
    //cout << "create migration tab" << endl;

    m_filePanel = new GCFileList(m_bookCtrl,m_logic);
    m_bookCtrl->AddPage( m_filePanel, DATA_FILES, false );
    //cout << "create file list tab" << endl;

    // log at bottom of every tab panel - better for debugging
    //m_sizerFrame->Add(m_logText, 0, wxEXPAND );

    // log in its own tab - safer for users
    m_bookCtrl->AddPage( m_logText, LOG_TEXT, false );
    //cout << "create log tab" << endl;

    m_sizerFrame->Insert(0, m_bookCtrl, wxSizerFlags(5).Expand().Border());
    m_sizerFrame->Show(m_bookCtrl);
    m_sizerFrame->Layout();
    m_sizerFrame->SetSizeHints(this);

    m_basePanel->SetSizer(m_sizerFrame);
    m_sizerFrame->Fit(this);
    Centre(wxBOTH);

    SetUpMenus();
    UpdateUserCues();
}

GCFrame::~GCFrame()
{
    delete wxLog::SetActiveTarget(m_oldLog);
}

void
GCFrame::UpdateUserCues()
{
    m_filePanel->UpdateUserCues();
    m_gridPanel->UpdateUserCues();
    m_migPanel->UpdateUserCues();

    EnableMenus();
    Layout();
}

void
GCFrame::DispatchMenuEvent( wxCommandEvent& event)
// wxWidgets can be a little tempermental about performing
// time-consuming actions while in the middle of dispatching
// a menu or control event. To overcome this, issuing a menu
// command dispatches a home-grown event. We will get these
// events after the menu display portion is done.
{
    gcEventActor * actor = NULL;
    switch(event.GetId())
        // stuff handled in the non-default changes the frame
        // (this object) but not the datastore
    {
        case wxID_EXIT:
            Close(TRUE);
            break;
        default:
            actor = MakeMenuActor(event.GetId());
            PublishScreenEvent(GetEventHandler(),actor);
    }
    UpdateUserCues();
}

void
GCFrame::DispatchDataEvent( wxCommandEvent& event)
{
    switch(event.GetId())
    {
        case D2S_UserInteractionPhaseEnd:
            // EWFIX.P3 UNDO -- insert undo/redo phase here, not inside UpdateUserCues
            UpdateUserCues();
            break;
        default:
            m_logic.GCWarning("unimplemented data event");
            assert(false);
            break;
    }
}

void
GCFrame::DispatchScreenEvent(wxCommandEvent& event)
{
    try
    {
        wxClientData * cd = event.GetClientObject();
        GCClientData * gc = dynamic_cast<GCClientData*>(cd);
        assert(gc != NULL);

        GCLogic logicCopy(m_logic);
        gcEventActor * actor = gc->GetActor();

        bool takeResult = false;
        try
        {
            takeResult = actor->OperateOn(this,m_logic);
        }
        catch(const gc_data_error& e)
        {
            // EWFIX.P4 -- add wrapper
            m_logic.GCWarning(e.what());
        }
        catch(const incorrect_xml& g)
        {
            // EWFIX.P4 -- add wrapper
            m_logic.GCWarning(g.what());
        }
        catch(const gc_implementation_error& f)
        {
            // EWFIX.P4 -- add wrapper
            m_logic.GCWarning(f.what());
        }

        if(!takeResult)
        {
            m_logic = logicCopy;
        }

        delete gc;  // EWFIX.P4 -- hate that we have to do this
    }
    catch (const gc_data_error& e)
    {
        m_logic.GCFatal(wxString("uncaught data error: ")+e.what());
    }
    catch (const gc_implementation_error& e)
    {
        m_logic.GCFatalUnlessDebug(wxString("implementation error: ")+e.what());
    }
    catch (const gc_ex& e)
    {
        m_logic.GCFatal(wxString("gc exception: ")+e.what());
    }
    catch (const std::exception& e)
    {
        m_logic.GCFatal(wxString("unexpected exception: ")+e.what());
    }
    // each of the above method calls might result in the publishing
    // of one or more DATA_2_SCREEN events. We now publish this event
    // to indicate that we've finished publishing all the events
    // for a single user action
    PublishDataEvent(GetEventHandler(),D2S_UserInteractionPhaseEnd);
}

void
GCFrame::OnNotebookCtrl(wxNotebookEvent& event)
{
    static const struct EventInfo
    {
        wxEventType typeChanged, typeChanging;
        const wxChar *name;
    } events[] =
          {
              {
                  wxEVT_COMMAND_NOTEBOOK_PAGE_CHANGED,
                  wxEVT_COMMAND_NOTEBOOK_PAGE_CHANGING,
                  _T("wxNotebook")
              },
          };

    wxString nameEvent, nameControl, veto;
    const wxEventType eventType = event.GetEventType();
    for ( size_t n = 0; n < WXSIZEOF(events); n++ )
    {
        const EventInfo& ei = events[n];
        if ( eventType == ei.typeChanged )
        {
            nameEvent = wxT("Changed");
        }
        else if ( eventType == ei.typeChanging )
        {
            const int idx = event.GetOldSelection();

            nameEvent = wxT("Changing");
        }
        else // skip end of the loop
        {
            continue;
        }

        nameControl = ei.name;
        break;
    }

    static int s_num = 0;

#if 0
    wxLogVerbose(wxT("Event #%d: %s: %s (%d) new sel %d, old %d%s"),
                 ++s_num,
                 nameControl.c_str(),
                 nameEvent.c_str(),
                 eventType,
                 event.GetSelection(),
                 event.GetOldSelection(),
                 veto.c_str());
#endif

    m_logText->SetInsertionPointEnd();

}
//____________________________________________________________________________________
