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

/** @file RunStitchPanel.cpp
 *
 *  @author Pablo d'Angelo <pablo.dangelo@web.de>
 *
 *  $Id: hugin_stitch_project.cpp 2705 2008-01-27 19:56:06Z ippei $
 *
 *  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, see
 *  <http://www.gnu.org/licenses/>.
 *
 */

#include <hugin_config.h>
#include "panoinc_WX.h"
#include "panoinc.h"

#include <wx/wfstream.h>
#include <wx/stdpaths.h>

#include <fstream>
#include <sstream>
#include <vigra/error.hxx>
#include "base_wx/huginConfig.h"
#include "base_wx/MyExternalCmdExecDialog.h"
#include "base_wx/Executor.h"
#include "base_wx/AssistantExecutor.h"
#include "base_wx/StitchingExecutor.h"

#include "base_wx/platform.h"
#include "base_wx/wxPlatform.h"
#include "base_wx/wxutils.h"

#include "RunStitchPanel.h"

#include "hugin/config_defaults.h"

// ==========================================
// Implementation of stitch window

// event ID's for RunStitchPanel
enum
{
    ID_Quit = 1,
    ID_About   
};

RunStitchPanel::RunStitchPanel(wxWindow * parent)
    : wxPanel(parent)
{
	m_paused=false;
	m_overwrite=false;
    /*
    wxMenu *menuFile = new wxMenu;

    menuFile->AppendSeparator();
    menuFile->Append( ID_Quit, _("E&xit") );

    wxMenu *menuHelp = new wxMenu;
    menuHelp->Append( ID_About, _("&About...") );

    wxMenuBar *menuBar = new wxMenuBar;
    menuBar->Append( menuFile, _("&File") );
    menuBar->Append( menuHelp, _("&Help") );
    SetMenuBar( menuBar );
    */

    wxBoxSizer * topsizer = new wxBoxSizer( wxVERTICAL );
    m_execPanel = new MyExecPanel(this);

    topsizer->Add(m_execPanel, 1, wxEXPAND, 0);
    SetSizer( topsizer );
//    topsizer->SetSizeHints( this );   // set size hints to honour minimum size
    Bind(wxEVT_END_PROCESS, &RunStitchPanel::OnProcessTerminate, this);
}

bool RunStitchPanel::StitchProject(const wxString& scriptFile, const wxString& outname, const wxString& userDefinedOutput)
{
    DEBUG_TRACE("");
    wxFileName fname(scriptFile);
    if ( !fname.FileExists() ) {
        wxLogError( _("Could not open project file:") + scriptFile);
        return false;
    }

    wxString pathToPTO;
    wxFileName::SplitPath(scriptFile, &pathToPTO, NULL, NULL);
    pathToPTO.Append(wxFileName::GetPathSeparator());

    std::ifstream prjfile((const char *)scriptFile.mb_str(HUGIN_CONV_FILENAME));
    if (prjfile.bad()) {
        wxLogError( wxString::Format(_("could not open script: %s"), scriptFile.c_str()) );
        return false;
    }
    HuginBase::Panorama pano;
    HuginBase::PanoramaMemento newPano;
    int ptoVersion = 0;
    if (newPano.loadPTScript(prjfile, ptoVersion, (const char *)pathToPTO.mb_str(HUGIN_CONV_FILENAME))) {
        pano.setMemento(newPano);
        if (pano.getActiveImages().empty())
        {
            wxLogError(wxString::Format(_("Project %s does not contain any active images."), scriptFile.c_str()));
            return false;
        }
        HuginBase::PanoramaOptions opts = pano.getOptions();
        if (ptoVersion < 2) {
            // no options stored in file, use default arguments in config
			
			wxConfig* config = new wxConfig("hugin");  //needed for PTBatcher console application
			wxConfigBase::Set(config);                      //
			opts.enblendOptions = wxConfigBase::Get()->Read("/Enblend/Args", HUGIN_ENBLEND_ARGS).mb_str(wxConvLocal);
			opts.enfuseOptions = wxConfigBase::Get()->Read("/Enfuse/Args", HUGIN_ENFUSE_ARGS).mb_str(wxConvLocal);

        }
        opts.remapUsingGPU = wxConfigBase::Get()->Read("/Nona/UseGPU", HUGIN_NONA_USEGPU) == 1;
        pano.setOptions(opts);
    } else {
        wxLogError( wxString::Format(_("error while parsing panotools script: %s"), scriptFile.c_str()) );
        return false;
    }
    // get options and correct for correct makefile
    HuginBase::PanoramaOptions opts = pano.getOptions();
    if (!userDefinedOutput.IsEmpty())
    {
        wxFileInputStream input(userDefinedOutput);
        if (!input.IsOk())
        {
            wxLogError(wxString::Format(_("Can't open user defined output sequence \"%s\"."), userDefinedOutput.c_str()));
            return false;
        }
        wxFileConfig settings(input);
        // disable cropped output if user defined setting is requesting
        long supportsCroppedOutput;
        settings.Read("/General/SupportsCroppedTIFF", &supportsCroppedOutput, 1l);
        if (supportsCroppedOutput != 1)
        {
            opts.tiff_saveROI = false;
        };
    };
    opts.outputFormat = HuginBase::PanoramaOptions::TIFF_m;
    if (opts.enblendOptions.length() == 0) {
        // no options stored in file, use default arguments in config file
        opts.enblendOptions = wxConfigBase::Get()->Read("/Enblend/Args", HUGIN_ENBLEND_ARGS).mb_str(wxConvLocal);
    }
    pano.setOptions(opts);
    DEBUG_DEBUG("output file specified is " << (const char *)outname.mb_str(wxConvLocal));

    wxString basename;
    wxString outpath;
    wxFileName outputPrefix(outname);
    outputPrefix.MakeAbsolute();
    outpath = outputPrefix.GetPath();
    basename = outputPrefix.GetFullName();
    //get temp dir from preferences
    wxString tempDir= wxConfigBase::Get()->Read("tempDir",wxEmptyString);
    if(!tempDir.IsEmpty())
        if(tempDir.Last()!=wxFileName::GetPathSeparator())
            tempDir.Append(wxFileName::GetPathSeparator());

    try {
        // copy pto file to temporary file
        m_currentPTOfn = wxFileName::CreateTempFileName(tempDir+"huginpto_");
        if(m_currentPTOfn.empty()) {
            wxLogError(_("Could not create temporary file"));
        }
        DEBUG_DEBUG("tmpPTOfn file: " << (const char *)m_currentPTOfn.mb_str(wxConvLocal));
        // copy is not enough, need to adjust image path names...
        const std::string script(m_currentPTOfn.mb_str(HUGIN_CONV_FILENAME));
        pano.WritePTOFile(script);

        // cd to output directory, if one is given.
        m_oldCwd = wxFileName::GetCwd();
        wxFileName::SetCwd(outpath);
        const wxFileName exePath(wxStandardPaths::Get().GetExecutablePath());
        wxArrayString outputFiles;
        wxString statusText;
        m_tempFiles.clear();
        HuginQueue::CommandQueue* commands;
        std::stringstream errors;
        if (userDefinedOutput.IsEmpty())
        {
            commands = HuginQueue::GetStitchingCommandQueue(pano, exePath.GetPath(wxPATH_GET_VOLUME | wxPATH_GET_SEPARATOR), m_currentPTOfn, basename, statusText, outputFiles, m_tempFiles, errors);
        }
        else
        {
            commands= HuginQueue::GetStitchingCommandQueueUserOutput(pano, exePath.GetPath(wxPATH_GET_VOLUME | wxPATH_GET_SEPARATOR), m_currentPTOfn, basename, userDefinedOutput, statusText, outputFiles, m_tempFiles, errors);
        };
        if (commands->empty())
        {
            hugin_utils::HuginMessageBox(_("Queue is empty. This should never happen.") + "\n\n" + wxString(errors.str()), _("Hugin"), wxICON_ERROR | wxOK, this);
            return false;
        };
        // check output directories.
        wxString overwrittenFiles;
        if (!m_overwrite)
        {
            for (size_t i = 0; i < outputFiles.size(); i++)
            {
                wxString fn(outputFiles[i]);
                if (wxFile::Exists(fn))
                {
                    overwrittenFiles.Append(fn + " ");
                };
            };

            if (!overwrittenFiles.IsEmpty())
            {
                hugin_utils::MessageDialog dlg = hugin_utils::GetMessageDialog(_("Overwrite existing images?") + "\n\n" + overwrittenFiles, _("Hugin"), wxYES_NO | wxICON_QUESTION, wxGetActiveWindow());
                dlg->SetYesNoLabels(_("Overwrite files"), _("Cancel, don't overwrite files"));
                if (dlg->ShowModal() != wxID_YES)
                {
                    DEBUG_DEBUG("Abort, do not overwrite images!");
                    return false;
                }
                DEBUG_DEBUG("Overwrite existing images!");
            };
        };
        if (!statusText.empty())
        {
            m_execPanel->AddString(statusText);
        };
        if (m_execPanel->ExecQueue(commands) == -1)
        {
            hugin_utils::HuginMessageBox(wxString::Format(_("Error while stitching project\n%s"), scriptFile),
                             _("Hugin"),  wxICON_ERROR | wxOK, this);
        };
    } catch (std::exception & e)
    {
        std::cerr << "caught exception: " << e.what() << std::endl;
        hugin_utils::HuginMessageBox(wxString(e.what(), wxConvLocal),
                     _("Hugin"), wxICON_ERROR | wxOK, this);
    }
    return true;
}

bool RunStitchPanel::DetectProject(const wxString& scriptFile, const wxString& userDefinedAssistant)
{
    m_currentPTOfn=wxEmptyString;
    wxFileName fname(scriptFile);
    if ( !fname.FileExists() ) {
        wxLogError( _("Could not open project file:") + scriptFile);
        return false;
    }
    // check user defined assistant file
    if (!userDefinedAssistant.IsEmpty())
    {
        wxFileInputStream input(userDefinedAssistant);
        if (!input.IsOk())
        {
            wxLogError(wxString::Format(_("Can't open user defined output sequence \"%s\"."), userDefinedAssistant.c_str()));
            return false;
        };
    };

    // get path to project file
    wxString pathToPTO;
    wxFileName::SplitPath(scriptFile, &pathToPTO, NULL, NULL);
    pathToPTO.Append(wxFileName::GetPathSeparator());

    //read project file
    std::ifstream prjfile((const char *)scriptFile.mb_str(HUGIN_CONV_FILENAME));
    if (prjfile.bad())
    {
        wxLogError( wxString::Format(_("could not open script: %s"), scriptFile.c_str()));
        return false;
    }
    HuginBase::Panorama pano;
    HuginBase::PanoramaMemento newPano;
    int ptoVersion = 0;
    if (!newPano.loadPTScript(prjfile, ptoVersion, (const char *)pathToPTO.mb_str(HUGIN_CONV_FILENAME)))
    {
        wxLogError(wxString::Format(_("error while parsing panotools script: %s"), scriptFile.c_str()));
        return false;
    }
    pano.setMemento(newPano);

    //read settings
    wxString tempDir= wxConfigBase::Get()->Read("tempDir",wxEmptyString);
    if (!tempDir.IsEmpty())
    {
        if (tempDir.Last() != wxFileName::GetPathSeparator())
        {
            tempDir.Append(wxFileName::GetPathSeparator());
        };
    };

    try {
        // prepare running assistant
        fname.Normalize(wxPATH_NORM_ABSOLUTE | wxPATH_NORM_DOTS | wxPATH_NORM_TILDE | wxPATH_NORM_SHORTCUT);
        const wxFileName exePath(wxStandardPaths::Get().GetExecutablePath());
        HuginQueue::CommandQueue* commands;
        if (userDefinedAssistant.IsEmpty())
        {
            commands = HuginQueue::GetAssistantCommandQueue(pano, exePath.GetPath(wxPATH_GET_VOLUME | wxPATH_GET_SEPARATOR), fname.GetFullPath());
        }
        else
        {
            std::stringstream errors;
            commands = HuginQueue::GetAssistantCommandQueueUserDefined(pano, exePath.GetPath(wxPATH_GET_VOLUME | wxPATH_GET_SEPARATOR), fname.GetFullPath(), userDefinedAssistant, m_tempFiles, errors);
        };
        if (commands->empty())
        {
            hugin_utils::HuginMessageBox(_("Queue is empty. This should never happen."), _("Hugin"), wxICON_ERROR | wxOK, this);
            return false;
        };
        if (m_execPanel->ExecQueue(commands) == -1)
        {
            hugin_utils::HuginMessageBox(wxString::Format(_("Error while running assistant\n%s"), scriptFile),
                         _("Hugin"),  wxICON_ERROR | wxOK, this);
        }
    } 
    catch (std::exception & e)
    {
        std::cerr << "caught exception: " << e.what() << std::endl;
        hugin_utils::HuginMessageBox(wxString(e.what(), wxConvLocal),
                     _("Hugin"), wxICON_ERROR | wxOK, this);
    }
    return true;
}

void RunStitchPanel::OnProcessTerminate(wxProcessEvent & event)
{
    DEBUG_TRACE("");
#ifndef DEBUG
    if(!m_currentPTOfn.IsEmpty())
    {
        wxRemoveFile(m_currentPTOfn);
    };
#endif
    // delete all temp files
    if (!m_tempFiles.empty())
    {
        for (size_t i = 0; i < m_tempFiles.size(); ++i)
        {
            if (wxFileExists(m_tempFiles[i]))
            {
                wxRemoveFile(m_tempFiles[i]);
            };
        };
    };
    // restore old working directory, if changed
    if (!m_oldCwd.IsEmpty())
    {
        wxFileName::SetCwd(m_oldCwd);
    };
    // notify parent of exit
    if (this->GetParent())
    {
        event.SetEventObject( this );
        DEBUG_TRACE("Sending wxProcess event");   
        this->GetParent()->GetEventHandler()->ProcessEvent( event );
    }
}

void RunStitchPanel::CancelStitch()
{
    DEBUG_TRACE("");
    m_execPanel->KillProcess();
}

bool RunStitchPanel::IsPaused()
{
	return m_paused;
}

void RunStitchPanel::SetOverwrite(bool over)
{
	m_overwrite = over;
}

void RunStitchPanel::PauseStitch()
{
	m_paused=true;
	m_execPanel->PauseProcess();
}

void RunStitchPanel::ContinueStitch()
{
	m_execPanel->ContinueProcess();
	m_paused=false;
}

long RunStitchPanel::GetPid()
{
	return m_execPanel->GetPid();
}

bool RunStitchPanel::SaveLog(const wxString &filename)
{
    return m_execPanel->SaveLog(filename);
};
