// -*- c-basic-offset: 4 -*-

/** @file MainFrame.cpp
 *
 *  @brief implementation of MainFrame Class
 *
 *  @author Pablo d'Angelo <pablo.dangelo@web.de>
 *
 *  $Id: MainFrame.cpp 3891 2009-05-29 05:23:39Z tmodes $
 *
 *  This program is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU General Public
 *  License as published by the Free Software Foundation; either
 *  version 2 of the License, or (at your option) any later version.
 *
 *  This software is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *  General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public
 *  License along with this software; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 */

#include <config.h>
#include <hugin_version.h>
#include <exiv2/exif.hpp>
#include <exiv2/image.hpp>

#include "panoinc_WX.h"
#include "panoinc.h"

#include "base_wx/platform.h"

#include "vigra_ext/Correlation.h"

#include "PT/utils.h"

#include "hugin/config_defaults.h"
#include "hugin/PreferencesDialog.h"
#include "hugin/MainFrame.h"
#include "hugin/wxPanoCommand.h"
#include "hugin/CommandHistory.h"
#include "hugin/PanoPanel.h"
#include "hugin/AssistantPanel.h"
#include "hugin/ImagesPanel.h"
#include "hugin/LensPanel.h"
#include "hugin/CropPanel.h"
#include "hugin/OptimizePanel.h"
#include "hugin/OptimizePhotometricPanel.h"
#include "hugin/PreviewFrame.h"
#include "hugin/GLPreviewFrame.h"
#include "hugin/huginApp.h"
#include "hugin/CPEditorPanel.h"
#include "hugin/CPListFrame.h"
#include "hugin/LocalizedFileTipProvider.h"
#include "hugin/HFOVDialog.h"

#include "base_wx/MyProgressDialog.h"
#include "base_wx/ImageCache.h"

#include "base_wx/huginConfig.h"



using namespace PT;
using namespace utils;
using namespace std;
using namespace hugin_utils;

//ImagesPanel * images_panel;
//LensPanel * lens_panel;
//OptimizeVector * optset;

#ifdef __MINGW32__
// fixes for mingw compilation...
#undef FindWindow
#endif

/** file drag and drop handler method */
bool PanoDropTarget::OnDropFiles(wxCoord x, wxCoord y, const wxArrayString& filenames)
{
    DEBUG_TRACE("OnDropFiles");
    MainFrame * mf = MainFrame::Get();
    if (!mf) return false;

    if (!m_imageOnly && filenames.GetCount() == 1) {
        wxFileName file(filenames[0]);
        if (file.GetExt().CmpNoCase(wxT("pto")) == 0 ||
            file.GetExt().CmpNoCase(wxT("ptp")) == 0 ||
            file.GetExt().CmpNoCase(wxT("pts")) == 0 )
        {
            // load project
            if (mf->CloseProject(true)) {
                mf->LoadProjectFile(file.GetFullPath());
                // remove old images from cache
                ImageCache::getInstance().flush();
            }
            return true;
        }
    }
    
    // try to add as images
    std::vector<std::string> filesv;
    for (unsigned int i=0; i< filenames.GetCount(); i++) {
        wxFileName file(filenames[i]);

        if (file.GetExt().CmpNoCase(wxT("jpg")) == 0 ||
            file.GetExt().CmpNoCase(wxT("jpeg")) == 0 ||
            file.GetExt().CmpNoCase(wxT("tif")) == 0 ||
            file.GetExt().CmpNoCase(wxT("tiff")) == 0 ||
            file.GetExt().CmpNoCase(wxT("png")) == 0 ||
            file.GetExt().CmpNoCase(wxT("bmp")) == 0 ||
            file.GetExt().CmpNoCase(wxT("gif")) == 0 ||
            file.GetExt().CmpNoCase(wxT("pnm")) == 0 ||
            file.GetExt().CmpNoCase(wxT("sun")) == 0 ||
            file.GetExt().CmpNoCase(wxT("hdr")) == 0 ||
            file.GetExt().CmpNoCase(wxT("viff")) == 0 )
        {
            filesv.push_back((const char *)filenames[i].mb_str(HUGIN_CONV_FILENAME));
        }
    }
    // we got some images to add.
    if (filesv.size() > 0) {
        // use a Command to ensure proper undo and updating of GUI parts
        wxBusyCursor();
        GlobalCmdHist::getInstance().addCommand(new wxAddImagesCmd(pano,filesv));
    }
    return true;
}



// event table. this frame will recieve mostly global commands.
BEGIN_EVENT_TABLE(MainFrame, wxFrame)
    EVT_MENU(XRCID("action_new_project"),  MainFrame::OnNewProject)
    EVT_MENU(XRCID("action_load_project"),  MainFrame::OnLoadProject)
    EVT_MENU(XRCID("action_save_project"),  MainFrame::OnSaveProject)
    EVT_MENU(XRCID("action_save_as_project"),  MainFrame::OnSaveProjectAs)
    EVT_MENU(XRCID("action_save_as_ptstitcher"),  MainFrame::OnSavePTStitcherAs)
	EVT_MENU(XRCID("action_send_to_batch"),  MainFrame::OnSendToBatch)
	EVT_MENU(XRCID("action_open_batch_processor"),  MainFrame::OnOpenPTBatcher)
    EVT_MENU(XRCID("action_apply_template"),  MainFrame::OnApplyTemplate)
    EVT_MENU(XRCID("action_exit_hugin"),  MainFrame::OnUserQuit)
    EVT_MENU(XRCID("action_show_about"),  MainFrame::OnAbout)
    EVT_MENU(XRCID("action_show_help"),  MainFrame::OnHelp)
    EVT_MENU(XRCID("action_show_tip"),  MainFrame::OnTipOfDay)
    EVT_MENU(XRCID("action_show_shortcuts"),  MainFrame::OnKeyboardHelp)
    EVT_MENU(XRCID("action_show_faq"),  MainFrame::OnFAQ)
    EVT_MENU(XRCID("action_show_donate"),  MainFrame::OnShowDonate)
    EVT_MENU(XRCID("action_show_prefs"), MainFrame::OnShowPrefs)
    EVT_MENU(XRCID("ID_EDITUNDO"), MainFrame::OnUndo)
    EVT_MENU(XRCID("ID_EDITREDO"), MainFrame::OnRedo)
    EVT_MENU(XRCID("ID_SHOW_PREVIEW_FRAME"), MainFrame::OnTogglePreviewFrame)
    EVT_MENU(XRCID("ID_SHOW_GL_PREVIEW_FRAME"), MainFrame::OnToggleGLPreviewFrame)
    EVT_BUTTON(XRCID("ID_SHOW_PREVIEW_FRAME"),MainFrame::OnTogglePreviewFrame)
    EVT_BUTTON(XRCID("ID_SHOW_GL_PREVIEW_FRAME"), MainFrame::OnToggleGLPreviewFrame)

    EVT_MENU(XRCID("action_optimize"),  MainFrame::OnOptimize)
    EVT_BUTTON(XRCID("action_optimize"),  MainFrame::OnOptimize)
    EVT_MENU(XRCID("action_finetune_all_cp"), MainFrame::OnFineTuneAll)
//    EVT_BUTTON(XRCID("action_finetune_all_cp"), MainFrame::OnFineTuneAll)

    EVT_MENU(XRCID("ID_CP_TABLE"), MainFrame::OnShowCPFrame)
    EVT_BUTTON(XRCID("ID_CP_TABLE"),MainFrame::OnShowCPFrame)

    EVT_MENU(XRCID("action_add_images"),  MainFrame::OnAddImages)
    EVT_BUTTON(XRCID("action_add_images"),  MainFrame::OnAddImages)
    EVT_MENU(XRCID("action_add_time_images"),  MainFrame::OnAddTimeImages)
    EVT_BUTTON(XRCID("action_add_time_images"),  MainFrame::OnAddTimeImages)
    EVT_MENU(XRCID( "action_edit_text_dialog"),  MainFrame::OnTextEdit)
    //EVT_NOTEBOOK_PAGE_CHANGED(XRCID( "controls_notebook"), MainFrame::UpdatePanels)
	EVT_CLOSE(  MainFrame::OnExit)
    EVT_SIZE(MainFrame::OnSize)
END_EVENT_TABLE()

// change this variable definition
//wxTextCtrl *itemProjTextMemo;
// image preview
//wxBitmap *p_img = (wxBitmap *) NULL;
//WX_DEFINE_ARRAY()

MainFrame::MainFrame(wxWindow* parent, Panorama & pano)
    : cp_frame(0), pano(pano), m_doRestoreLayout(false), m_help(0)
{
    preview_frame = 0;
    m_progressMax = 1;
    m_progress = 0;

    wxBitmap bitmap;
    wxSplashScreen* splash = 0;
    wxYield();

    if (bitmap.LoadFile(huginApp::Get()->GetXRCPath() + wxT("data/splash.png"), wxBITMAP_TYPE_PNG))
    {
        // embed package version into string.
        {
            wxMemoryDC dc;
            dc.SelectObject(bitmap);
#ifdef __WXMAC__
            wxFont font(9, wxFONTFAMILY_SWISS, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL);
#else
            wxFont font(8, wxFONTFAMILY_SWISS, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL);
#endif
            dc.SetFont(font);
            dc.SetTextForeground(*wxBLACK);
            dc.SetTextBackground(*wxWHITE);
            int tw, th;
            wxString version;
#ifdef HUGIN_DEVELOPMENT_VERSION
            version.Printf(_("Prerelease %s"),wxString(PACKAGE_VERSION, wxConvLocal).c_str());
#else
            version.Printf(_("Version %s"),wxString(PACKAGE_VERSION, wxConvLocal).c_str());
#endif
            dc.GetTextExtent(version, &tw, &th);
            // place text on bitmap.
            dc.DrawText(version, bitmap.GetWidth() - tw - 3, bitmap.GetHeight() - th - 3);
        }
       
#ifdef __unix__
        splash = new wxSplashScreen(bitmap,
                              wxSPLASH_CENTRE_ON_SCREEN|wxSPLASH_NO_TIMEOUT,
                              0, NULL, -1, wxDefaultPosition,
                                    wxDefaultSize,
                                    wxSIMPLE_BORDER|wxSTAY_ON_TOP);
#else
        splash = new wxSplashScreen(bitmap,
                                    wxSPLASH_CENTRE_ON_SCREEN|wxSPLASH_NO_TIMEOUT,
                                    0, NULL, -1, wxDefaultPosition,
                                    wxDefaultSize,
                                    wxSIMPLE_BORDER);
#endif
    } else {
        wxLogFatalError(_("Fatal installation error\nThe file data/splash.png was not found at:") + huginApp::Get()->GetXRCPath());
        abort();
    }
    //splash->Refresh();
    wxYield();

    // save our pointer
    m_this = this;

    DEBUG_TRACE("");
    // load our children. some children might need special
    // initialization. this will be done later.
    wxXmlResource::Get()->LoadFrame(this, parent, wxT("main_frame"));
    DEBUG_TRACE("");

    // load our menu bar
#ifdef __WXMAC__
    wxApp::s_macAboutMenuItemId = XRCID("action_show_about");
    wxApp::s_macPreferencesMenuItemId = XRCID("action_show_prefs");
    wxApp::s_macExitMenuItemId = XRCID("action_exit_hugin");
    wxApp::s_macHelpMenuTitleName = _("&Help");
#endif
    SetMenuBar(wxXmlResource::Get()->LoadMenuBar(this, wxT("main_menubar")));

    // create tool bar
    SetToolBar(wxXmlResource::Get()->LoadToolBar(this, wxT("main_toolbar")));

    // Disable tools by default
    enableTools(false);
	
    // put an "unknown" object in an xrc file and
    // take as wxObject (second argument) the return value of wxXmlResource::Get
    // finish the images_panel
    DEBUG_TRACE("");

    assistant_panel = XRCCTRL(*this, "assistant_panel_unknown", AssistantPanel);
    assert(assistant_panel);
    assistant_panel->Init(&pano);

    // image_panel
    images_panel = XRCCTRL(*this, "images_panel_unknown", ImagesPanel);
    assert(images_panel);
    images_panel->Init(&pano);

    DEBUG_TRACE("");

    m_notebook = XRCCTRL((*this), "controls_notebook", wxNotebook);
//    m_notebook = ((wxNotebook*) ((*this).FindWindow(XRCID("controls_notebook"))));
//    m_notebook = ((wxNotebook*) (FindWindow(XRCID("controls_notebook"))));
    DEBUG_ASSERT(m_notebook);

    // the lens_panel
    lens_panel = XRCCTRL(*this, "lens_panel_unknown", LensPanel);
    assert(lens_panel);
    lens_panel->Init(&pano);


    // the crop panel
    crop_panel = XRCCTRL(*this, "crop_panel_unknown", CropPanel);
    assert(crop_panel);
    crop_panel->Init(&pano);

    // the pano_panel
    DEBUG_TRACE("");
    pano_panel = XRCCTRL(*this, "panorama_panel_unknown", PanoPanel);
    assert(pano_panel);
    pano_panel->Init(&pano);
    pano_panel->panoramaChanged (pano); // initialize from pano

    cpe = XRCCTRL(*this, "cp_editor_panel_unknown", CPEditorPanel);
    assert(cpe);
    cpe->Init(&pano);

    opt_panel = XRCCTRL(*this, "optimizer_panel_unknown", OptimizePanel);
    assert(opt_panel);
    opt_panel->Init(&pano);

    opt_photo_panel = XRCCTRL(*this, "optimizer_photometric_panel_unknown", OptimizePhotometricPanel);
    assert(opt_photo_panel);
    opt_photo_panel->Init(&pano);

    preview_frame = new PreviewFrame(this, pano);
    gl_preview_frame = new GLPreviewFrame(this, pano);

    // set the minimize icon
#ifdef __WXMSW__
    wxIcon myIcon(GetXRCPath() + wxT("data/icon.ico"),wxBITMAP_TYPE_ICO);
#else
    wxIcon myIcon(GetXRCPath() + wxT("data/icon.png"),wxBITMAP_TYPE_PNG);
#endif
    SetIcon(myIcon);

    // create a new drop handler. wxwindows deletes the automaticall
    SetDropTarget(new PanoDropTarget(pano));
    DEBUG_TRACE("");

    // create a status bar
    const int fields (2);
    CreateStatusBar(fields);
    int widths[fields] = {-1, 85};
    SetStatusWidths( fields, &widths[0]);
    SetStatusText(_("Started"), 0);
    wxYield();
    DEBUG_TRACE("");

    // observe the panorama
    pano.addObserver(this);

    // Set sizing characteristics
    //set minumum size
#if defined __WXMAC__ || defined __WXMSW__
    // a minimum nice looking size; smaller than this would clutter the layout.
    SetSizeHints(900, 675);
#else
    // For ASUS eeePc
    SetSizeHints(780, 455); //set minumum size
#endif

#if 0
    bool maximized = config->Read(wxT("/MainFrame/maximized"), 0l) != 0;
    if (maximized) {
        this->Maximize();
        // explicitly layout after maximize
        this->Layout();
    } else {
        //size
        int w = config->Read(wxT("/MainFrame/width"),-1l);
        int h = config->Read(wxT("/MainFrame/height"),-1l);
        if (w >0) {
            SetClientSize(w,h);
        } else {
            Fit();
        }
        //position
        int x = config->Read(wxT("/MainFrame/positionX"),-1l);
        int y = config->Read(wxT("/MainFrame/positionY"),-1l);
        if ( y > 0) {
            Move(x, y);
        } else {
            Move(0, 44);
        }
    }
#endif

    // set progress display for image cache.
    ImageCache::getInstance().setProgressDisplay(this);
    ImageCache::getInstance().SetUpperLimit(wxConfigBase::Get()->Read(wxT("/ImageCache/UpperBound"), HUGIN_IMGCACHE_UPPERBOUND));

    if(splash) {
        splash->Close();
    }
    wxYield();
	
    // disable automatic Layout() calls, to it by hand
    SetAutoLayout(false);


#ifdef __WXMSW__
    // wxFrame does have a strange background color on Windows, copy color from a child widget
    this->SetBackgroundColour(images_panel->GetBackgroundColour());
#endif

// By using /SUBSYSTEM:CONSOLE /ENTRY:"WinMainCRTStartup" in the linker
// options for the debug build, a console window will be used for stdout
// and stderr. No need to redirect to a file. Better security since we can't
// guarantee that c: exists and writing a file to the root directory is
// never a good idea. release build still uses /SUBSYSTEM:WINDOWS

#if 0
#ifdef DEBUG
#ifdef __WXMSW__

    freopen("c:\\hugin_stdout.txt", "w", stdout);    // redirect stdout to file
    freopen("c:\\hugin_stderr.txt", "w", stderr);    // redirect stderr to file
#endif
#endif
#endif
    DEBUG_TRACE("");
}

MainFrame::~MainFrame()
{
    DEBUG_TRACE("dtor");
    ImageCache::getInstance().setProgressDisplay(0);
	delete & ImageCache::getInstance();
	delete & GlobalCmdHist::getInstance();
//    delete cpe;
//    delete images_panel;
    DEBUG_DEBUG("removing observer");
    pano.removeObserver(this);

    // get the global config object
    wxConfigBase* config = wxConfigBase::Get();

    StoreFramePosition(this, wxT("MainFrame"));

    config->Flush();

    DEBUG_TRACE("dtor end");
}

void MainFrame::RestoreLayout()
{
    DEBUG_TRACE("");	
    // restore layout of child widgets, now that all widgets have been created,
    // are of similar size
//    cpe->RestoreLayout();
//    lens_panel->RestoreLayout();
//    images_panel->RestoreLayout();
    DEBUG_TRACE("");	
}

//void MainFrame::panoramaChanged(PT::Panorama &panorama)
void MainFrame::panoramaImagesChanged(PT::Panorama &panorama, const PT::UIntSet & changed)
{
    DEBUG_TRACE("");
    assert(&pano == &panorama);
    if (pano.getNrOfImages() == 0) {
	  enableTools(false);
	} else {
	  enableTools(true);
	}
}

void MainFrame::OnUserQuit(wxCommandEvent & e)
{
    Close();
}

bool MainFrame::CloseProject(bool cnacelable)
{
    if (pano.isDirty()) {
        int answer = wxMessageBox(_("The panorama has been changed\nSave changes?"), _("Save Panorama?"),
                                  cnacelable? (wxYES_NO | wxCANCEL | wxICON_EXCLAMATION):(wxYES_NO | wxICON_EXCLAMATION),
                                  this);
        switch (answer){
            case wxYES:
            {
                wxCommandEvent dummy;
                OnSaveProject(dummy);
                return true;
            }
            case wxCANCEL:
            {
                return false;
            }
            default: //no save
            {
                return true;
            }
        }
    }
    else return true;
}

void MainFrame::OnExit(wxCloseEvent & e)
{
    DEBUG_TRACE("");
    if(!CloseProject(e.CanVeto()))
    {
       if (e.CanVeto())
       {
            e.Veto();
            return;
       }
       wxLogError(_("forced close"));
    }

    if(preview_frame)
    {
       preview_frame->Close(true);
    }

    ImageCache::getInstance().flush();
    //Close(TRUE);
    this->Destroy();
    DEBUG_TRACE("");
}

void MainFrame::OnSaveProject(wxCommandEvent & e)
{
    DEBUG_TRACE("");
    try {
    wxFileName scriptName = m_filename;
    if (m_filename == wxT("")) {
        OnSaveProjectAs(e);
        scriptName = m_filename;
    } else {
        // the project file is just a PTOptimizer script...
        std::string path = getPathPrefix(std::string(scriptName.GetFullPath().mb_str(HUGIN_CONV_FILENAME)));
        DEBUG_DEBUG("stripping " << path << " from image filenames");
        std::ofstream script(scriptName.GetFullPath().mb_str(HUGIN_CONV_FILENAME));
        PT::OptimizeVector optvec = opt_panel->getOptimizeVector();
        PT::UIntSet all;
        if (pano.getNrOfImages() > 0) {
           fill_set(all, 0, pano.getNrOfImages()-1);
        }
        pano.printPanoramaScript(script, optvec, pano.getOptions(), all, false, path);
        script.close();

        int createMakefile = 1;
#if defined __WXMAC__ && defined MAC_SELF_CONTAINED_BUNDLE
        createMakefile = 0;
#endif
        if (createMakefile && pano.getNrOfImages() > 0) {
            wxString makefn = scriptName.GetFullPath() + wxT(".mk");
            std::ofstream makefile(makefn.mb_str(HUGIN_CONV_FILENAME));
            wxString ptoFnWX = scriptName.GetFullPath();
            std::string ptoFn(ptoFnWX.mb_str(HUGIN_CONV_FILENAME));
            wxString bindir = huginApp::Get()->GetUtilsBinDir();
            PTPrograms progs = getPTProgramsConfig(bindir, wxConfigBase::Get());
            std::string resultFn;
            wxString resultFnwx = scriptName.GetFullPath();
            resultFn = resultFnwx.mb_str(HUGIN_CONV_FILENAME);
            resultFn = utils::stripPath(utils::stripExtension(resultFn));
            std::string tmpDir((wxConfigBase::Get()->Read(wxT("tempDir"),wxT(""))).mb_str(HUGIN_CONV_FILENAME));

            std::vector<std::string> outputFiles;
            HuginBase::PanoramaMakefileExport::createMakefile(pano,
                                                              pano.getActiveImages(),
                                                              ptoFn,
                                                              resultFn,
                                                              progs,
                                                              "",
                                                              outputFiles,
                                                              makefile,
                                                              tmpDir);
        }
    }
    SetStatusText(wxString::Format(_("saved project %s"), m_filename.c_str()),0);
    this->SetTitle(scriptName.GetName() + wxT(".") + scriptName.GetExt() + wxT(" - hugin"));
    pano.clearDirty();
    } catch (std::exception & e) {
        wxString err(e.what(), wxConvLocal);
        wxMessageBox(err, wxT("Exception during saving project file"));
    }
}

void MainFrame::OnSaveProjectAs(wxCommandEvent & e)
{
    DEBUG_TRACE("");
    wxString scriptName;
    if (m_filename == wxT("")) {
        scriptName = getDefaultProjectName(pano) + wxT(".pto");
    }
    wxFileDialog dlg(this,
                     _("Save project file"),
                     wxConfigBase::Get()->Read(wxT("/actualPath"),wxT("")), scriptName,
                     _("Project files (*.pto)|*.pto|All files (*)|*"),
                     wxSAVE, wxDefaultPosition);
    dlg.SetDirectory(wxConfigBase::Get()->Read(wxT("/actualPath"),wxT("")));
    if (dlg.ShowModal() == wxID_OK) {
        wxConfig::Get()->Write(wxT("/actualPath"), dlg.GetDirectory());  // remember for later
        wxString fn = dlg.GetPath();
        if (fn.Right(4) != wxT(".pto")) {
            fn.Append(wxT(".pto"));
        }
        if (wxFile::Exists(fn)) {
            int d = wxMessageBox(wxString::Format(_("File %s exists. Overwrite?"), fn.c_str()),
                                 _("Save project"), wxYES_NO | wxICON_QUESTION);
            if (d != wxYES) {
                return;
            }
        }
        m_filename = fn;
        OnSaveProject(e);
    }
}

void MainFrame::OnSavePTStitcherAs(wxCommandEvent & e)
{
    DEBUG_TRACE("");
    wxString scriptName = m_filename;
    if (m_filename == wxT("")) {
        scriptName = getDefaultProjectName(pano);
    }
    wxFileName scriptNameFN(scriptName);
    wxString fn = scriptNameFN.GetName() + wxT(".txt");
    wxFileDialog dlg(this,
                     _("Save PTmender script file"),
                     wxConfigBase::Get()->Read(wxT("/actualPath"),wxT("")), fn,
                     _("PTmender files (*.txt)|*.txt"),
                     wxSAVE, wxDefaultPosition);
    dlg.SetDirectory(wxConfigBase::Get()->Read(wxT("/actualPath"),wxT("")));
    if (dlg.ShowModal() == wxID_OK) {
        wxString fname = dlg.GetPath();
        // the project file is just a PTStitcher script...
        wxFileName scriptName = fname;
        PT::UIntSet all;
        if (pano.getNrOfImages() > 0) {
            fill_set(all, 0, pano.getNrOfImages()-1);
        }
        std::ofstream script(scriptName.GetFullPath().mb_str(HUGIN_CONV_FILENAME));
        pano.printStitcherScript(script, pano.getOptions(), all);
        script.close();
    }

}

void MainFrame::LoadProjectFile(const wxString & filename)
{
    DEBUG_TRACE("");
    m_filename = filename;

    // remove old images from cache
    // hmm probably not a good idea, if the project is reloaded..
    // ImageCache::getInstance().flush();

    SetStatusText( _("Open project:   ") + filename);

    wxFileName fname(filename);
    wxString path = fname.GetPath(wxPATH_GET_VOLUME | wxPATH_GET_SEPARATOR);
    if (fname.IsOk() && fname.FileExists()) {
        wxBusyCursor wait;
        GlobalCmdHist::getInstance().addCommand(
           new wxLoadPTProjectCmd(pano,(const char *)filename.mb_str(HUGIN_CONV_FILENAME), (const char *)path.mb_str(HUGIN_CONV_FILENAME))
           );
        DEBUG_DEBUG("project contains " << pano.getNrOfImages() << " after load");
        opt_panel->setModeCustom();
        SetStatusText(_("Project opened"));
        this->SetTitle(fname.GetName() + wxT(".") + fname.GetExt() + wxT(" - hugin"));
        if (! (fname.GetExt() == wxT("pto"))) {
            // do not remember filename if its not a hugin project
            // to avoid overwriting the original project with an
            // incompatible one
            m_filename = wxT("");
        }
        // get the global config object
        wxConfigBase* config = wxConfigBase::Get();
        config->Write(wxT("/actualPath"), path);  // remember for later
    } else {
        SetStatusText( _("Error opening project:   ") + filename);
        DEBUG_ERROR("Could not open file " << filename);
    }
    // clean up some cruft from older versions
    // force TIFF_m output
    PanoramaOptions opts = pano.getOptions();
    switch (opts.outputMode) {
        case PanoramaOptions::TIFF:
        case PanoramaOptions::JPEG:
        case PanoramaOptions::PNG:
            opts.outputLDRBlended = true;
            break;
        default:
            break;
    }
    opts.outputFormat = PanoramaOptions::TIFF_m;

    pano.clearDirty();
    // force update of preview window
    if ( !(preview_frame->IsIconized() ||(! preview_frame->IsShown()) ) ) {
        wxCommandEvent dummy;
        preview_frame->OnUpdate(dummy);
    }
}

#ifdef __WXMAC__
void MainFrame::MacOnOpenFile(const wxString & filename)
{
    if(!CloseProject(true)) return; //if closing old project is canceled do nothing.

    ImageCache::getInstance().flush();
    LoadProjectFile(filename);
}
#endif

void MainFrame::OnLoadProject(wxCommandEvent & e)
{
    DEBUG_TRACE("");

    if(CloseProject(true)) //if closing old project is canceled do nothing.
    {
        // get the global config object
        wxConfigBase* config = wxConfigBase::Get();

        wxString defaultdir = config->Read(wxT("/actualPath"),wxT(""));
        wxFileDialog dlg(this,
                         _("Open project file"),
                         defaultdir, wxT(""),
                         _("Project files (*.pto,*.ptp,*.pts,*.oto)|*.pto;*.ptp;*.pts;*.oto;|All files (*)|*"),
                         wxOPEN, wxDefaultPosition);
        dlg.SetDirectory(defaultdir);
        if (dlg.ShowModal() == wxID_OK) {
            // remove old images from cache
            ImageCache::getInstance().flush();

            wxString filename = dlg.GetPath();
            LoadProjectFile(filename);
            return;
        }
    }
    // do not close old project
    // nothing to open
    SetStatusText( _("Open project: cancel"));
}

void MainFrame::OnNewProject(wxCommandEvent & e)
{
    if(!CloseProject(true)) return; //if closing current project is canceled

    m_filename = wxT("");
    GlobalCmdHist::getInstance().addCommand( new NewPanoCmd(pano));
    // remove old images from cache
    ImageCache::getInstance().flush();
    this->SetTitle(wxT("hugin"));
    pano.clearDirty();

    // Setup pano with options from preferences
    PanoramaOptions opts = pano.getOptions();
    wxConfigBase* config = wxConfigBase::Get();
    opts.enblendOptions = config->Read(wxT("Enblend/Args"),wxT(HUGIN_ENBLEND_ARGS)).mb_str(wxConvLocal);
    opts.enfuseOptions = config->Read(wxT("Enfuse/Args"),wxT(HUGIN_ENFUSE_ARGS)).mb_str(wxConvLocal);
    pano.setOptions(opts);

    wxCommandEvent dummy;
    preview_frame->OnUpdate(dummy);
}

void MainFrame::OnAddImages( wxCommandEvent& event )
{
    DEBUG_TRACE("");
    wxString e_stat;

    // To get users path we do following:
    // get the global config object
    wxConfigBase* config = wxConfigBase::Get();

    wxString path = config->Read(wxT("/actualPath"), wxT(""));
    wxFileDialog dlg(this,_("Add images"),
                     path, wxT(""),
                     HUGIN_WX_FILE_IMG_FILTER,
                     wxOPEN|wxMULTIPLE , wxDefaultPosition);
    dlg.SetDirectory(path);

    // remember the image extension
    wxString img_ext;
    if (config->HasEntry(wxT("lastImageType"))){
      img_ext = config->Read(wxT("lastImageType")).c_str();
    }
    if (img_ext == wxT("all images"))
      dlg.SetFilterIndex(0);
    else if (img_ext == wxT("jpg"))
      dlg.SetFilterIndex(1);
    else if (img_ext == wxT("tiff"))
      dlg.SetFilterIndex(2);
    else if (img_ext == wxT("png"))
      dlg.SetFilterIndex(3);
    else if (img_ext == wxT("hdr"))
      dlg.SetFilterIndex(4);
    else if (img_ext == wxT("exr"))
      dlg.SetFilterIndex(5);
    else if (img_ext == wxT("all files"))
      dlg.SetFilterIndex(6);
    DEBUG_INFO ( "Image extention: " << img_ext.mb_str(wxConvLocal) )

    // call the file dialog
    if (dlg.ShowModal() == wxID_OK) {
        // get the selections
        wxArrayString Pathnames;
        wxArrayString Filenames;
        dlg.GetPaths(Pathnames);
        dlg.GetFilenames(Filenames);

        // e safe the current path to config
        config->Write(wxT("/actualPath"), dlg.GetDirectory());  // remember for later

        std::vector<std::string> filesv;
        for (unsigned int i=0; i< Pathnames.GetCount(); i++) {
            filesv.push_back((const char *)Pathnames[i].mb_str(HUGIN_CONV_FILENAME));
        }

        // we got some images to add.
        if (filesv.size() > 0) {
            // use a Command to ensure proper undo and updating of GUI
            // parts
            wxBusyCursor();
            GlobalCmdHist::getInstance().addCommand(
                new wxAddImagesCmd(pano,filesv)
                );
        }
        DEBUG_INFO ( wxString::Format(wxT("img_ext: %d"), dlg.GetFilterIndex()).mb_str(wxConvLocal) )
        // save the image extension
        switch ( dlg.GetFilterIndex() ) {
        case 0: config->Write(wxT("lastImageType"), wxT("all images")); break;
        case 1: config->Write(wxT("lastImageType"), wxT("jpg")); break;
        case 2: config->Write(wxT("lastImageType"), wxT("tiff")); break;
        case 3: config->Write(wxT("lastImageType"), wxT("png")); break;
        case 4: config->Write(wxT("lastImageType"), wxT("hdr")); break;
        case 5: config->Write(wxT("lastImageType"), wxT("exr")); break;
        case 6: config->Write(wxT("lastImageType"), wxT("all files")); break;
        }

    } else {
        // nothing to open
        SetStatusText( _("Add Image: cancel"));
    }

    DEBUG_TRACE("");
}

time_t ReadExifTime(const char* filename)
{
    Exiv2::Image::AutoPtr image;
    try {
        image = Exiv2::ImageFactory::open(filename);
    }catch(...) {
        return 0;
    }
    if (image.get() == 0) {
        return 0;
    }

    image->readMetadata();
    Exiv2::ExifData &exifData = image->exifData();
    if (exifData.empty()) {
        return 0;
    }

    Exiv2::Exifdatum& tag = exifData["Exif.Image.DateTime"];
    const std::string date_time = tag.toString();

    // Remember the file and a shutter timestamp.
    struct tm when;
    memset(&when, 0, sizeof(when));
    when.tm_wday = -1;

    // parse into the tm_structure
    const int a = sscanf(date_time.c_str(), "%d:%d:%d %d:%d:%d",
            &when.tm_year, &when.tm_mon, &when.tm_mday,
            &when.tm_hour, &when.tm_min, &when.tm_sec);

    if (a == 6){
        when.tm_isdst = -1;
        when.tm_mon -= 1;      // Adjust for unix zero-based months
        when.tm_year -= 1900;  // Adjust for year starting at 1900
    } else {
        // Not in EXIF format
        return 0;
    }

    time_t stamp;
    stamp = mktime(&when);
    if (stamp == (time_t)(-1))
        return 0;

    return stamp;
}

WX_DECLARE_STRING_HASH_MAP(time_t, StringToPointerHash);
WX_DECLARE_STRING_HASH_MAP(int, StringToFlagHash);

struct sortbytime
{
    sortbytime(map<string, time_t> & h)
        : m_time(h)
    { }

    bool operator()(const std::string & s1, const std::string & s2)
    {
        time_t t1 = m_time[s1];
        time_t t2 = m_time[s2];
        return t1 < t2;
    }

    map<string, time_t> & m_time;
};


void MainFrame::OnAddTimeImages( wxCommandEvent& event )
{
    DEBUG_TRACE("");

    int maxtimediff = wxConfigBase::Get()->Read(wxT("CaptureTimeSpan"), HUGIN_CAPTURE_TIMESPAN);

    // If no images already loaded, offer user a chance to pick one.
    int images = pano.getNrOfImages();
    if (!images) {
        OnAddImages(event);
        images = pano.getNrOfImages();
        if (!images)
            return;
    }

    DEBUG_TRACE("seeking similarly timed images");

    // Collect potential image-mates.
    StringToPointerHash filenames;
    StringToFlagHash preloaded;
    while (images)
    {
        --images;

        // Get the filename.
        const PanoImage& image = pano.getImage(images);
        std::string filename = image.getFilename();
        wxString file(filename.c_str(), HUGIN_CONV_FILENAME);
        preloaded[file] = 1;

        // Glob for all files of same type in same directory.
        wxString name(filename.c_str(), HUGIN_CONV_FILENAME);
        wxString path = ::wxPathOnly(name) + wxT("/*");
        file = ::wxFindFirstFile(path);
        while (!file.IsEmpty())
        {
            // Associated with a NULL dummy timestamp for now.
			filenames[file] = 0;
			file = ::wxFindNextFile();
        }
    }

    DEBUG_INFO("found " << filenames.size() <<
                " candidate files to search.");

    // For each globbed or loaded file,
    StringToPointerHash::iterator found;
    std::map<std::string, time_t> timeMap;
    for (found = filenames.begin(); found != filenames.end(); found++)
    {
        wxString file = found->first;
        // Check the time if it's got a camera EXIF timestamp.
		  time_t stamp = ReadExifTime(file.mb_str(HUGIN_CONV_FILENAME));
      	  if (stamp) {
            filenames[file] = stamp;
            timeMap[(const char *)file.mb_str(HUGIN_CONV_FILENAME)] = stamp;
      	  }
    }

    //TODO: sorting the filenames keys by timestamp would be useful

    std::vector<std::string> filesv;
    int changed;
    do
    {
        changed = FALSE;

        // For each timestamped file,
        for (found = filenames.begin(); found != filenames.end(); found++)
        {
            wxString recruit = found->first;
			if (preloaded[recruit] == 1)
			  continue;
            time_t pledge = filenames[recruit];
            if (!pledge)
                continue;

            // For each other image already loaded,
            images = pano.getNrOfImages();
            while (images)
            {
                --images;
                const PanoImage& image = pano.getImage(images);
                std::string filename = image.getFilename();
                wxString file(filename.c_str(), HUGIN_CONV_FILENAME);
                if (file == recruit)
                    continue;

                // If it is within threshold time,
                time_t stamp = filenames[file];
                if (abs((int)(pledge - stamp)) < maxtimediff)
                {
                    // Load this file, and remember it.
                    DEBUG_TRACE("Recruited " << recruit.mb_str(wxConvLocal));
                    std::string file = (const char *)recruit.mb_str(HUGIN_CONV_FILENAME);
                    filesv.push_back(file);
                    // Don't recruit it again.
                    filenames[recruit] = 0;
                    // Count this as a change.
                    changed++;
                    break;
                }
            }
        }
    } while (changed);

    // sort files by date
    sortbytime spred(timeMap);
    sort(filesv.begin(), filesv.end(), spred);

    // Load all of the named files.
    wxBusyCursor();
    GlobalCmdHist::getInstance().addCommand(
        new wxAddImagesCmd(pano,filesv)
        );
}


void MainFrame::OnTextEdit( wxCommandEvent& WXUNUSED(event) )
{
        DEBUG_TRACE("");
  wxDialog dlg;
  wxXmlResource::Get()->LoadDialog(&dlg, this, wxT("text_edit_dialog"));
  dlg.ShowModal();
//  dlg.Show (TRUE);
        DEBUG_TRACE("");
}

void MainFrame::OnShowDonate(wxCommandEvent & e)
{
    wxLaunchDefaultBrowser(wxT("http://sourceforge.net/project/project_donations.php?group_id=77506"));
}

void MainFrame::OnAbout(wxCommandEvent & e)
{
    DEBUG_TRACE("");
    wxDialog dlg;
	wxString strFile;
	wxString langCode;
	
    wxXmlResource::Get()->LoadDialog(&dlg, this, wxT("about_dlg"));

#if __WXMAC__ && defined MAC_SELF_CONTAINED_BUNDLE
    //rely on the system's locale choice
    strFile = MacGetPathToBundledResourceFile(CFSTR("about.htm"));
    if(strFile!=wxT("")) XRCCTRL(dlg,"about_html",wxHtmlWindow)->LoadPage(strFile);
#else    
    //if the language is not default, load custom About file (if exists)
    langCode = huginApp::Get()->GetLocale().GetName().Left(2).Lower();
    DEBUG_INFO("Lang Code: " << langCode.mb_str(wxConvLocal));
    if(langCode != wxString(wxT("en")))
    {
        strFile = GetXRCPath() + wxT("data/about_") + langCode + wxT(".htm");
        if(wxFile::Exists(strFile))
        {
            DEBUG_TRACE("Using About: " << strFile.mb_str(wxConvLocal));
            XRCCTRL(dlg,"about_html",wxHtmlWindow)->LoadPage(strFile);
        }
    }
#endif
    dlg.ShowModal();
}

void MainFrame::OnHelp(wxCommandEvent & e)
{
    DisplayHelp(wxT("contents.html"));
}

void MainFrame::OnKeyboardHelp(wxCommandEvent & e)
{
    DisplayHelp(wxT("Hugin_Keyboard_shortcuts.html"));
}

void MainFrame::OnFAQ(wxCommandEvent & e)
{
    DisplayHelp(wxT("Hugin_FAQ.html"));
}


void MainFrame::DisplayHelp(wxString section)
{
    DEBUG_TRACE("");

    if (m_help == 0)
    {
#if defined __WXMAC__ && defined MAC_SELF_CONTAINED_BUNDLE
        // On Mac, xrc/data/help_LOCALE should be in the bundle as LOCALE.lproj/help
        // which we can rely on the operating sytem to pick the right locale's.
        wxString strFile = MacGetPathToBundledResourceFile(CFSTR("help"));
        if(strFile!=wxT(""))
        {
            strFile += wxT("/hugin.hhp");
        } else {
            wxLogError(wxString::Format(wxT("Could not find help directory in the bundle"), strFile.c_str()));
            return;
        }
#else
        // find base filename
        wxString helpFile = wxT("help_") + huginApp::Get()->GetLocale().GetCanonicalName() + wxT("/hugin.hhp");
        DEBUG_INFO("help file candidate: " << helpFile.mb_str(wxConvLocal));
        //if the language is not default, load custom About file (if exists)
        wxString strFile = GetXRCPath() + wxT("data/") + helpFile;
        if(wxFile::Exists(strFile))
        {
            DEBUG_TRACE("Using About: " << strFile.mb_str(wxConvLocal));
        } else {
            // try default
            strFile = GetXRCPath() + wxT("data/help_en_EN/hugin.hhp");
        }
#endif

        if(!wxFile::Exists(strFile))
        {
            wxLogError(wxString::Format(wxT("Could not open help file: %s"), strFile.c_str()));
            return;
        }
        DEBUG_INFO("help file: " << strFile.mb_str(wxConvLocal));
        m_help = new wxHtmlHelpController();
        m_help->AddBook(strFile);
    }
    m_help->Display(section);
}

void MainFrame::OnTipOfDay(wxCommandEvent& WXUNUSED(e))
{
    wxString strFile;
    bool bShowAtStartup;
// DGSW FIXME - Unreferenced
//	bool bTipsExist = false;
    int nValue;

    wxConfigBase * config = wxConfigBase::Get();
    nValue = config->Read(wxT("/MainFrame/ShowStartTip"),1l);

    //TODO: tips not localisable
    DEBUG_INFO("Tip index: " << nValue);
    strFile = GetXRCPath() + wxT("data/tips.txt");  //load default file
	
    DEBUG_INFO("Reading tips from " << strFile.mb_str(wxConvLocal));
    wxTipProvider *tipProvider = new LocalizedFileTipProvider(strFile, nValue);
    bShowAtStartup = wxShowTip(this, tipProvider,(nValue ? true:false));

    //store startup preferences
    nValue = (bShowAtStartup ? tipProvider->GetCurrentTip() : 0);
    DEBUG_INFO("Writing tip index: " << nValue);
    config->Write(wxT("/MainFrame/ShowStartTip"), nValue);
    delete tipProvider;
}


void MainFrame::OnShowPrefs(wxCommandEvent & e)
{
    DEBUG_TRACE("");
//    wxDialog dlg(this, -1, _("Preferences"), wxDefaultPosition, wxDefaultSize, wxDEFAULT_DIALOG_STYLE | wxDIALOG_MODAL);
    PreferencesDialog * pref_dlg = new PreferencesDialog(this);
    pref_dlg->ShowModal();
    //update image cache size
    ImageCache::getInstance().SetUpperLimit(wxConfigBase::Get()->Read(wxT("/ImageCache/UpperBound"), HUGIN_IMGCACHE_UPPERBOUND));

}

void MainFrame::UpdatePanels( wxCommandEvent& WXUNUSED(event) )
{   // Maybe this can be invoced by the Panorama::Changed() or
    // something like this. So no everytime update would be needed.
    DEBUG_TRACE("");
}

void MainFrame::OnTogglePreviewFrame(wxCommandEvent & e)
{
    DEBUG_TRACE("");
    if (preview_frame->IsIconized()) {
        preview_frame->Iconize(false);
    }
    preview_frame->Show();
    preview_frame->Raise();
	
	// we need to force an update since autoupdate fires
	// before the preview frame is shown
    wxCommandEvent dummy;
	preview_frame->OnUpdate(dummy);
}

void MainFrame::OnToggleGLPreviewFrame(wxCommandEvent & e)
{
    if (gl_preview_frame->IsIconized()) {
        gl_preview_frame->Iconize(false);
    }
    gl_preview_frame->Show();
    gl_preview_frame->Raise();
}

void MainFrame::OnShowCPFrame(wxCommandEvent & e)
{
    DEBUG_TRACE("");
    if (cp_frame) {
        if (cp_frame->IsIconized()) {
            cp_frame->Iconize(false);
        }
        cp_frame->Show();
        cp_frame->Raise();
    } else {
        cp_frame = new CPListFrame(this, pano);
        cp_frame->Show();
    }
}

void MainFrame::OnCPListFrameClosed()
{
    cp_frame = 0;
}

void MainFrame::OnOptimize(wxCommandEvent & e)
{
    DEBUG_TRACE("");
    wxCommandEvent dummy;
    opt_panel->OnOptimizeButton(dummy);
}

void MainFrame::OnDoStitch(wxCommandEvent & e)
{
    DEBUG_TRACE("");
    pano_panel->DoStitch();
}

void MainFrame::OnApplyTemplate(wxCommandEvent & e)
{
    // get the global config object
    wxConfigBase* config = wxConfigBase::Get();

    wxFileDialog dlg(this,
                     _("Choose template project"),
                     config->Read(wxT("/templatePath"),wxT("")), wxT(""),
                     _("Project files (*.pto,*.ptp,*.pts,*.oto)|*.pto;*.ptp;*.pts;*.oto;|All files (*)|*"),
                     wxOPEN, wxDefaultPosition);
    dlg.SetDirectory(wxConfigBase::Get()->Read(wxT("/templatePath"),wxT("")));
    if (dlg.ShowModal() == wxID_OK) {
        wxString filename = dlg.GetPath();
        wxConfig::Get()->Write(wxT("/templatePath"), dlg.GetDirectory());  // remember for later

        std::ifstream file((const char *)filename.mb_str(HUGIN_CONV_FILENAME));

        GlobalCmdHist::getInstance().addCommand(
                new wxApplyTemplateCmd(pano, file));

    }
}


void MainFrame::OnSendToBatch(wxCommandEvent & e)
{
	pano_panel->SendToBatch();
}

void MainFrame::OnOpenPTBatcher(wxCommandEvent & e)
{
#ifdef __WINDOWS__
	wxString huginPath = getExePath(wxGetApp().argv[0])+wxFileName::GetPathSeparator();
#else
	wxString huginPath = _T("");	//we call the batch processor directly without path on linux
#endif	
	wxExecute(huginPath+_T("PTBatcherGUI"));
}

void MainFrame::OnFineTuneAll(wxCommandEvent & e)
{
    DEBUG_TRACE("");
    // fine-tune all points

    CPVector cps = pano.getCtrlPoints();

    // create a map of all control points.
    std::set<unsigned int> unoptimized;
    for (unsigned int i=0; i < cps.size(); i++) {
        // create all control points.
        unoptimized.insert(i);
    }

    unsigned int nGood=0;
    unsigned int nBad=0;

    wxConfigBase *cfg = wxConfigBase::Get();
    bool rotatingFinetune = cfg->Read(wxT("/Finetune/RotationSearch"), HUGIN_FT_ROTATION_SEARCH) == 1;
    double startAngle=HUGIN_FT_ROTATION_START_ANGLE;
    cfg->Read(wxT("/Finetune/RotationStartAngle"),&startAngle,HUGIN_FT_ROTATION_START_ANGLE);
    startAngle=DEG_TO_RAD(startAngle);
    double stopAngle=HUGIN_FT_ROTATION_STOP_ANGLE;
    cfg->Read(wxT("/Finetune/RotationStopAngle"),&stopAngle,HUGIN_FT_ROTATION_STOP_ANGLE);
    stopAngle=DEG_TO_RAD(stopAngle);
    int nSteps = cfg->Read(wxT("/Finetune/RotationSteps"), HUGIN_FT_ROTATION_STEPS);

    double corrThresh=HUGIN_FT_CORR_THRESHOLD;
    cfg->Read(wxT("/Finetune/CorrThreshold"), &corrThresh, HUGIN_FT_CORR_THRESHOLD);
    double curvThresh = HUGIN_FT_CURV_THRESHOLD;
    wxConfigBase::Get()->Read(wxT("/Finetune/CurvThreshold"),&curvThresh,
                              HUGIN_FT_CURV_THRESHOLD);

    {
    ProgressReporterDialog progress(unoptimized.size(),_("Fine-tuning all points"),_("Finetuning"),this);

    ImageCache & imgCache = ImageCache::getInstance();

    // do not process the control points in random order,
    // but walk from image to image, to reduce image reloading
    // in low mem situations.
    for (unsigned int imgNr = 0 ; imgNr < pano.getNrOfImages(); imgNr++) {
        std::set<unsigned int>::iterator it=unoptimized.begin();

        imgCache.softFlush();

        while (it != unoptimized.end()) {
            if (cps[*it].image1Nr == imgNr || cps[*it].image2Nr == imgNr) {
                progress.increaseProgress(1);
                if (cps[*it].mode == ControlPoint::X_Y) {
                    // finetune only normal points
                    DEBUG_DEBUG("fine tuning point: " << *it);
                    wxImage wxSearchImg;
                    ImageCache::EntryPtr searchImg = imgCache.getImage(
                         pano.getImage(cps[*it].image2Nr).getFilename());

                    ImageCache::EntryPtr templImg = imgCache.getImage(
                         pano.getImage(cps[*it].image1Nr).getFilename());


                    // load parameters
                    long templWidth = wxConfigBase::Get()->Read(
                        wxT("/Finetune/TemplateSize"),HUGIN_FT_TEMPLATE_SIZE);
                    long sWidth = templWidth + wxConfigBase::Get()->Read(
                        wxT("/Finetune/LocalSearchWidth"),HUGIN_FT_LOCAL_SEARCH_WIDTH);
                    vigra_ext::CorrelationResult res;
                    vigra::Diff2D roundP1(roundi(cps[*it].x1), roundi(cps[*it].y1));
                    vigra::Diff2D roundP2(roundi(cps[*it].x2), roundi(cps[*it].y2));

                    if (rotatingFinetune) {
                        res = vigra_ext::PointFineTuneRotSearch(
                            *(templImg->get8BitImage()),
                            roundP1,
                            templWidth,
                            *(searchImg->get8BitImage()),
                            roundP2,
                            sWidth,
                            startAngle, stopAngle, nSteps
                            );

                    } else {
                        res = vigra_ext::PointFineTune(
                            *(templImg->get8BitImage()),
                            roundP1,
                            templWidth,
                            *(searchImg->get8BitImage()),
                            roundP2,
                            sWidth
                            );

                    }
                    // invert curvature. we always assume its a maxima, the curvature there is negative
                    // however, we allow the user to specify a positive threshold, so we need to
                    // invert it
                    res.curv.x = - res.curv.x;
                    res.curv.y = - res.curv.y;

                    if (res.maxi < corrThresh ||res.curv.x < curvThresh || res.curv.y < curvThresh  )
                    {
                        // Bad correlation result.
                        nBad++;
                        if (res.maxi >= corrThresh) {
                            cps[*it].error = 0;
                        }
                        cps[*it].error = res.maxi;
                        DEBUG_DEBUG("low correlation: " << res.maxi << " curv: " << res.curv);
                    } else {
                        nGood++;
                        // only update if a good correlation was found
                        cps[*it].x1 = roundP1.x;
                        cps[*it].y1 = roundP1.y;
                        cps[*it].x2 = res.maxpos.x;
                        cps[*it].y2 = res.maxpos.y;
                        cps[*it].error = res.maxi;
                    }
                }
                unsigned int rm = *it;
                it++;
                unoptimized.erase(rm);
            } else {
                it++;
            }
        }
    }
    }
    wxString result;
    result.Printf(_("%d points fine-tuned, %d points not updated due to low correlation\n\nHint: The errors of the fine-tuned points have been set to the correlation coefficient\nProblematic points can be spotted (just after fine-tune, before optimizing)\nby an error <= %.3f.\nThe error of points without a well defined peak (typically in regions with uniform color)\nwill be set to 0\n\nUse the Control Point list (F3) to see all points of the current project\n"),
                  nGood, nBad, corrThresh);
    wxMessageBox(result, _("Fine-tune result"), wxOK);
    // set newly optimized points
    GlobalCmdHist::getInstance().addCommand(
        new UpdateCPsCmd(pano,cps)
        );
}

void MainFrame::OnUndo(wxCommandEvent & e)
{
    DEBUG_TRACE("OnUndo");
    GlobalCmdHist::getInstance().undo();
}

void MainFrame::OnRedo(wxCommandEvent & e)
{
    DEBUG_TRACE("OnRedo");
    GlobalCmdHist::getInstance().redo();
}


void MainFrame::ShowCtrlPoint(unsigned int cpNr)
{
    DEBUG_DEBUG("Showing control point " << cpNr);
    m_notebook->SetSelection(4);
    cpe->ShowControlPoint(cpNr);
}

void MainFrame::ShowCtrlPointEditor(unsigned int img1, unsigned int img2)
{
    m_notebook->SetSelection(4);
    cpe->setLeftImage(img1);
    cpe->setRightImage(img2);
}


/** update the display */
void MainFrame::updateProgressDisplay()
{
    wxString msg;
    // build the message:
    for (std::vector<ProgressTask>::reverse_iterator it = tasks.rbegin();
                 it != tasks.rend(); ++it)
    {
        wxString cMsg;
        if (it->getProgress() > 0) {
            cMsg.Printf(wxT("%s %s [%3.0f%%]"),
                        wxString(it->getShortMessage().c_str(), wxConvLocal).c_str(),
                        wxString(it->getMessage().c_str(), wxConvLocal).c_str(),
                        100 * it->getProgress());
        } else {
            cMsg.Printf(wxT("%s %s"),wxString(it->getShortMessage().c_str(), wxConvLocal).c_str(),
                        wxString(it->getMessage().c_str(), wxConvLocal).c_str());
        }
        // append to main message
        if (it == tasks.rbegin()) {
            msg = cMsg;
        } else {
            msg.Append(wxT(" | "));
            msg.Append(cMsg);
        }
    }
    wxStatusBar *m_statbar = GetStatusBar();
    DEBUG_TRACE("Statusmb : " << msg.mb_str(wxConvLocal));
    m_statbar->SetStatusText(msg,0);

#ifdef __WXMSW__
    UpdateWindow(NULL);
#else
    // This is a bad call.. we just want to repaint the window, instead we will
    // process user events as well :( Unfortunately, there is not portable workaround...
    //wxYield();
#endif
}

void MainFrame::enableTools(bool option)
{
    {
	wxToolBar* theToolBar = GetToolBar();
	theToolBar->EnableTool(XRCID("action_optimize"), option);
	theToolBar->EnableTool(XRCID("ID_SHOW_PREVIEW_FRAME"), option);
	theToolBar->EnableTool(XRCID("ID_SHOW_GL_PREVIEW_FRAME"), option);
	//theToolBar->EnableTool(XRCID("action_save_project"), option);
	//theToolBar->EnableTool(XRCID("action_save_as_project"), option);
    }
    {
	wxMenuBar* theMenuBar = GetMenuBar();
	theMenuBar->Enable(XRCID("action_optimize"), option);
	theMenuBar->Enable(XRCID("action_finetune_all_cp"), option);
	theMenuBar->Enable(XRCID("ID_SHOW_PREVIEW_FRAME"), option);
	theMenuBar->Enable(XRCID("ID_SHOW_GL_PREVIEW_FRAME"), option);
	//theMenuBar->Enable(XRCID("action_save_project"), option);
	//theMenuBar->Enable(XRCID("action_save_as_project"), option);
	//theMenuBar->Enable(XRCID("action_save_as_ptstitcher"), option);
    }
}


void MainFrame::OnSize(wxSizeEvent &e)
{
    wxSize sz = this->GetSize();
    wxSize csz = this->GetClientSize();
    wxSize vsz = this->GetVirtualSize();
    DEBUG_TRACE(" size:" << sz.x << "," << sz.y <<
                " client: "<< csz.x << "," << csz.y <<
                " virtual: "<< vsz.x << "," << vsz.y);

    Layout();
    // redo layout if requested
    if (m_doRestoreLayout) {
        m_doRestoreLayout = false;
        // first, layout everything. This should ensure that the
        // children have the correct size
        // then restore the layout
        RestoreLayout();
    }
    e.Skip();
}

void MainFrame::RestoreLayoutOnNextResize()
{
    cout << "MainFrame::RestoreLayoutOnNextResize()" << endl;
    m_doRestoreLayout = true;
    //crop_panel->RestoreLayoutOnNextResize();
    //images_panel->RestoreLayoutOnNextResize();
}

const wxString & MainFrame::GetXRCPath()
{
     return huginApp::Get()->GetXRCPath();
};

/// hack.. kind of a pseudo singleton...
MainFrame * MainFrame::Get()
{
    if (m_this) {
        return m_this;
    } else {
        DEBUG_FATAL("MainFrame not yet created");
        DEBUG_ASSERT(m_this);
        return 0;
    }
}

void MainFrame::resetProgress(double max)
{
    m_progressMax = max;
    m_progress = 0;
    m_progressMsg = wxT("");
}

bool MainFrame::increaseProgress(double delta)
{
    m_progress += delta;

    // build the message:
    int percentage = (int) floor(m_progress/m_progressMax*100);
    if (percentage > 100) percentage = 100;

    return displayProgress();
}

bool MainFrame::increaseProgress(double delta, const std::string & msg)
{
    m_progress += delta;
    m_progressMsg = wxString(msg.c_str(), wxConvLocal);

    return displayProgress();
}


void MainFrame::setMessage(const std::string & msg)
{
    m_progressMsg = wxString(msg.c_str(), wxConvLocal);
}

bool MainFrame::displayProgress()
{
    // build the message:
    int percentage = (int) floor(m_progress/m_progressMax*100);
    if (percentage > 100) percentage = 100;

    wxStatusBar *statbar = GetStatusBar();
    statbar->SetStatusText(wxString::Format(wxT("%s: %d%%"),m_progressMsg.c_str(), percentage),0);
#ifdef __WXMSW__
    UpdateWindow(NULL);
#else
    // This is a bad call.. we just want to repaint the window, instead we will
    // process user events as well :( Unfortunately, there is not portable workaround...
    //wxYield();
#endif
    return true;
}

wxString MainFrame::getProjectName()
{
    return m_filename;
}

bool getLensDataFromUser(wxWindow * parent, SrcPanoImage & srcImg,
                         double & focalLength, double & cropFactor)
{
    // display lens dialog
    HFOVDialog dlg(parent, srcImg, focalLength, cropFactor);
    dlg.CenterOnParent();
    int ret = dlg.ShowModal();
    if (ret == wxID_OK) {
        // assume a cancel dialog.
        srcImg = dlg.GetSrcImage();
        if (dlg.GetCropFactor() <= 0) {
            srcImg.setExifCropFactor(1);
        }
        return true;
    } else {
        return false;
    }
}

MainFrame * MainFrame::m_this = 0;

