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

 Copyright 2012 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 <QDebug>

#include <Assertions.hpp>
#include "GenAppService.hpp"
#include "MgQSceneItem.h"
#include "MvQApplication.h"
#include "MvQLayerContentsIcon.h"
#include "MvQLayerDialog.h"
#include "MvPath.hpp"
#include "MvRequestUtil.hpp"
#include "ObjectList.h"
#include "../libMetview/Path.h"  // path given to disambiguate from Magics Path.h
#include "Root.h"
#include "uPlotBase.h"


MvQLayerContentsIcon::MvQLayerContentsIcon(const QString& path, const QString& type, const QString& id, bool fromMacro) :
    visibility_(true),
    canBeDeleted_(false),
    canBeEdited_(false),
    canBeSaved_(false),
    canBeSavedAs_(false),
    isData_(false)
{
    // Initialise first variables
    path_            = path;
    type_            = type;
    id_              = id;
    calledFromMacro_ = fromMacro;

    //qDebug() << "Constructor MvQLayerContentsIcon " << path_ << type_ << id_;

    QStringList lst = path_.split("/");
    if (!lst.isEmpty())
        name_ = lst.last();

    // Get the pixmap full path&name and update the interface
    std::string fullName = this->getPixmapPath(type_.toStdString());
    if (!fullName.empty()) {
        std::size_t found = fullName.find_last_of(".");
        if (found != std::string::npos) {
            QString name = QString::fromStdString(fullName.substr(0, found));
            char* suffix = strdup(fullName.substr(found + 1).c_str());
            // const char* suffix = fullName.substr(found+1).c_str();  never use this because the result is compiler dependent.
            pix_ = QPixmap::fromImage(QImage(name, suffix)).scaled(QSize(24, 24), Qt::KeepAspectRatio, Qt::SmoothTransformation);
            free(suffix);
        }
    }

    // Set icon permissions
    // Icon in this format, <icon_name> , means that this icon is a default
    // icon and it does not exist in directory ~/System/Defaults (Metview
    // icons default directory). Therefore, it can not be saved.
    std::string sclass = type_.toStdString();
    if (ObjectList::IsDataUnit(sclass.c_str())) {
        canBeDeleted_ = true;
        canBeEdited_  = false;
        canBeSaved_   = false;
        canBeSavedAs_ = false;
        isData_       = true;
    }
    else if (ObjectList::IsVisDef(sclass.c_str()) ||
             ObjectList::IsVisDefText(sclass.c_str()) ||
             ObjectList::IsVisDefLegend(sclass.c_str()) ||
             ObjectList::IsVisDefImport(sclass.c_str())) {
        canBeDeleted_ = true;
        canBeEdited_  = true;
        canBeSaved_   = (name_[0] == '<' && name_[name_.size() - 1] == '>') ? false : true;
        canBeSavedAs_ = true;
    }
    else if (ObjectList::IsVisDefCoastlines(sclass.c_str())) {
        canBeDeleted_ = true;
        canBeEdited_  = false;
        canBeSaved_   = false;
        canBeSavedAs_ = false;
    }
    else if (ObjectList::IsView(sclass.c_str())) {
        // FAMI20160929: Edit and Save options are currently
        // disabled because the current version can not handle
        // embeded icons. Investigate the use of script mv_compress
        canBeDeleted_ = false;
        canBeEdited_  = false;
        canBeSaved_   = false;  //( name_[0] == '<' && name_[name_.size()-1] == '>' ) ? false : true;
        canBeSavedAs_ = false;
    }
    else {
        canBeDeleted_ = false;
        canBeEdited_  = false;
        canBeSaved_   = false;
        canBeSavedAs_ = false;
    }

    // If an icon came from a Macro then it can not be edited
    if (calledFromMacro_)
        canBeSaved_ = false;
}

MvQLayerContentsIcon::~MvQLayerContentsIcon() = default;

MvQLayerContentsIcon& MvQLayerContentsIcon::operator=(const MvQLayerContentsIcon& icon)
{
    path_            = icon.path_;
    type_            = icon.type_;
    id_              = icon.id_;
    name_            = icon.name_;
    pix_             = icon.pix_;
    calledFromMacro_ = icon.calledFromMacro_;

    canBeDeleted_ = icon.canBeDeleted_;
    canBeEdited_  = icon.canBeEdited_;
    canBeSaved_   = icon.canBeSaved_;
    canBeSavedAs_ = icon.canBeSavedAs_;
    isData_       = icon.isData_;

    return *this;
}

bool MvQLayerContentsIcon::operator==(const MvQLayerContentsIcon& icon)
{
    return (path_ == icon.path_ && type_ == icon.type_);
}

void MvQLayerContentsIcon::startEditor()
{
    if (!canBeEdited_)
        return;

    // Get icon request to be edited.
    // Although the Desktop Editor can create a copy of this icon at the
    // Temporary Directory, it can not be used because it does not have
    // the hidden parameters. Mars function save_all_requests only saves
    // the hidden parameters if Metview is running in the debug mode.
    // The solution is to use the icon from the DataBase, but there is a
    // need to update parameter _NAME to point to the uPlot Temporary
    // Directory; otherwise, the Desktop Editor will save the changes to
    // the original icon.

    // Get Presentable SuperPage and the DataBase
    Presentable* pres        = Root::Instance().FindSuperPage();
    MvIconDataBase& dataBase = pres->IconDataBase();

    // Retrieve icon from the DataBase
    MvIcon mvIcon;
    if (!dataBase.FindIcon(id_.toInt(), mvIcon))
        return;  // icon not found

    // Add parameter indicating the name of the uPlot Temporary Directory.
    // This will be used by the Desktop  Editor
    MvRequest req        = mvIcon.Request();
    std::string uTempDir = uPlotBase::tempDir();
    if (uTempDir[uTempDir.size() - 1] == '/')  // remove last '/'
        uTempDir = uTempDir.substr(0, uTempDir.size() - 1);

    std::size_t ipos       = uTempDir.find_last_of("/");
    std::string uTempName  = (ipos != std::string::npos) ? uTempDir.substr(ipos + 1) : uTempDir;
    req("_UPLOT_TEMP_DIR") = uTempName.c_str();

    // Save information stored in the hidden parameters because, after this
    // icon is edited, the request sent back to function "edited" does not
    // contain them.
    // Save name
    if (const char* chv = req("_NAME_ORIGINAL"))
        nameOrg_ = std::string(chv);
    else if (const char* chv = req("_NAME"))
        nameOrg_ = std::string(chv);

    // probably needs to save more hidden parameters here

    // If this is the first time that this icon is edited then create a
    // temporary filename which points to the uPlot Temporary Directory.
    // The Desktop editor will use this temporary file.
    if (!(const char*)req("_CONTENTS_ICON_EDITED")) {
        // Get full filename to be edited (temporary directory + filename)
        std::string fname = uPlotBase::tempDir() + name_.toStdString();

        // Parameter _NAME is the full filename without the Metview user directory
        std::string userDir = GetUserDirectory();  // Metview user directory
        std::size_t ipos    = fname.find(userDir);
        ipos                = (ipos != std::string::npos) ? ipos + userDir.size() : 0;
        req("_NAME")        = fname.substr(ipos).c_str();

        // If this is a default icon then remove the _DEFAULT parameter.
        // A new clone icon will be created in the temporary directory
        // and the system should edit this icon. The problem here is that
        // if this parameter is not removed then the Desktop module will
        // edit the default icon stored at /System/Defaults.
        if ((const char*)req("_DEFAULT"))
            req.unsetParam("_DEFAULT");

        // Create an icon file on disk, directory uPlot Temporary Directory.
        // Desktop editor will save the user changes in this file.
        Path pnewIcon(fname);
        pnewIcon.saveRequest(req);
    }

    // Ask GenApp to edit my request
    CallGenAppService(*this, &MvQLayerContentsIcon::edited, req);
}

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

    // Restore some hidden parameters that were saved in the function
    // "startEditor". Unfortunately, these parameters are not sent back
    // by the Desktop Editor
    newIconRequest("_NAME_ORIGINAL")        = nameOrg_.c_str();
    newIconRequest("_ID")                   = id_.toInt();
    newIconRequest("_CONTENTS_ICON_EDITED") = id_.toInt();
    newIconRequest("_CALLED_FROM_MACRO")    = calledFromMacro_;

    // Create a new drop request
    // Get the target Presentable (Page) id
    int ivoid;  // void variable
    MvRequest dropRequest("DROP");
    Presentable* pres        = Root::Instance().FindSuperPage();
    dropRequest("DROP_ID")   = pres->FindBranchIdByIconId(id_.toInt(), ivoid);
    dropRequest("_CONTENTS") = 1;

    dropRequest = dropRequest + newIconRequest;

    // Call myself to process the request
    std::ostringstream appName;
    appName << "uPlot" << (long int)getpid();
    MvApplication::callService(appName.str().c_str(), dropRequest, nullptr);
}

void MvQLayerContentsIcon::deleteIcon()
{
    if (!canBeDeleted_)
        return;

    // Get Presentable SuperPage
    Presentable* pres = Root::Instance().FindSuperPage();

    // Delete icon and rebuild the plot
    pres->RemoveIconWithId(id_.toInt());
}

void MvQLayerContentsIcon::saveIcon()
{
    if (!canBeSaved_)
        return;

    // Get Presentable SuperPage
    Presentable* pres = Root::Instance().FindSuperPage();

    // Retrieve icon from the DataBase and update it on disk
    MvIcon mvIcon;
    MvIconDataBase& dataBase = pres->IconDataBase();
    if (dataBase.FindIcon(id_.toInt(), mvIcon)) {
        // Update icon on disk
        MvRequest iconReq = mvIcon.Request();
        const char* cname = (const char*)iconReq("_NAME_ORIGINAL") ? (const char*)iconReq("_NAME_ORIGINAL") : (const char*)iconReq("_NAME");
        if (!cname) {
            marslog(LOG_WARN, "Icon file not available on disk");
            return;
        }
        std::string fullPath = MakeUserPath(string(cname));

        // Remove all hiden parameters before saving the icon
        metview::RemoveParameters(iconReq, "_");
        iconReq.save(fullPath.c_str());

        // Notify everyone
        //MvApplication::notifyIconModified(icon_.IconName(),icon_.IconClass());
    }
}

void MvQLayerContentsIcon::saveAsIcon()
{
    if (!canBeSavedAs_)
        return;

    // Show dialog
    QString startDir;
    if (uPlotBase::owner()) {
        startDir =  QString::fromStdString(ObjectInfo::ObjectFullPath(uPlotBase::owner()->Request()));
    }

    MvQLayerDialog layerDialog(id_.toInt(), startDir);

    if (!layerDialog.exec())
        return;

    return;
}

// This function assumes that:
// a) icons are located in the subdirectory /icons_mv5/
// b) File ObjectList stores the pixmap filenames's suffix as .icon
//
// This function should be updated after icons are moved to subdirectory /icons/
// and the pixmap filenames' suffix are updated to .svg or .xpm
std::string MvQLayerContentsIcon::getPixmapPath(const std::string& sclass)
{
    // Get the pixmap file name given the class name
    std::string fname = ObjectList::FindPlusChildren("object", sclass, "pixmap");
    if (fname.empty())
        return fname;

    // Get path and filename
    std::size_t pos      = fname.find_last_of("/\\");
    std::string path     = fname.substr(0, pos);
    std::string filename = fname.substr(pos + 1);

    // Remove suffix .icon from the filename
    pos = filename.rfind(".icon");
    if (pos != std::string::npos)
        filename = filename.substr(0, pos);

    // Replace subdirectoy /icons by /icons_mv5
    path += "_mv5/";

    // Build the pixmap filename. Try to find the filename suffix.
    std::string fullName = path + filename + ".svg";
    if (FileCanBeOpened(fullName.c_str(), "r"))
        return fullName;
    else {
        fullName = path + filename + ".xpm";
        if (FileCanBeOpened(fullName.c_str(), "r"))
            return fullName;
    }

    return {};
}
