/***************************** 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 <QScrollArea>
#include <QVBoxLayout>
#include <QTabWidget>
#include <QLabel>
#include <QSettings>
#include <QDialogButtonBox>
#include <QGroupBox>
#include <QMessageBox>

#include <QHBoxLayout>

#include "ExportDialog.h"
#include "GenAppService.hpp"
//#include "MvQGui.h"
#include "MvPath.hpp"
#include "MvRequestUtil.hpp"
#include "ObjectList.h"
#include "MvQMethods.h"
#include "uPlot.h"


ExportDialog::ExportDialog(int currentFrame, int totalFrames, QWidget* parent) :
    QFileDialog(parent, "Export"),
    current_(currentFrame),
    total_(totalFrames),
    comboBox_(0),
    leFs3_(0),
    cbFs1_(0),
    cbFs2_(0),
    cbFs3_(0)
{
    // Set widget mode behaviour to "Save As"
    this->setAcceptMode(QFileDialog::AcceptSave);

    // Setting for Mac OS X
    setOption(QFileDialog::DontUseNativeDialog, true);

    // Restore previous gui settings
    QSettings settings(MVQ_QSETTINGS_DEFAULT_ARGS, "uPlotExportDialog");
    settings.beginGroup("main");
    selectFile(settings.value("fileName").toString());
    int fIndex = settings.value("formatIndex").toInt();
    settings.endGroup();

    // Set filter options to select which files to be shown
#ifdef METVIEW_QT5
    QStringList filters;
    filters << "All Files (*.*)"
            << "Images (*.png *.ps *.eps *.jpg *.kml *.gif *.svg *.pdf)";
    this->setNameFilters(filters);
#else
    this->setFilter(tr("All Files (*.*);;Images (*.png *.ps *.eps *.jpg *.kml *.gif *.svg *.pdf)"));
#endif

    // Get the main layout
    QGridLayout* mainLayout = static_cast<QGridLayout*>(layout());
    int row                 = mainLayout->rowCount();
    int col                 = mainLayout->columnCount();

    // Build label for the Output format
    QLabel* label = new QLabel(this);
    label->setText(tr("Output format:"));
    mainLayout->addWidget(label, row, 0);

    // Retrieve and save the list of output formats from the resources file
    comboBox_ = new QComboBox(this);
    ObjectList::FindAll("output_format", reqFormats_);
    int index = 0;
    while (reqFormats_) {
        // Add output format to the user interface
        comboBox_->insertItem(index++, (const char*)reqFormats_("class"));
        reqFormats_.advance();
    }
    comboBox_->setCurrentIndex(fIndex);

    // Build the Edit button
    bEdit_ = new QToolButton();
    QIcon icon;
    icon.addPixmap(QPixmap(QString::fromUtf8(":/uPlot/configure.svg")), QIcon::Normal, QIcon::Off);
    bEdit_->setIcon(icon);
    bEdit_->setToolTip(tr("Edit selected format"));
    bEdit_->setPopupMode(QToolButton::InstantPopup);

    // Buil a horizontal layout to hold the combobox and the toolbutton
    QHBoxLayout* hb = new QHBoxLayout;
    hb->addWidget(comboBox_);
    hb->addWidget(bEdit_);
    mainLayout->addLayout(hb, row, 1, 1, 1);

    connect(bEdit_, SIGNAL(clicked()), this, SLOT(slotEditFormat()));

    // Build Frame Selection widget
    buildFrameSelection(row - 2, col - 1, mainLayout);

    // Move Save and Cancel buttons to the bottom.
    // Save and Cancel buttons are defined together in a QDialogButtonBox.
    // Therefore, only plays with one of the buttons.
    row++;
    QLayoutItem* liCancel = mainLayout->itemAtPosition(row - 3, col - 1);
    mainLayout->removeItem(liCancel);
    QDialogButtonBox* bb = static_cast<QDialogButtonBox*>(liCancel->widget());
    bb->setOrientation(Qt::Horizontal);
    mainLayout->addItem(liCancel, row, col - 1);
}

// Destructor
ExportDialog::~ExportDialog()
{
    // Save current gui settings
    QSettings settings(MVQ_QSETTINGS_DEFAULT_ARGS, "uPlotExportDialog");
    settings.beginGroup("main");
    settings.setValue("fileName", selectedFiles());

    int fIndex = comboBox_->currentIndex();
    settings.setValue("formatIndex", fIndex);
    settings.endGroup();

    delete comboBox_;
    delete bEdit_;
    delete leFs3_;
    delete cbFs1_;
    delete cbFs2_;
    delete cbFs3_;
}

// Callback from the SAVE button
void ExportDialog::accept()
{
    // This function is also called if the user selected a filename
    // from the treeView. If this is the case just continue.
    if (sender()->objectName() == "treeView")
        return;

    // Get filename from the user interface
    QStringList s   = selectedFiles();
    string filename = s.at(0).toStdString();

    // Get output format name from the user interface
    string format = comboBox_->currentText().toStdString();

    // Get the output format request from the Metview/Defaults directory.
    // If it does not exist, create a default request.
    // Get the output format verb name
    const char* verb = this->getFormatVerb(format);
    if (!verb) {
        ostringstream info;
        info << "Format name not found: " << format << ends;
        errorMessage((const char*)info.str().c_str());
        return;
    }

    // Read the selected output format request from the Metview/Defaults directory.
    MvRequest reqOut = ObjectList::UserDefaultRequest(verb);

    // Add output filename
    reqOut("OUTPUT_NAME") = filename.c_str();

    // Get frames selection
    if (!getFrameSelection(reqOut))
        return;

    // Send export info to be processed
    emit sendExpDialogInfo(&reqOut);

    // Close the window
    int res = 0;
    done(res);
    //  hide();
}

void ExportDialog::reject()
{
    hide();
}

void ExportDialog::slotEditFormat()
{
    // Get short format name
    string format = comboBox_->currentText().toStdString();

    // Get output format request
    MvRequest reqFormat = ObjectList::Find("output_format", format.c_str());

    // 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")) {
        errorMessage("Parameter _NAME not found!", 0);
        return;
    }

    // If it is a new default request, then remove characters '<' and '>'
    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
    string userDir    = GetUserDirectory();
    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
    string filename = path + '/' + name;
    reqOut("_NAME") = filename.c_str();

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

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

    // Nothing to be done. The edited file will be read when it is needed.
}

void ExportDialog::slotRangeFrameSelection(bool checked)
{
    // Enable/Disable line edit
    leFs3_->setEnabled(checked);
}

void ExportDialog::buildFrameSelection(int row, int col, QGridLayout* mainLayout)
{
    // Group Box with radio buttons and one line edit
    QGroupBox* gbFs = new QGroupBox(tr("Frame Selection"));
    cbFs1_          = new QRadioButton(tr("&Current"));
    cbFs2_          = new QRadioButton(tr("&All"));
    cbFs3_          = new QRadioButton(tr("&Range"));
    cbFs1_->setChecked(true);

    // Line edit with a validator
    leFs3_ = new QLineEdit();
    leFs3_->setToolTip(tr("Specify one or more frame ranges, e.g. 1-3,6,7"));
    leFs3_->setEnabled(false);
    QRegExp rx("^\\d{1,}(-\\d{1,})?(,\\d{1,}|,\\d{1,}-\\d{1,})*");
    QValidator* validator = new QRegExpValidator(rx, this);
    leFs3_->setValidator(validator);

    // Set layout
    QGridLayout* glFs = new QGridLayout(gbFs);
    glFs->addWidget(cbFs1_, 0, 0);
    glFs->addWidget(cbFs2_, 1, 0);
    glFs->addWidget(cbFs3_, 2, 0);
    glFs->addWidget(leFs3_, 2, 1);

    // Conexions
    connect(cbFs3_, SIGNAL(toggled(bool)), this, SLOT(slotRangeFrameSelection(bool)));

    // Attach widget to the parent
    mainLayout->addWidget(gbFs, row, col, 3, 1);

    return;
}

bool ExportDialog::getFrameSelection(MvRequest& req)
{
    // Three options: CURRENT, ALL, RANGE
    // ALL: nothing need to be done
    if (cbFs2_->isChecked())
        return true;

    // CURRENT: get the current frame numbeer
    int i;
    QList<int> list;
    if (cbFs1_->isChecked()) {
        list.append(current_ + 1);  // start from 1, not from 0
    }
    else  // RANGE
    {
        // Get the list of range entries (split by comma)
        QStringList list1 = leFs3_->text().split(",", QString::SkipEmptyParts);

        // Analyse all entries
        for (i = 0; i < list1.size(); i++) {
            // Check if it is a single value or a range of values
            QStringList list2 = list1.at(i).split("-", QString::SkipEmptyParts);
            if (list2.size() == 1)  // single value
                list.append(list2.at(0).toInt());
            else {
                int i1 = list2.at(0).toInt();
                int i2 = list2.at(1).toInt();
                if (i1 > i2)  // invert sequence
                {
                    int iaux = i1;
                    i1       = i2;
                    i2       = iaux;
                }
                for (int j = i1; j <= i2; j++)
                    list.append(j);
            }
        }

        // Check if the range of values is valid
        qSort(list.begin(), list.end());
        if (list.at(list.size() - 1) > total_) {
            ostringstream info;
            info << "Enter values from 1 to " << total_ << ends;
            errorMessage("Invalid range!", (const char*)info.str().c_str());
            return false;
        }
    }

    // Save list of frames selection for all drivers
    while (req) {
        for (i = 0; i < list.size(); i++)
            req.addValue("OUTPUT_FRAME_LIST", list.at(i));

        req.advance();
    }

    req.rewind();

    return true;
}

void ExportDialog::errorMessage(const char* msg, const char* info)
{
    QMessageBox msgBox;
    msgBox.setIcon(QMessageBox::Critical);
    msgBox.setWindowTitle(tr("Export Error"));
    msgBox.setText(QString::fromUtf8(msg));
    if (info)
        msgBox.setInformativeText(QString::fromUtf8(info));

    msgBox.exec();
}

const char* ExportDialog::getFormatVerb(string shortName)
{
    reqFormats_.rewind();
    while (reqFormats_) {
        if ((const char*)reqFormats_("class") == shortName)
            return (const char*)reqFormats_("output");

        reqFormats_.advance();
    }

    return 0;  // not found
}
