/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#include "EditorUI_QT_Precompiled.h"
#include "QFileSelectResourceWidget.h"
#include "Utils.h"
#include "UIUtils.h"

#include <QPushButton>
#include <QIcon>
#include <QEvent>
#include <QKeyEvent>
#include <QString>

#include <Controls/QBitmapPreviewDialogImp.h>

#include "AttributeView.h"

// EditorCore
#include <BaseLibrary.h>
#include <Util/smartptr.h>
#include <Util/Variable.h>
#include <Util/VariablePropertyType.h>
#include <IEditor.h>
#include <IEditorParticleUtils.h>
#include <Util/FileUtil.h>
#include <Util/PathUtil.h>
#include <QtViewPaneManager.h>

// Interface
#include <Include/IBaseLibraryManager.h>
#include <Include/IResourceSelectorHost.h>
#include <Include/IAssetBrowser.h>
#include <Include/IEditorMaterialManager.h>
#include <Material/MaterialManager.h>

#include <BaseLibraryManager.h>
#include <QKeyEvent>
#include <VariableWidgets/ui_QFileSelectWidget.h>

//to disable shortcuts while in line edit
#include <QMessageBox>

#include <AzToolsFramework/API/ToolsApplicationAPI.h>
#include <AzToolsFramework/AssetBrowser/AssetSelectionModel.h>


QFileSelectResourceWidget::QFileSelectResourceWidget(CAttributeItem* parent, CAttributeView* attributeView,
    int propertyType)
    : QWidget(parent)
    , CBaseVariableWidget(parent)
    , m_ignoreSetVar(false)
    , m_propertyType(propertyType)
    , m_hasFocus(false)
    , m_attributeView(attributeView)
{
    setMouseTracking(true);

    m_gridLayout = new QGridLayout(this);
    m_gridLayout->setMargin(0);

    m_browseEdit = new AzQtComponents::BrowseEdit();
    m_browseEdit->setClearButtonEnabled(true);
    m_browseEdit->installEventFilter(this);
    m_gridLayout->addWidget(m_browseEdit);

    connect(m_browseEdit, &AzQtComponents::BrowseEdit::attachedButtonTriggered, this, &QFileSelectResourceWidget::onOpenSelectDialog);
    connect(m_browseEdit, &AzQtComponents::BrowseEdit::returnPressed, this, &QFileSelectResourceWidget::onReturnPressed);

    QToolButton* clearButton = AzQtComponents::LineEdit::getClearButton(m_browseEdit->lineEdit());
    assert(clearButton);
    connect(clearButton, &QToolButton::clicked, this, &QFileSelectResourceWidget::onClearButtonClicked);

    QPushButton* btn = NULL;

    // Set type specific buttons
    switch (m_propertyType)
    {
    case ePropertyTexture:
    {
        m_browseEdit->setPlaceholderText("Add a texture");

        // Open texture file in editor
        btn = addButton("Open Input Bindings Editor", tr("Open File"), 1, 0, 1, 6);
        connect(btn, &QPushButton::clicked, this, &QFileSelectResourceWidget::OpenSourceFile);
        m_btns.push_back(btn);
        m_btns.back()->installEventFilter(this);
        break;
    }
    case ePropertyMaterial:
    {
        // Open AssetBrowser
        m_browseEdit->setPlaceholderText("Add a material");

        // Open material editor
        btn = addButton("Open Input Bindings Editor", tr("Open File"), 1, 0, 1, 6);
        connect(btn, &QPushButton::clicked, this, &QFileSelectResourceWidget::OpenSourceFile);
        m_btns.push_back(btn);
        m_btns.back()->installEventFilter(this);

        break;
    }
    case ePropertyModel:
    {
        m_browseEdit->setPlaceholderText("Add a model");

        break;
    }
    case ePropertyAudioTrigger:
    {
        m_browseEdit->setPlaceholderText("Add a sound");
        break;
    }
    default:
        break;
    }

    m_tooltip = new QToolTipWrapper(this);
}

QPushButton* QFileSelectResourceWidget::addButton(const QString& caption, const QString& tooltip, int row, int col, int rowspan, int colspan, const QIcon* icon)
{
    QPushButton* btn = new QPushButton(this);

    if (icon)
    {
        btn->setIcon(*icon);
    }

    btn->setText(caption);
    btn->setToolTip(tooltip);

    m_gridLayout->addWidget(btn, row, col);

    return btn;
}

void QFileSelectResourceWidget::setPath(const QString& path)
{
    SelfCallFence(m_ignoreSetVar);

    QString newPath = path;

    newPath = newPath.toLower();

    if (newPath.size() > MAX_PATH)
    {
        newPath.resize(MAX_PATH);
    }

    newPath.replace("\\\\", "/");

    if (m_propertyType == ePropertyMaterial && newPath.endsWith(".mtl"))
    {
        newPath.chop(4);
    }

    //display a shortened path
    QString relativePath = Path::GetRelativePath(newPath.toUtf8().data(), false);
    QString formattedPath = relativePath;
    relativePath = formattedPath.trimmed();
    //Get relative part.
    QString oldPath;
    m_var->Get(oldPath);
    if (oldPath != relativePath)
    {
        m_var->Set(relativePath);
        emit m_parent->SignalUndoPoint();
    }

    m_browseEdit->setText(QString(relativePath));
}

void QFileSelectResourceWidget::onOpenSelectDialog()
{
    SResourceSelectorContext x;
    x.typeName = Prop::GetPropertyTypeToResourceType((PropertyType)m_propertyType);
    //get the path from the variable for use in the shortcut as the display path is shortened and invalid.
    QString currPath = "";
    getVar()->Get(currPath);
    QString newPath = currPath;

    // remove shortened path prefix - ".../"
    currPath.replace(QString(".../"), QString(""));

    switch (m_propertyType)
    {
    case ePropertyTexture:
    {
        AssetSelectionModel selection = AssetSelectionModel::AssetGroupSelection("Texture");
        AzToolsFramework::EditorRequests::Bus::Broadcast(&AzToolsFramework::EditorRequests::BrowseForAssets, selection);
        if (selection.IsValid())
        {
            newPath = selection.GetResult()->GetFullPath().c_str();
        }
        break;
    }
    case ePropertyMaterial:
    {
        AssetSelectionModel selection = AssetSelectionModel::AssetGroupSelection("All");
        AzToolsFramework::EditorRequests::Bus::Broadcast(&AzToolsFramework::EditorRequests::BrowseForAssets, selection);
        if (selection.IsValid())
        {
            newPath = selection.GetResult()->GetFullPath().c_str();
        }
        break;
    }
    case ePropertyModel:
    {
        AssetSelectionModel selection = AssetSelectionModel::AssetGroupSelection("Geometry");
        AzToolsFramework::EditorRequests::Bus::Broadcast(&AzToolsFramework::EditorRequests::BrowseForAssets, selection);
        if (selection.IsValid())
        {
            newPath = selection.GetResult()->GetRelativePath().c_str();
        }
        break;
    }
    case ePropertyAudioTrigger:
        newPath = GetIEditor()->GetResourceSelectorHost()->SelectResource(x, currPath);
        break;
    default:
        break;
    }

    setPath(newPath);
}

void QFileSelectResourceWidget::UpdatePreviewTooltip(QString filePath, QPoint position, bool showTooltip)
{
    m_tooltip->hide();

    // Force the tooltip image to clear so that the new version updates correctly.
    GetIEditor()->GetParticleUtils()->ToolTip_BuildFromConfig(m_tooltip, "", "Reset", "", true);

    GetIEditor()->GetParticleUtils()->ToolTip_BuildFromConfig(m_tooltip, m_parent->getAttributeView()->GetVariablePath(m_var), QString(filePath), QString(m_parent->getVar()->GetDisplayValue()), isEnabled());
    m_tooltip->TryDisplay(m_lastTooltipPos, m_browseEdit, QToolTipWidget::ArrowDirection::ARROW_RIGHT);

    // Reshow the tooltip if it was showing at the start.
    if (showTooltip)
    {
        m_tooltip->show();
    }
}

void QFileSelectResourceWidget::onReturnPressed()
{
    setPath(m_browseEdit->text());

    UpdatePreviewTooltip(m_browseEdit->text(), m_lastTooltipPos, m_tooltip->isVisible());
}

void QFileSelectResourceWidget::onClearButtonClicked()
{    
    setPath("");

    // Update the tooltip to show the blank image.
    QString filePath;
    getVar()->Get(filePath);

    UpdatePreviewTooltip(filePath, m_lastTooltipPos, m_tooltip->isVisible());
}

QString QFileSelectResourceWidget::pathFilter(const QString& path)
{
    return path;
}

void QFileSelectResourceWidget::setVar(IVariable* var)
{
    onVarChanged(var);
}

void QFileSelectResourceWidget::onVarChanged(IVariable* var)
{
    if (m_ignoreSetVar)
    {
        return;
    }
    QString newPath;
    var->Get(newPath);

    newPath = newPath.toLower();

    if (newPath.size() > MAX_PATH)
    {
        newPath.resize(MAX_PATH);
    }

    newPath.replace("\\\\", "/");

    if (m_propertyType == ePropertyMaterial && newPath.endsWith(".mtl"))
    {
        newPath.chop(4);
    }

    QSignalBlocker blocker(this);
    setPath(newPath);
}

bool QFileSelectResourceWidget::eventFilter(QObject* obj, QEvent* event)
{
    for (unsigned int i = 0; i < m_btns.count(); i++)
    {
        if (obj == (QObject*)m_btns[i])
        {
            if (event->type() == QEvent::ToolTip)
            {
                QHelpEvent* e = (QHelpEvent*)event;

                GetIEditor()->GetParticleUtils()->ToolTip_BuildFromConfig(m_tooltip, m_parent->getAttributeView()->GetVariablePath(m_var) + "." + m_btns[i]->toolTip(), "Button", "", isEnabled());

                m_tooltip->TryDisplay(e->globalPos(), m_btns[i], QToolTipWidget::ArrowDirection::ARROW_RIGHT);

                return true;
            }
        }
    }
    if (obj == (QObject*)m_browseEdit)
    {
        if (event->type() == QEvent::FocusIn)
        {
            //disable hotkeys while focused on path
            GetIEditor()->GetParticleUtils()->HotKey_SetEnabled(false);
        }
        if (event->type() == QEvent::FocusOut)
        {
            //disable hotkeys while focused on path
            GetIEditor()->GetParticleUtils()->HotKey_SetEnabled(true);
        }
        if (event->type() == QEvent::MouseButtonPress)
        {
            m_tooltip->hide();
        }
        if (event->type() == QEvent::KeyPress)
        {
            QKeyEvent* keyEvent = (QKeyEvent*)event;
            Qt::KeyboardModifiers mods = keyEvent->modifiers();
            //The tooltip has functionality with the following modifiers. We dont want to hide the tooltip in these case.
            //  Alt: show alpha channel of the texture;
            //  shift : show RGBA channel of the texture.
            if ((mods & Qt::KeyboardModifier::AltModifier) ||
                (mods & Qt::KeyboardModifier::ShiftModifier && !(mods & Qt::KeyboardModifier::ControlModifier)))
            {
                return true;
            }
            m_tooltip->hide();
        }
        if (event->type() == QEvent::ToolTip)
        {
            QHelpEvent* e = (QHelpEvent*)event;

            QString filePath;
            getVar()->Get(filePath);
            
            GetIEditor()->GetParticleUtils()->ToolTip_BuildFromConfig(m_tooltip, m_parent->getAttributeView()->GetVariablePath(m_var), QString(filePath), QString(m_parent->getVar()->GetDisplayValue()), isEnabled());

            m_lastTooltipPos = e->globalPos();
            m_tooltip->TryDisplay(e->globalPos(), m_browseEdit, QToolTipWidget::ArrowDirection::ARROW_RIGHT);

            return true;
        }
        if (event->type() == QEvent::MouseButtonDblClick)
        {
            QMouseEvent* mev = static_cast<QMouseEvent*>(event);
            if (mev->button() == Qt::LeftButton)
            {
                m_browseEdit->lineEdit()->selectAll();
                return true;
            }
        }
    }
    if (event->type() == QEvent::Leave)
    {
        if (m_tooltip->isVisible())
        {
            m_tooltip->hide();
        }
    }
    return false;
}

void QFileSelectResourceWidget::OpenSourceFile()
{
    QString filePath;
    getVar()->Get(filePath);

    switch (m_propertyType)
    {
    case ePropertyTexture:
    {
        while (filePath[0] == '/' || filePath[0] == '\\')
        {
            filePath = filePath.mid(1);
        }
        filePath.replace("\\", "/");
        filePath = filePath.mid(0, filePath.lastIndexOf('.'));
        QString folderPath = filePath;
        folderPath = folderPath.mid(0, filePath.lastIndexOf('/'));
        QDir dir(folderPath);
        if (!dir.exists())
        {
            folderPath = GetIEditor()->GetProjectName() + "/" + folderPath;
            dir.setPath(folderPath);
        }

        QString filename;
        filename = filePath.mid(filePath.lastIndexOf('/') + 1, filePath.length() - filePath.lastIndexOf('/'));
        filename.append(".*");

        QStringList files;
        files = dir.entryList(QStringList(filename), QDir::Files | QDir::NoSymLinks);
        // Looking for file with psd type
        if (files.size() > 0)
        {
            QString finalPath = dir.absolutePath() + "/" + files.first(); // Contribute Full path first
            GetIEditor()->GetFileUtil()->EditTextureFile(finalPath.toUtf8().data(), false);
            break;
        }
        // Not found, print error message
        GetIEditor()->GetEnv()->pLog->LogError("Failed to find souce file for texture: '%s'", filePath.toUtf8().data());
        QMessageBox::warning(this, "Warning", "Failed to find source file for texture: '" + filePath + "'", "Ok");
        break;
    }
    case ePropertyMaterial:
    {
        CMaterial *mat = GetIEditor()->GetMaterialManager()->LoadMaterial(filePath.toUtf8().data(), false);
        GetIEditor()->GetMaterialManager()->SetSelectedItem(mat);
        QtViewPaneManager::instance()->OpenPane(LyViewPane::MaterialEditor);
        break;
    }
    case ePropertyModel:
    case ePropertyAudioTrigger:
    default:
        break;
    }
}

#include <VariableWidgets/QFileSelectResourceWidget.moc>