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

 Copyright 2014 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 "MvQFolderWidget.h"

#include <QAction>
#include <QDebug>
#include <QDir>
#include <QStackedLayout>
#include <QVBoxLayout>

#include "MvQDetailedFolderView.h"
#include "MvQFolderHistory.h"
#include "MvQFolderModel.h"
#include "MvQFolderNavigation.h"
#include "MvQFolderViewBase.h"
#include "MvQFolderViewHandler.h"
#include "MvQFolderWatcher.h"
#include "MvQIconFolderView.h"

#include "IconClass.h"
#include "Folder.h"
#include "FolderSettings.h"
#include "Path.h"
#include "MvRequest.h"

MvQFolderWidget::MvQFolderWidget(QString expectedFolderPath, MvQFolderHistory* folderHistory,
                                 MvQActionList* iconActions,
                                 MvQActionList* desktopActions,
                                 QWidget* parent) :
    QWidget(parent),
    folderHistory_(folderHistory),
    expectedFolderPath_(expectedFolderPath)
{
    // Read folder settings
    int iconSize = FolderSettings::defaultIconSize();
    Desktop::FolderViewMode viewMode = FolderSettings::defaultViewMode();
    if (Folder* f = Folder::folder(expectedFolderPath.toStdString(), false)) {
        FolderSettings* fi = f->settings();
        viewMode = fi->viewMode();
        iconSize = fi->iconSize();
    }

    //--------------------------------------------
    // Create folder model - to speed up things we
    // set the correct iconsize
    //--------------------------------------------

    folderModel_ = new MvQFolderModel(iconSize, this);

    connect(folderModel_, SIGNAL(folderChanged(Folder*)),
            this, SLOT(slotFolderChanged(Folder*)));

    //-----------------------------
    // Layout
    //-----------------------------

    auto* mainLayout = new QVBoxLayout(this);
    mainLayout->setSpacing(0);
    mainLayout->setContentsMargins(1, 1, 1, 1);

    //------------------------------
    // Central part
    //------------------------------

    // View mode stacked widget
    centralLayout_ = new QStackedLayout;
    mainLayout->addLayout(centralLayout_, 1);

    // View handler
    views_ = new MvQFolderViewHandler(centralLayout_, folderModel_->itemProp());

    // Icon view
    auto* iconView = new MvQIconFolderView(folderModel_, iconActions, desktopActions, this);
    views_->add(Desktop::IconViewMode, iconView);

    // Simple view - the same object as icon view!!
    views_->add(Desktop::SimpleViewMode, iconView);

    // Detailed view
    auto* detailedView = new MvQDetailedFolderView(folderModel_, iconActions, desktopActions, this);
    views_->add(Desktop::DetailedViewMode, detailedView);

    foreach (QWidget* v, views_->uniqueWidgets()) {
        connect(v, SIGNAL(currentFolderChanged(Folder*)),
                this, SLOT(slotFolderReplacedInView(Folder*)));

        connect(v, SIGNAL(iconCommandRequested(QString, IconObjectH)),
                this, SIGNAL(iconCommandRequested(QString, IconObjectH)));

        connect(v, SIGNAL(desktopCommandRequested(QString, QPoint)),
                this, SIGNAL(desktopCommandRequested(QString, QPoint)));

        connect(v, SIGNAL(itemEntered(IconObject*)),
                this, SIGNAL(itemInfoChanged(IconObject*)));
    }

    // Init view mode
    views_->setCurrentMode(viewMode);

    // At this point the folder is not set on the model!!!
    // So non of the views store any icons!!!

    // Set the folder
    folderModel_->setFolder(expectedFolderPath_);
    folderNavigation_ = new MvQFolderNavigation;
    if (folderModel_->folder()) {
        folderNavigation_->add(expectedFolderPath_);

        // Add root to history
        folderHistory_->add(expectedFolderPath_);
    }
}

MvQFolderWidget::~MvQFolderWidget()
{
    MvQFolderWatcher::remove(this);

    delete views_;
}

QObject* MvQFolderWidget::realObject()
{
    return this;
}

Folder* MvQFolderWidget::currentFolder()
{
    return folderModel_->folder();
}

// This is used for the tab title
QString MvQFolderWidget::currentFolderName()
{
    // If there is a folder.
    if (folderModel_->folder())
        return QString::fromStdString(folderModel_->folder()->name());

    // The folder does not exits. This can be the case during init when the
    // stored path exists on the local disk and we start metview from another
    // machine where this path is not accessible. In this case we return the expected
    // path.
    else {
        return QDir(expectedFolderPath_).dirName();
    }


    return {};
}

QString MvQFolderWidget::currentFolderPath()
{
    if (folderModel_->folder())
        return QString::fromStdString(folderModel_->folder()->fullName());

    // The folder does not exits. This can be the case during init when the
    // stored path exists on the local disk and we start metview from another
    // machine where this path is not accessible. In this case we return the expected
    // path.
    else {
        return expectedFolderPath_;
    }
}

QString MvQFolderWidget::currentFolderFsPath()
{
    if (Folder* folder = folderModel_->folder())
        return QString::fromStdString(folder->path().str());

    // The folder does not exits. This can be the case during init when the
    // stored path exists on the local disk and we start metview from another
    // machine where this path is not accessible. In this case we return the expected
    // path.
    else {
        return expectedFolderPath_;
    }
}

//----------------------------------
//   Navigation
//----------------------------------

void MvQFolderWidget::chFolderBack()
{
    QString fullName = folderNavigation_->prev();
    if (views_->changeFolder(fullName)) {
        expectedFolderPath_.clear();
        Folder* folder = folderModel_->folder();
        if (!folder)
            return;
        folderHistory_->add(fullName);

        emit pathChanged();
    }
}

void MvQFolderWidget::chFolderForward()
{
    QString fullName = folderNavigation_->next();
    if (views_->changeFolder(fullName)) {
        expectedFolderPath_.clear();
        Folder* folder = folderModel_->folder();
        if (!folder)
            return;

        folderHistory_->add(fullName);

        emit pathChanged();
    }
}

void MvQFolderWidget::chFolderParent()
{
    Folder* folderCurrent = folderModel_->folder();
    Folder* folder = folderCurrent->parent();
    if (folder && views_->changeFolder(folder)) {
        expectedFolderPath_.clear();
        folder = folderModel_->folder();
        if (!folder)
            return;

        QString fullName = QString::fromStdString(folder->fullName());

        folderNavigation_->removeAfterCurrent();
        folderNavigation_->add(fullName);
        folderHistory_->add(fullName);

        emit pathChanged();
    }
}

void MvQFolderWidget::slotFolderReplacedInView(Folder* folder)
{
    if (!folder)
        return;

    QString fullName = QString::fromStdString(folder->fullName());
    expectedFolderPath_.clear();

    folderNavigation_->removeAfterCurrent();
    folderNavigation_->add(fullName);
    folderHistory_->add(fullName);

    emit pathChanged();
}

void MvQFolderWidget::chFolder(QString fullName)
{
    if (views_->changeFolder(fullName)) {
        expectedFolderPath_.clear();
        Folder* folder = folderModel_->folder();
        if (!folder)
            return;

        QString fullName = QString::fromStdString(folder->fullName());

        folderNavigation_->removeAfterCurrent();
        folderNavigation_->add(fullName);
        folderHistory_->add(fullName);

        emit pathChanged();
    }
}

void MvQFolderWidget::chFolderFromHistory(QString fullName)
{
    if (views_->changeFolder(fullName)) {
        expectedFolderPath_.clear();
        Folder* folder = folderModel_->folder();
        if (!folder)
            return;

        folderNavigation_->removeAfterCurrent();
        folderNavigation_->add(fullName);

        emit pathChanged();
    }
}

void MvQFolderWidget::chFolderFromBookmarks(QString fullName)
{
    if (views_->changeFolder(fullName)) {
        expectedFolderPath_.clear();
        Folder* folder = folderModel_->folder();
        if (!folder)
            return;

        folderNavigation_->removeAfterCurrent();
        folderNavigation_->add(fullName);
        folderHistory_->add(fullName);

        emit pathChanged();
    }
}

void MvQFolderWidget::chFolderFromBreadcrumbs(QString fullName)
{
    if (views_->changeFolder(fullName)) {
        expectedFolderPath_.clear();
        folderNavigation_->removeAfterCurrent();
        folderNavigation_->add(fullName);
        folderHistory_->add(fullName);

        emit pathChanged();
    }
}

// When the folder object itself is changed (e.g. renamed). It is a callback from the model!
void MvQFolderWidget::slotFolderChanged(Folder* folder)
{
    expectedFolderPath_.clear();
    slotFolderReplacedInView(folder);
}

//------------------------
// IconSize
//------------------------

int MvQFolderWidget::iconSize() const
{
    return folderModel_->iconSize();
}

void MvQFolderWidget::setIconSize(int iconSize)
{
    folderModel_->setIconSize(iconSize);
    if (Folder* f = folderModel_->folder()) {
        FolderSettings* fi = f->settings();
        fi->setIconSize(iconSize);
    }
}


//------------------------
// Snap to grid
//------------------------

void MvQFolderWidget::toGrid(Desktop::GridSortMode type)
{
    views_->currentBase()->toGrid(type);
}

void MvQFolderWidget::showLastCreated()
{
    views_->currentBase()->showLastCreated();
}

//------------------------
// Rescan
//------------------------

void MvQFolderWidget::reload()
{
    MvQFolderWatcher::reload(this);
}

void MvQFolderWidget::showEvent(QShowEvent* event)
{
    QWidget::showEvent(event);
    MvQFolderWatcher::add(this);
    // qDebug() << "show -->" << currentFolderName();
}

void MvQFolderWidget::hideEvent(QHideEvent* event)
{
    QWidget::hideEvent(event);
    MvQFolderWatcher::remove(this);
    // qDebug() << "hide -->" << currentFolderName();
}

//------------------------
// ViewMode
//------------------------

Desktop::FolderViewMode MvQFolderWidget::viewMode()
{
    return views_->currentMode();
}

void MvQFolderWidget::setViewMode(Desktop::FolderViewMode mode)
{
    views_->setCurrentMode(mode);
    if (Folder* f = folderModel_->folder()) {
        FolderSettings* fi = f->settings();
        fi->setViewMode(mode);
    }
}


bool MvQFolderWidget::viewModeGrid()
{
    return true;
}

void MvQFolderWidget::iconCommandFromMain(QString name)
{
    views_->currentBase()->iconCommandFromMain(name);
}

//---------------------------------------
// Find icons
//---------------------------------------

void MvQFolderWidget::findIcons(FolderSearchData* data)
{
    folderModel_->setSearchData(data);
}

//---------------------------------------
// Highlight one icon in the view
//---------------------------------------

void MvQFolderWidget::showIcon(IconObject* obj)
{
    views_->currentBase()->showIcon(obj);
}

//------------------------
// Save icon positions
//------------------------

void MvQFolderWidget::saveFolderInfo()
{
    folderModel_->saveFolderInfo();
}

//------------------------
// Save/restore settings
//------------------------

void MvQFolderWidget::writeSettings(QSettings& settings)
{
    Folder* folder = currentFolder();
    settings.setValue("path", (folder) ? QString::fromStdString(folder->fullName()) : expectedFolderPath_);
    settings.setValue("viewMode", static_cast<int>(views_->currentMode()));
    settings.setValue("iconSize", folderModel_->iconSize());
}

void MvQFolderWidget::readSettings(QSettings& settings)
{
    // Settings must store the same values as FolderSettings!!
    // However, we need the code below because for read-only folders we cannot store the folder settings
    // in a hidden file in the folder!! So we must read the the settings and overwrite the foldersettings with it if
    // they are different.
    if (settings.contains("viewMode") && settings.contains("iconSize")) {
        Desktop::FolderViewMode viewMode = FolderSettings::toViewMode(settings.value("viewMode").toInt());
        int iconSize = settings.value("iconSize").toInt();

        if (Folder* f = folderModel_->folder()) {
            FolderSettings* fi = f->settings();
            if (fi && fi->hasFile()) {
                if (viewMode != fi->viewMode())
                    views_->setCurrentMode(viewMode);
                if (iconSize != fi->iconSize())
                    setIconSize(iconSize);
            }
        }
    }
}
