/***************************** LICENSE START ***********************************

 Copyright 2017 ECMWF and INPE. This software is distributed under the terms
 of the Apache License version 2.0. In applying this license, ECMWF does not
 waive the privileges and immunities granted to it by virtue of its status as
 an Intergovernmental Organization or submit itself to any jurisdiction.

 ***************************** LICENSE END *************************************/

#include "ShareTargets.h"

#include <fstream>
#include <string>

#include "fstream_mars_fix.h"

#include "GenAppService.hpp"
#include "MvPath.hpp"
#include "MvQProgressManager.h"
#include "ObjectList.h"
#include "PlotMod.h"
#include "PlotModConst.h"
#include "MvLog.h"

#include <QFileSystemWatcher>

/* --------------------------------------------------
 * ShareTargetManager - the registry of share targets
 * -------------------------------------------------- */

ShareTargetManager* ShareTargetManager::instance_ = nullptr;

ShareTargetManager* ShareTargetManager::instance()
{
    if (!instance_)
        instance_ = new ShareTargetManager();

    return instance_;
}

/* ---------------------------------------------------------
 * ShareTarget - the base class of each type of share target
 * --------------------------------------------------------- */

ShareTarget::ShareTarget(const std::string& name, const std::string& icon) :
    pm_(nullptr),
    name_(name),
    icon_(icon)
{
    // Register self to the list of available share targets
    ShareTargetManager::instance()->push_back(this);
}

/* ------------------------------------------------------
 * GlobeShareTarget - specific share target for the globe
 * ------------------------------------------------------ */
//#ifdef MV_SHARE_GLOBE_BUILD

GlobeShareTarget::GlobeShareTarget(QObject* parent) :
    QObject(parent),
    ShareTarget("Weather Globe", "globe.svg")
{
}

GlobeShareTarget::~GlobeShareTarget() = default;

bool GlobeShareTarget::dialog(MvRequest& reqPlot, MvQProgressManager* pm)
{
    // Save request. It will be used later in the plotting routine
    if (!reqPlot) {
        MvLog().popup().err() << "WEATHER GLOBE: Internal Error: empty Display Window";
        return false;
    }
    reqPlot_ = reqPlot;

    // GlobeShareTarget is in charge to delete pm
    if (pm_)
        delete pm_;

    pm_ = pm;

    // Get output format request
    MvRequest reqFormat = ObjectList::Find("output_format", class_.c_str());
    if (std::string(reqFormat.getVerb()) == std::string("EMPTY")) {
        MvLog().popup().err() << "WEATHER GLOBE: Internal Error: GLOBEOutputDef file not found";
        return false;
    }

    // Read the selected output format request from the Metview/Defaults directory.
    // If not found, create a default request
    MvRequest reqOut = ObjectList::UserDefaultRequest((const char*)reqFormat("output"));

    // Parameter _NAME contains the output format filename.
    // If the file already exists, this parameter contains the absolute path.
    // Otherwise, it contains the following filename: absolute_path/<output_format_name>,
    // which needs to be replaced by relative_path/output_format_name.
    // There is a need to update this filename because the Desktop's Icon
    // Editor requires a relative path in order to be able to save the file.
    // If a relative path is not given, the Editor creates a temporary file, which
    // is not appropriate here.
    if (!(const char*)reqOut("_NAME")) {
        MvLog().popup().err() << "WEATHER GLOBE: Internal Error: parameter _NAME not available";
        return false;
    }

    // If it is a new default request, then remove characters '<' and '>'
    std::string name = mbasename((const char*)reqOut("_NAME"));
    if (name[0] == '<')  // it is a default filename, remove the brackets
        name = name.substr(1, name.size() - 2);

    // Get the prefix path (METVIEW_USER_DIRECTORY) and check it againt the filename
    std::string userDir = GetUserDirectory();
    std::string path = mdirname((const char*)reqOut("_NAME"));
    std::size_t found = path.find(userDir);
    if (found != std::string::npos)
        path = path.substr(userDir.size());  // remove the prefix path

    // Compose the filename
    std::string filename = path + '/' + name;
    reqOut("_NAME") = filename.c_str();

    // Call Metview user interface's editor
    CallGenAppService(*this, &GlobeShareTarget::edited, reqOut);

    return true;
}

void GlobeShareTarget::edited(MvRequest& reqUser)
{
    // Nothing to be edited. Button CANCEL selected by user.
    if (!reqUser)
        return;

    // Check user input parameters
    // Output file path must exist
    out_dir_ = (const char*)reqUser("OUTPUT_GLOBE_DIRECTORY");
    if (out_dir_ == "$MV_SHARE_GLOBE_PATH") {
        if ((const char*)getenv("MV_SHARE_GLOBE_PATH"))
            out_dir_ = (const char*)getenv("MV_SHARE_GLOBE_PATH");
        else {
            MvLog().popup().err() << "WEATHER GLOBE: Environment variable $MV_SHARE_GLOBE_PATH not defined";
            return;
        }
    }
    struct stat sb;
    if (stat(out_dir_.c_str(), &sb) != 0 || !S_ISDIR(sb.st_mode)) {
        MvLog().popup().err() << "WEATHER GLOBE: Directory not found: " << out_dir_;
        return;
    }

    // Read input parameters from the dialog
    out_fname_ = (const char*)reqUser("OUTPUT_GLOBE_FILE_NAME");
    out_image_width_ = (int)reqUser("OUTPUT_GLOBE_IMAGE_WIDTH");
    out_frame_rate_ = (const char*)reqUser("OUTPUT_GLOBE_FRAME_RATE");

    // Create a temporary directory to store the animation files
    // This directory will be deleted at the end
    tmp_path_ = CreateTmpPath(out_fname_.c_str());

    // Customise output request. It needs the above input parameters.
    this->customisePlotRequest();

    // Define output mp4 full filename
    // To 'watch' this file Qt requires that it already exists
    std::string out_fullname = out_dir_ + "/" + out_fname_ + ".mp4";
    std::fstream fs;
    fs.open(out_fullname.c_str(), std::ios::out);
    fs.close();

    // Define directories/files to be watched
    QFileSystemWatcher* watcher = pm_->getWatcher();
    QStringList ldirs;
    ldirs << out_fullname.c_str() << tmp_path_.c_str();
    watcher->addPaths(ldirs);

    // Define functions to be executed after:
    // a) output file is created
    // b) all temporary files have been created
    QObject::connect(watcher, SIGNAL(fileChanged(const QString&)), this, SLOT(fileChanged(const QString&)));

    QObject::connect(pm_, SIGNAL(endTempFilesCreation()), this, SLOT(upload()));

    // Show progress bar
    pm_->setVisible(true);

    // Call uPlotBatch syncronously to do the job in batch mode
    // Send a window message to start the job
    MvLog().popup().warn() << "Weather Globe: Click OK to start the creation of the following animation file:\n"
                           << out_fullname << "\nPlease note that this process may take a while. The Display Window will be available for further "
                                              "interaction, but the animation file will be based on the current visualization state.\n"
                                              "If the Display Window is closed this processing will be aborted too.\n"
                                              "Please see the Progress Bar icon above to follow this processing.";

    MvApplication::callService("uPlotBatch", reqPlot_, nullptr);
    //   QApplication::setOverrideCursor(QCursor(Qt::BusyCursor));
    //   int err;
    //   MvApplication::waitService ( "uPlotBatch", outReq, err );
    //   QApplication::restoreOverrideCursor();   // restore cursor
}

void GlobeShareTarget::customisePlotRequest()
{
    // Update SUPERPAGE and PAGE requests
    while (reqPlot_) {
        if (strcmp(reqPlot_.getVerb(), "SUPERPAGE") == 0) {
            reqPlot_("SUPER_PAGE_X_LENGTH") = 100;
            reqPlot_("SUPER_PAGE_Y_LENGTH") = 50;
        }
        else if (strcmp(reqPlot_.getVerb(), "PAGE") == 0) {
            reqPlot_("PAGE_X_LENGTH") = 100;
            reqPlot_("PAGE_Y_LENGTH") = 50;
            reqPlot_("PAGE_X_POSITION") = 0;
            reqPlot_("PAGE_Y_POSITION") = 0;

            reqPlot_("SUBPAGE_X_LENGTH") = 100;
            reqPlot_("SUBPAGE_Y_LENGTH") = 50;
            reqPlot_("SUBPAGE_X_POSITION") = 0;
            reqPlot_("SUBPAGE_Y_POSITION") = 0;
        }
        reqPlot_.advance();
    }
    reqPlot_.rewind();

    // Define output device
    std::string fullfname = tmp_path_ + "/" + out_fname_ + ".png";
    MvRequest devReq("PNGOUTPUT");
    devReq("OUTPUT_FULLNAME") = (const char*)(fullfname.c_str());
    devReq("OUTPUT_WIDTH") = out_image_width_;
    //   devReq("OUTPUT_NAME_FIRST_PAGE_NUMBER") = "OFF";
    MvRequest outReq("PRINTER_MANAGER");
    outReq("DESTINATION") = MVFILE;
    outReq("OUTPUT_DEVICES") = devReq;

    reqPlot_ = outReq + reqPlot_;
}

bool GlobeShareTarget::upload()
{
    // Define output and input filenames
    std::string out_fullname = out_dir_ + "/" + out_fname_ + ".mp4";
    std::string in_fname = tmp_path_ + "/" + out_fname_ + "*.png";

    // Call external function to create the animation
    std::string cmd = "ffmpeg -y -framerate " + out_frame_rate_ + " -f image2 -pattern_type glob -i \'";
    cmd += in_fname;
    cmd += "\' -r 30 -vcodec libx264 -crf 25 -pix_fmt yuv420p ";
    cmd += out_fullname;
    system(cmd.c_str());

    return true;
}

void GlobeShareTarget::fileChanged(const QString& /*str*/)
{
    // Send a popup window message
    std::string out_fullname = out_dir_ + "/" + out_fname_ + ".mp4";
    MvLog().popup().warn() << "The Weather Globe animation file has been created at:\n"
                           << out_fullname;

    // Remove directories/file that have been watched
    QFileSystemWatcher* watcher = pm_->getWatcher();
    QStringList ldirs;
    ldirs << out_fullname.c_str() << tmp_path_.c_str();
    watcher->removePaths(ldirs);

    // Delete temporary directory
    DeletePath(tmp_path_.c_str());

    // Delete Progress Manager
    if (pm_) {
        pm_->setVisible(false);
        delete pm_;
        pm_ = nullptr;
    }
}

// create instances of the share targets we want to enable
static GlobeShareTarget globeShareTarget;

//#endif
