// $Id: gc_file_dialogs.cpp,v 1.50 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 "errhandling.h"
#include "gc_data.h"
#include "gc_datastore.h"
#include "gc_errhandling.h"
#include "gc_event_ids.h"
#include "gc_file.h"
#include "gc_file_dialogs.h"
#include "gc_layout.h"
#include "gc_loci_match.h"
#include "gc_phase_err.h"
#include "gc_pop_match.h"
#include "gc_strings.h"
#include "gc_strings_cmdfile.h"
#include "gc_strings_phase.h"
#include "gc_text_ctrl.h"

#include "giraffe32.xpm"
#include "tinyxml.h"

#include "wx/checkbox.h"
#include "wx/filedlg.h"
#include "wx/icon.h"
#include "wx/log.h"
#include "wx/sizer.h"
#include "wx/statline.h"

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

gcFullPath::gcFullPath(size_t fileId)
    :
    m_fileId(fileId)
{
}

gcFullPath::~gcFullPath()
{
}

wxString
gcFullPath::FromDataStore(GCDataStore & dataStore)
{
    return dataStore.GetDataFile(m_fileId).GetName();
}

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

gcParseInfoNone::gcParseInfoNone()
{
}

gcParseInfoNone::~gcParseInfoNone()
{
}

wxString
gcParseInfoNone::FromDataStore(GCDataStore &)
{
    return gcstr::parseInfoNone;
}

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

gcParseInfoOne::gcParseInfoOne(size_t fileId)
    :
    m_fileId(fileId)
{
}

gcParseInfoOne::~gcParseInfoOne()
{
}

wxString
gcParseInfoOne::FromDataStore(GCDataStore & dataStore)
{
    const GCFile & fileRef = dataStore.GetDataFile(m_fileId);
    const GCParse & parse = dataStore.GetParse(fileRef);
    wxString parseInfo = parse.GetSettings();
    return parseInfo;
}

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

gcParseChoice::gcParseChoice(size_t parseId)
    :
    m_parseId(parseId),
    m_box(NULL)
{
}

gcParseChoice::~gcParseChoice()
{
}

#if 0

wxString
gcParseChoice::GetLabel(GCDataStore & dataStore)
{
    const GCParse & parse = dataStore.GetParse(m_parseId);
    wxString parseInfo = parse.GetSettings();
    return parseInfo;
}

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

bool
gcParseChoice::GetSelected(GCDataStore & dataStore)
{
    const GCParse & thisParse = dataStore.GetParse(m_parseId);
    const GCFile & fileRef = thisParse.GetFileRef();

    if(dataStore.GetStructures().HasParse(fileRef))
    {
        const GCParse & assignedParse = dataStore.GetStructures().GetParse(fileRef);
        return (assignedParse.GetId() == m_parseId);
    }
}

void
gcParseChoice::SetSelected(GCDataStore & dataStore, bool selected)
{
}

#endif

void
gcParseChoice::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
    const GCParse & parse = dataStore.GetParse(m_parseId);
    wxString parseInfo = parse.GetSettings();
    m_box->SetLabel(parseInfo);

    UpdateDisplayInterim(dataStore);
}

void
gcParseChoice::UpdateDisplayInterim(GCDataStore & dataStore)
{
    assert(m_box != NULL);

    const GCParse & thisParse = dataStore.GetParse(m_parseId);
    const GCFile & fileRef = thisParse.GetFileRef();

    if(dataStore.GetStructures().HasParse(fileRef))
    {
        const GCParse & assignedParse = dataStore.GetStructures().GetParse(fileRef);
        if (assignedParse.GetId() == m_parseId)
        {
            m_box->SetValue(1);
            return;
        }
    }

    m_box->SetValue(0);
}

void
gcParseChoice::UpdateDataInterim(GCDataStore & dataStore)
{
    int value = m_box->GetValue();
    if(value > 0)
    {
        const GCParse & thisParse = dataStore.GetParse(m_parseId);
        dataStore.GetStructures().SetParse(thisParse);
    }
}

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

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

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

size_t
gcParseChoice::GetRelevantId()
{
    return m_parseId;
}

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

gcFileEditDialog::gcFileEditDialog( wxWindow *      parent,
                                    GCDataStore &   dataStore,
                                    size_t          fileId)
    :
    gcUpdatingDialog(   parent,
                        dataStore,
                        wxString::Format(gcstr::editFileSettings,
                                         dataStore.GetDataFile(fileId).GetShortName().c_str()),
                        false),
    // false value is because there is no such
    // thing as a newborn, empty file
    m_fileId(fileId)
{
}

gcFileEditDialog::~gcFileEditDialog()
{
}

void
gcFileEditDialog::DoDelete()
{
    m_dataStore.RemoveDataFile(m_dataStore.GetDataFile(m_fileId));
}

bool
DoDialogAddFiles(wxWindow * parentWindow, GCDataStore & dataStore)
{
    wxFileDialog dataFileDialog(parentWindow,
                                gcstr::dataFilesSelect,
                                wxEmptyString,      // default directory == current
                                wxEmptyString,      // default file = none
                                gcstr::dataFiles,   // show .phy and .mig files
                                wxFD_OPEN | wxFD_CHANGE_DIR | wxFD_FILE_MUST_EXIST | wxFD_MULTIPLE);

    dataFileDialog.SetIcon(wxICON(giraffe32));
    bool producedResultWeShouldKeep = false;

    std::vector<GCFile*> addedFiles;

    if(dataFileDialog.ShowModal() == wxID_OK)
    {
        wxArrayString fullPathFileNames;
        dataFileDialog.GetPaths(fullPathFileNames);
        fullPathFileNames.Sort();
        for(size_t fileIndex=0; fileIndex < fullPathFileNames.Count(); fileIndex++)
        {
            try
            {
                GCFile & fileRef = dataStore.AddDataFile(fullPathFileNames[fileIndex]);
                producedResultWeShouldKeep = true;
                addedFiles.push_back(&fileRef);
            }
            catch (const gc_ex& e)
            {
                dataStore.GCError(e.what());
            }
        }
    }

#if 0
    std::vector<GCFile*> suspiciousFiles;
    for(size_t i = 0; i < addedFiles.size(); i++)
    {
        GCFile & fileRef = *(addedFiles[i]);
        bool hasShortName = false;
        for(size_t j = 0; j < fileRef.GetParseCount(); j++)
        {
            const GCParse & parseRef = fileRef.GetParse(j);
            if(parseRef.GetHasSpacesInNames())
            {
                hasShortName = true;
            }
        }
        if(hasShortName)
        {
            suspiciousFiles.push_back(&fileRef);
        }
    }

    if(!suspiciousFiles.empty())
    {
        for(size_t i=0; i < suspiciousFiles.size(); i++)
        {
            wxLogDebug("short name file %s",suspiciousFiles[i]->GetName().c_str());
        }
    }
#endif

    return producedResultWeShouldKeep;
}

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

gcHapDefaultChoice::gcHapDefaultChoice(size_t fileId)
    :
    m_fileId(fileId)
{
}

gcHapDefaultChoice::~gcHapDefaultChoice()
{
}

void
gcHapDefaultChoice::UpdateDisplayInitial(GCDataStore & dataStore)
{
    UpdateDisplayInterim(dataStore);
}

void
gcHapDefaultChoice::UpdateDisplayInterim(GCDataStore & dataStore)
{
    m_box->SetLabel(gcstr::hapFileDefault);
    m_box->SetValue( dataStore.GetStructures().HasHapFileAdjacent(m_fileId) ? 0 : 1 );
}

void
gcHapDefaultChoice::UpdateDataInterim(GCDataStore & dataStore)
{
    if(m_box->GetValue() > 0)
    {
        dataStore.GetStructures().UnsetHapFileAdjacent(m_fileId);
    }
}

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

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

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

size_t
gcHapDefaultChoice::GetRelevantId()
{
    assert(false);
    return 0;
}

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

gcHapAdjacentChoice::gcHapAdjacentChoice(size_t fileId)
    :
    m_fileId(fileId)
{
}

gcHapAdjacentChoice::~gcHapAdjacentChoice()
{
}

void
gcHapAdjacentChoice::UpdateDisplayInitial(GCDataStore & dataStore)
{
    UpdateDisplayInterim(dataStore);
}

void
gcHapAdjacentChoice::UpdateDisplayInterim(GCDataStore & dataStore)
{
    const GCStructures & structures = dataStore.GetStructures();

    if(dataStore.FileInducesHaps(m_fileId))
    {
        m_box->SetValue(0);
        m_panel->Enable(false);
    }

    if(structures.HasHapFileAdjacent(m_fileId))
    {
        m_box->SetValue(1);
        m_text->SetValue(wxString::Format("%ld",(long)structures.GetHapFileAdjacent(m_fileId)));
    }
    else
    {
        m_box->SetValue(0);
    }
}

void
gcHapAdjacentChoice::UpdateDataInterim(GCDataStore & dataStore)
{
    if(m_box->GetValue() > 0)
    {
        wxString numHapsString = m_text->GetValue();
        long numHaps;
        if(numHapsString.ToLong(&numHaps) && (numHaps > 1))
        {
            dataStore.GetStructures().SetHapFileAdjacent(m_fileId,(size_t)numHaps);
        }
        else
        {
            throw gc_bad_ind_match_adjacency_value(numHapsString);
        }
    }
}

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

wxWindow *
gcHapAdjacentChoice::MakeWindow(wxWindow * parent)
{
    m_panel = new wxPanel(parent,-1);
    wxBoxSizer * sizer = new wxBoxSizer(wxHORIZONTAL);
    m_box = new wxCheckBox(m_panel,-1,gcstr_phase::adjacentHaps1);
    m_text = new GCNonNegativeIntegerInput(m_panel);
    m_text->SetValue(wxString::Format("%ld",(long)(gcdata::defaultHapCount)));
    wxStaticText * statText = new wxStaticText(m_panel,-1,gcstr_phase::adjacentHaps2);
    sizer->Add(m_box);
    sizer->Add(m_text);
    sizer->Add(statText);
    m_panel->SetSizerAndFit(sizer);
    return m_panel;
}

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

size_t
gcHapAdjacentChoice::GetRelevantId()
{
    assert(false);
    return 0;
}

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

#if 0

gcHapResolverChoice::gcHapResolverChoice(size_t fileId, size_t hapResolverId)
    :
    m_fileId(fileId),
    m_hapResolverId(hapResolverId)
{
}

gcHapResolverChoice::~gcHapResolverChoice()
{
}

void
gcHapResolverChoice::UpdateDisplayInitial(GCDataStore & dataStore)
{

    const gcHapResolver & hapResolver = dataStore.GetHapResolver(m_hapResolverId);
    m_box->SetLabel(hapResolver.DisplayString());
    UpdateDisplayInterim(dataStore);
}

void
gcHapResolverChoice::UpdateDisplayInterim(GCDataStore & dataStore)
{
    const GCStructures & structures = dataStore.GetStructures();

    int checkValue = 0;
    if(structures.HasHapFile(m_fileId))
    {
        if(structures.GetHapFileId(m_fileId) == m_hapResolverId)
        {
            checkValue = 1;
        }
    }

    m_box->SetValue(checkValue);
}

void
gcHapResolverChoice::UpdateDataInterim(GCDataStore & dataStore)
{
    if(m_box->GetValue() > 0)
    {
        dataStore.GetStructures().SetHapFile(m_fileId,m_hapResolverId);
    }
}

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

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

}

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

size_t
gcHapResolverChoice::GetRelevantId()
{
    assert(false);
    return 0;
}

#endif

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

gcHapChoices::gcHapChoices( wxWindow *                      parent,
                            GCDataStore &                   dataStore,
                            size_t                          fileId,
                            std::vector<gcChoiceObject*>    choices)
    :
    gcUpdatingChoose(parent,gcstr::chooseHapResolution,choices),
    m_fileId(fileId)
{
}

gcHapChoices::~gcHapChoices()
{
}

void
gcHapChoices::BuildDisplay(GCDataStore & dataStore)
{
    gcUpdatingChoose::BuildDisplay(dataStore);
}

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

bool
DoDialogEditFile(wxWindow * parentWindow, GCDataStore & dataStore, size_t fileId)
{
    gcFileEditDialog dialog(parentWindow,dataStore,fileId);

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

    // always give the full path name
    gcPlainTextHelper * fullPathHelper = new gcFullPath(fileId);
    gcUpdatingComponent * fullPath = new gcUpdatingPlainText(
        &dialog,
        gcstr::fullPath,
        fullPathHelper);
    contentSizer->Add(fullPath,
                      0,
                      wxALL | wxALIGN_CENTER | wxEXPAND ,
                      gclayout::borderSizeSmall);
    creator.AddComponent(dialog,fullPath);

    // always give the parse info, but what kind depends on if there
    // were any choices
    gcUpdatingComponent * parseInfo = NULL;
    const GCFile & fileRef = dataStore.GetDataFile(fileId);
    size_t numParses = fileRef.GetParseCount();

    if(numParses == 0)
    {
        gcPlainTextHelper * parseNone = new gcParseInfoNone();
        parseInfo = new gcUpdatingPlainText(&dialog,
                                            gcstr::parseInfo,
                                            parseNone);
    }

    if(numParses == 1)
    {
        gcPlainTextHelper * parseOne = new gcParseInfoOne(fileId);
        parseInfo = new gcUpdatingPlainText(&dialog,
                                            gcstr::parseInfo,
                                            parseOne);
    }

    if(numParses > 1)
    {
        std::vector<gcChoiceObject*> parseChoices;
        for(size_t index=0; index < numParses; index++)
        {
            const GCParse & parseRef = fileRef.GetParse(index);
            gcParseChoice * choice = new gcParseChoice(parseRef.GetId());
            parseChoices.push_back(choice);
        }

        parseInfo = new gcUpdatingChoose(&dialog,
                                         gcstr::chooseOneParse,
                                         parseChoices);
    }

    assert(parseInfo != NULL);
    contentSizer->Add(parseInfo,
                      0,
                      wxALL | wxALIGN_CENTER | wxEXPAND ,
                      gclayout::borderSizeSmall);
    creator.AddComponent(dialog,parseInfo);

    ////////////////////////////////////////////
    std::vector<gcChoiceObject*> hapChoiceVec;
    hapChoiceVec.push_back(new gcHapDefaultChoice(fileId));
    hapChoiceVec.push_back(new gcHapAdjacentChoice(fileId));

    gcHapChoices * hapFileInfo = new gcHapChoices(&dialog,dataStore,fileId,hapChoiceVec);
    contentSizer->Add(hapFileInfo,
                      1,
                      wxALL | wxALIGN_CENTER | wxEXPAND ,
                      gclayout::borderSizeSmall);
    creator.AddComponent(dialog,hapFileInfo);

    ////////////////////////////////////////////

    creator.PlaceContent(dialog,contentSizer);

    return dialog.Go();
}

bool
DoDialogExportFile(wxWindow * parentWindow, GCDataStore & dataStore)
{
    try
    {
        TiXmlDocument * docPointer = dataStore.ExportFile();
        wxString titleString = gcstr::dataFileExport;
#ifdef LAMARC_COMPILE_MACOSX
        titleString += gcstr::saveFileInstructionsForMac;
#endif
        wxFileDialog dataFileDialog(    parentWindow,
                                        titleString,
                                        wxEmptyString,      // current dir
                                        dataStore.GetOutfileName(),
                                        gcstr::exportFileGlob,
                                        wxFD_SAVE | wxFD_OVERWRITE_PROMPT);
        // don't wxFD_CHANGE_DIR -- it should be contextual
        // based on operation, but since it's not, let's
        // not change on export
        if(dataFileDialog.ShowModal() == wxID_OK)
        {
            wxString fullPathFileName = dataFileDialog.GetPath();
            dataStore.SetOutfileName(fullPathFileName);
            dataStore.WriteExportedData(docPointer);
            wxLogVerbose(gcverbose::exportSuccess,fullPathFileName.c_str());
            return true;
        }
    }
    catch(const gc_abandon_export& x)
    {
        // do nothing except interrupt;
    }
    catch(const gc_ex& g)
    {
        dataStore.GCError(g.what());
    }
    return false;
}

bool
DoDialogExportBatchFile(wxWindow * parentWindow, GCDataStore & dataStore)
{
    try
    {
        TiXmlDocument * docPointer = dataStore.ExportBatch();
        wxString titleString = gcstr::dataFileBatchExport;
#ifdef LAMARC_COMPILE_MACOSX
        titleString += gcstr::saveFileInstructionsForMac;
#endif
        wxFileDialog batchFileDialog(   parentWindow,
                                        titleString,
                                        wxEmptyString,      // current dir
                                        gcstr::batchFileDefault,
                                        gcstr::exportFileGlob,
                                        wxFD_SAVE | wxFD_OVERWRITE_PROMPT);
        // don't wxFD_CHANGE_DIR -- it should be contextual
        // based on operation, but since it's not, let's
        // not change on export
        if(batchFileDialog.ShowModal() == wxID_OK)
        {
            wxString fullPathFileName = batchFileDialog.GetPath();
            dataStore.WriteBatchFile(docPointer,fullPathFileName);
            delete docPointer;
            return true;
        }
    }
    catch(const gc_ex& g)
    {
        dataStore.GCError(g.what());
    }
    return false;
}

bool
DoDialogReadCmdFile(wxWindow * parentWindow, GCDataStore & dataStore)
{
    wxFileDialog cmdFileDialog(parentWindow,
                               gcstr_cmdfile::cmdFilesSelect,
                               wxEmptyString,      // default directory == current
                               wxEmptyString,      // default file = none
                               gcstr::xmlFiles,    // files to display
                               wxFD_OPEN | wxFD_CHANGE_DIR | wxFD_FILE_MUST_EXIST );
    // EWFIX.P3.BUG.524 -- add wxFD_MULTIPLE to add multiple files
    // and use GetPaths instead of GetPath
    bool producedResultWeShouldKeep = false;

    if(cmdFileDialog.ShowModal() == wxID_OK)
    {
#if 0   // EWFIX.P3.BUG.524
        wxArrayString fullPathFileNames;
        cmdFileDialog.GetPaths(fullPathFileNames);
        fullPathFileNames.Sort();
        for(size_t fileIndex=0; fileIndex < fullPathFileNames.Count(); fileIndex++)
        {
            wxString cmdFileName = fullPathFileNames[fileIndex];
#endif
            wxString cmdFileName = cmdFileDialog.GetPath();
            try
            {
                dataStore.ProcessCmdFile(cmdFileName);
                producedResultWeShouldKeep = true;
            }
            catch(const gc_ex& e)
            {
                // EWFIX.P3 -- use file name
                wxString msg = wxString::Format(gcerr_cmdfile::badCmdFile,
                                                cmdFileName.c_str(),
                                                e.what());
                dataStore.GCError(msg);
            }
            catch(const data_error& e)
            {
                // EWFIX.P3 -- use file name
                wxString msg = wxString::Format(gcerr_cmdfile::badCmdFile,
                                                cmdFileName.c_str(),
                                                e.what());
                dataStore.GCError(msg);
            }
#if 0   // EWFIX.P3.BUG.524
        }
#endif
    }
    return producedResultWeShouldKeep;
}

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

bool
gcActor_File_Edit::OperateOn(wxWindow * parent, GCDataStore & dataStore)
{
    return DoDialogEditFile(parent,dataStore,m_fileId);
}

//____________________________________________________________________________________
