/*
* 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 "precompiled.h"

#include <QGraphicsItem>
#include <QFont>
#include <QPainter>

#include <AzCore/Serialization/EditContext.h>

#include <Components/Nodes/General/GeneralNodeTitleComponent.h>

#include <Components/Nodes/General/GeneralNodeFrameComponent.h>
#include <GraphCanvas/Components/StyleBus.h>
#include <GraphCanvas/Components/Nodes/NodeBus.h>
#include <GraphCanvas/Editor/GraphCanvasProfiler.h>
#include <GraphCanvas/tools.h>
#include <GraphCanvas/Styling/StyleHelper.h>
#include <GraphCanvas/Utils/QtDrawingUtils.h>
#include <GraphCanvas/Components/VisualBus.h>


namespace GraphCanvas
{
    //////////////////////////////
    // GeneralNodeTitleComponent
    //////////////////////////////

    void GeneralNodeTitleComponent::Reflect(AZ::ReflectContext* context)
    {
        AZ::SerializeContext* serializeContext = azrtti_cast<AZ::SerializeContext*>(context);
        if (serializeContext)
        {
            serializeContext->Class<GeneralNodeTitleComponentSaveData, ComponentSaveData>()
                ->Version(1)
                ->Field("PaletteOverride", &GeneralNodeTitleComponentSaveData::m_paletteOverride)
            ;

            serializeContext->Class<GeneralNodeTitleComponent, AZ::Component >()
                ->Version(4)
                ->Field("Title", &GeneralNodeTitleComponent::m_title)
                ->Field("SubTitle", &GeneralNodeTitleComponent::m_subTitle)
                ->Field("SaveData", &GeneralNodeTitleComponent::m_saveData)
                ->Field("DefaultPalette", &GeneralNodeTitleComponent::m_basePalette)
                ;
        }
    }

    GeneralNodeTitleComponent::GeneralNodeTitleComponent()
    {
    }

    void GeneralNodeTitleComponent::Init()
    {
        m_generalNodeTitleWidget = aznew GeneralNodeTitleGraphicsWidget(GetEntityId());
    }

    void GeneralNodeTitleComponent::Activate()
    {
        AZ::EntityId entityId = GetEntityId();
        m_saveData.Activate(entityId);
        SceneMemberNotificationBus::Handler::BusConnect(entityId);
        NodeTitleRequestBus::Handler::BusConnect(entityId);
        VisualNotificationBus::Handler::BusConnect(entityId);

        if (m_generalNodeTitleWidget)
        {
            m_generalNodeTitleWidget->SetTitle(m_title);
            m_generalNodeTitleWidget->SetSubTitle(m_subTitle);

            m_generalNodeTitleWidget->UpdateLayout();

            m_generalNodeTitleWidget->Activate();
        }
    }

    void GeneralNodeTitleComponent::Deactivate()
    {
        if (m_generalNodeTitleWidget)
        {
            m_generalNodeTitleWidget->Deactivate();
        }

        SceneMemberNotificationBus::Handler::BusDisconnect();
        NodeTitleRequestBus::Handler::BusDisconnect();
        VisualNotificationBus::Handler::BusDisconnect();

    }

    void GeneralNodeTitleComponent::SetTitle(const AZStd::string& title)
    {
        m_title.SetFallback(title);

        if (m_generalNodeTitleWidget)
        {
            m_generalNodeTitleWidget->SetTitle(title);
        }
    }

    void GeneralNodeTitleComponent::SetTranslationKeyedTitle(const TranslationKeyedString& title)
    {
        m_title = title;

        if (m_generalNodeTitleWidget)
        {
            m_generalNodeTitleWidget->SetTitle(title);
        }
    }

    AZStd::string GeneralNodeTitleComponent::GetTitle() const
    {
        return m_title.GetDisplayString();
    }

    void GeneralNodeTitleComponent::SetSubTitle(const AZStd::string& subtitle)
    {
        m_subTitle.SetFallback(subtitle);

        if (m_generalNodeTitleWidget)
        {
            m_generalNodeTitleWidget->SetSubTitle(subtitle);
        }
    }

    void GeneralNodeTitleComponent::SetTranslationKeyedSubTitle(const TranslationKeyedString& subtitle)
    {
        m_subTitle = subtitle;

        if (m_generalNodeTitleWidget)
        {
            m_generalNodeTitleWidget->SetSubTitle(subtitle);
        }
    }

    AZStd::string GeneralNodeTitleComponent::GetSubTitle() const
    {
        return m_subTitle.GetDisplayString();
    }

    QGraphicsWidget* GeneralNodeTitleComponent::GetGraphicsWidget()
    {
        return m_generalNodeTitleWidget;
    }

    void GeneralNodeTitleComponent::SetDefaultPalette(const AZStd::string& basePalette)
    {
        if (m_generalNodeTitleWidget)
        {
            m_basePalette = basePalette;
            m_generalNodeTitleWidget->SetPaletteOverride(basePalette);
        }
    }

    void GeneralNodeTitleComponent::SetPaletteOverride(const AZStd::string& paletteOverride)
    {
        m_saveData.m_paletteOverride = paletteOverride;
        m_saveData.SignalDirty();

        if (m_generalNodeTitleWidget)
        {
            m_generalNodeTitleWidget->SetPaletteOverride(paletteOverride);
        }
    }

    void GeneralNodeTitleComponent::SetDataPaletteOverride(const AZ::Uuid& uuid)
    {
        if (m_generalNodeTitleWidget)
        {
            m_generalNodeTitleWidget->SetPaletteOverride(uuid);
        }
    }

    void GeneralNodeTitleComponent::SetColorPaletteOverride(const QColor& color)
    {
        if (m_generalNodeTitleWidget)
        {
            m_generalNodeTitleWidget->SetPaletteOverride(color);
        }
    }

    void GeneralNodeTitleComponent::ConfigureIconConfiguration(PaletteIconConfiguration& paletteConfiguration)
    {
        if (m_generalNodeTitleWidget)
        {
            m_generalNodeTitleWidget->ConfigureIconConfiguration(paletteConfiguration);
        }
    }

    void GeneralNodeTitleComponent::ClearPaletteOverride()
    {
        m_saveData.m_paletteOverride = "";
        m_saveData.SignalDirty();

        if (m_generalNodeTitleWidget)
        {
            m_generalNodeTitleWidget->ClearPaletteOverride();
        }
    }

    void GeneralNodeTitleComponent::OnSceneSet(const AZ::EntityId& graphId)
    {
        if (!m_saveData.m_paletteOverride.empty())
        {
            if (m_generalNodeTitleWidget)
            {
                m_generalNodeTitleWidget->SetPaletteOverride(m_saveData.m_paletteOverride);
            }
        }
        else if (!m_basePalette.empty())
        {
            if (m_generalNodeTitleWidget)
            {
                m_generalNodeTitleWidget->SetPaletteOverride(m_basePalette);
            }
        }
    }

    ///////////////////////////////////
    // GeneralNodeTitleGraphicsWidget
    ///////////////////////////////////
    GeneralNodeTitleGraphicsWidget::GeneralNodeTitleGraphicsWidget(const AZ::EntityId& entityId)
        : m_entityId(entityId)
        , m_paletteOverride(nullptr)
        , m_colorOverride(nullptr)
    {
        setCacheMode(QGraphicsItem::CacheMode::DeviceCoordinateCache);
        setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
        setGraphicsItem(this);
        setAcceptHoverEvents(false);
        setFlag(ItemIsMovable, false);

        m_titleWidget = aznew GraphCanvasLabel(this);
        m_subTitleWidget = aznew GraphCanvasLabel(this);
        m_linearLayout = new QGraphicsLinearLayout(Qt::Vertical);
        m_linearLayout->setSpacing(0);
        setLayout(m_linearLayout);
        setData(GraphicsItemName, QStringLiteral("Title/%1").arg(static_cast<AZ::u64>(entityId), 16, 16, QChar('0')));
    }

    GeneralNodeTitleGraphicsWidget::~GeneralNodeTitleGraphicsWidget()
    {
        delete m_colorOverride;
    }

    void GeneralNodeTitleGraphicsWidget::Activate()
    {
        AZ::EntityId entityId = GetEntityId();
        SceneMemberNotificationBus::Handler::BusConnect(entityId);
        NodeNotificationBus::Handler::BusConnect(entityId);
        RootGraphicsItemNotificationBus::Handler::BusConnect(entityId);

        AZ::EntityId scene;
        SceneMemberRequestBus::EventResult(scene, entityId, &SceneMemberRequests::GetScene);
        if (scene.IsValid())
        {
            SceneNotificationBus::Handler::BusConnect(scene);
            UpdateStyles();
        }
    }

    void GeneralNodeTitleGraphicsWidget::Deactivate()
    {        
        SceneMemberNotificationBus::Handler::BusDisconnect();
        RootGraphicsItemNotificationBus::Handler::BusDisconnect();
        NodeNotificationBus::Handler::BusDisconnect();
        SceneNotificationBus::Handler::BusDisconnect();
    }

    void GeneralNodeTitleGraphicsWidget::SetTitle(const TranslationKeyedString& title)
    {
        if (m_titleWidget)
        {
            m_titleWidget->SetLabel(title);
            UpdateLayout();
        }
    }

    void GeneralNodeTitleGraphicsWidget::SetSubTitle(const TranslationKeyedString& subtitle)
    {
        if (m_subTitleWidget)
        {
            m_subTitleWidget->SetLabel(subtitle);
            UpdateLayout();
        }
    }

    void GeneralNodeTitleGraphicsWidget::SetPaletteOverride(AZStd::string_view paletteOverride)
    {
        AZ_Error("GraphCanvas", m_colorOverride == nullptr, "Unsupported use of Color and Palete Overrides");
        if (m_colorOverride)
        {
            delete m_colorOverride;
            m_colorOverride = nullptr;
        }

        AZ::EntityId sceneId;
        SceneMemberRequestBus::EventResult(sceneId, GetEntityId(), &SceneMemberRequests::GetScene);
        
        m_paletteOverride = nullptr;
        StyleManagerRequestBus::BroadcastResult(m_paletteOverride, &StyleManagerRequests::FindColorPalette, paletteOverride);
        update();
    }

    void GeneralNodeTitleGraphicsWidget::ConfigureIconConfiguration(PaletteIconConfiguration& paletteConfiguration)
    {
        bool isEnabled = false;
        RootGraphicsItemRequestBus::EventResult(isEnabled, GetEntityId(), &RootGraphicsItemRequests::IsEnabled);

        if (!isEnabled)
        {
            if (!m_disabledPalette)
            {
                StyleManagerRequestBus::BroadcastResult(m_disabledPalette, &StyleManagerRequests::FindColorPalette, "DisabledColorPalette");
            }

            if (m_disabledPalette)
            {
                m_disabledPalette->PopulatePaletteConfiguration(paletteConfiguration);
            }
        }
        else if (m_colorOverride)
        {
            m_colorOverride->PopulatePaletteConfiguration(paletteConfiguration);
        }
        else if (m_paletteOverride)
        {
            m_paletteOverride->PopulatePaletteConfiguration(paletteConfiguration);
        }
        else
        {
            m_styleHelper.PopulatePaletteConfiguration(paletteConfiguration);
        }
    }

    void GeneralNodeTitleGraphicsWidget::SetPaletteOverride(const AZ::Uuid& uuid)
    {
        AZ_Error("GraphCanvas", m_colorOverride == nullptr, "Unsupported use of Color and DataType Overrides");
        if (m_colorOverride)
        {
            delete m_colorOverride;
            m_colorOverride = nullptr;
        }

        AZ::EntityId sceneId;
        SceneMemberRequestBus::EventResult(sceneId, GetEntityId(), &SceneMemberRequests::GetScene);

        m_paletteOverride = nullptr;
        StyleManagerRequestBus::BroadcastResult(m_paletteOverride, &StyleManagerRequests::FindDataColorPalette, uuid);
        update();
    }

    void GeneralNodeTitleGraphicsWidget::SetPaletteOverride(const QColor& color)
    {
        if (m_colorOverride == nullptr)
        {
            if (m_paletteOverride != nullptr)
            {
                m_paletteOverride = nullptr;
            }

            AZ::EntityId sceneId;
            SceneMemberRequestBus::EventResult(sceneId, GetEntityId(), &SceneMemberRequests::GetScene);

            m_colorOverride = new Styling::StyleHelper();
            m_colorOverride->SetScene(sceneId);
            m_colorOverride->SetStyle("ColorOverrideNodeTitlePalette");
        }

        if (m_colorOverride)
        {
            m_colorOverride->AddAttributeOverride(Styling::Attribute::BackgroundColor, color);
            m_colorOverride->AddAttributeOverride(Styling::Attribute::LineColor, color);
            update();
        }
    }

    void GeneralNodeTitleGraphicsWidget::ClearPaletteOverride()
    {
        m_paletteOverride = nullptr;
        update();
    }

    void GeneralNodeTitleGraphicsWidget::UpdateLayout()
    {
        while (m_linearLayout->count() != 0)
        {
            m_linearLayout->removeAt(0);
        }

        if (!m_titleWidget->GetLabel().empty())
        {
            m_linearLayout->addItem(m_titleWidget);
        }

        if (!m_subTitleWidget->GetLabel().empty())
        {
            m_linearLayout->addItem(m_subTitleWidget);
        }

        RefreshDisplay();
        NodeTitleNotificationsBus::Event(GetEntityId(), &NodeTitleNotifications::OnTitleChanged);
        NodeUIRequestBus::Event(GetEntityId(), &NodeUIRequests::AdjustSize);
    }

    void GeneralNodeTitleGraphicsWidget::UpdateStyles()
    {
        m_styleHelper.SetStyle(GetEntityId(), Styling::Elements::Title);
        m_titleWidget->SetStyle(GetEntityId(), Styling::Elements::MainTitle);
        m_subTitleWidget->SetStyle(GetEntityId(), Styling::Elements::SubTitle);

        // Just clear our the disabled palette and we'll get it when we need it.
        m_disabledPalette = nullptr;
    }

    void GeneralNodeTitleGraphicsWidget::RefreshDisplay()
    {
        updateGeometry();
        update();
    }

    void GeneralNodeTitleGraphicsWidget::OnStylesChanged()
    {
        UpdateStyles();
        RefreshDisplay();
    }

    void GeneralNodeTitleGraphicsWidget::OnAddedToScene(const AZ::EntityId& scene)
    {
        SceneNotificationBus::Handler::BusConnect(scene);
        UpdateStyles();
        RefreshDisplay();
    }

    void GeneralNodeTitleGraphicsWidget::OnRemovedFromScene(const AZ::EntityId& scene)
    {
        SceneNotificationBus::Handler::BusDisconnect();
    }

    void GeneralNodeTitleGraphicsWidget::OnTooltipChanged(const AZStd::string& tooltip)
    {
        setToolTip(Tools::qStringFromUtf8(tooltip));
    }

    void GeneralNodeTitleGraphicsWidget::OnEnabledChanged(RootGraphicsItemEnabledState enabledState)
    {
        UpdateStyles();
        RefreshDisplay();        
    }

    void GeneralNodeTitleGraphicsWidget::paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget)
    {
        GRAPH_CANVAS_DETAILED_PROFILE_FUNCTION();

        Styling::PaletteStyle style = m_styleHelper.GetAttribute(Styling::Attribute::PaletteStyle, Styling::PaletteStyle::Solid);

        if (m_paletteOverride)
        {
            style = m_paletteOverride->GetAttribute(Styling::Attribute::PaletteStyle, Styling::PaletteStyle::Solid);
        }
        
        // Background
        QRectF bounds = boundingRect();

        qreal cornerRadius = 0.0;

        NodeUIRequestBus::EventResult(cornerRadius, GetEntityId(), &NodeUIRequests::GetCornerRadius);

        // Ensure the bounds are large enough to draw the full radius
        // Even in our smaller section
        if (bounds.height() < 2.0 * cornerRadius)
        {
            bounds.setHeight(2.0 * cornerRadius);
        }

        QPainterPath path;
        path.setFillRule(Qt::WindingFill);

        // -1.0 because the rounding is a little bit short(for some reason), so I subtract one and let it overshoot a smidge.
        path.addRoundedRect(bounds, cornerRadius - 1.0, cornerRadius - 1.0);

        // We only want corners on the top half. So we need to draw a rectangle over the bottom bits to square it out.
        QPointF bottomTopLeft(bounds.bottomLeft());
        bottomTopLeft.setY(bottomTopLeft.y() - cornerRadius - 1.0);
        path.addRect(QRectF(bottomTopLeft, bounds.bottomRight()));

        painter->save();
        painter->setClipPath(path);

        bool isEnabled = false;
        RootGraphicsItemRequestBus::EventResult(isEnabled, GetEntityId(), &RootGraphicsItemRequests::IsEnabled);

        if (!isEnabled)
        {
            if (!m_disabledPalette)
            {
                StyleManagerRequestBus::BroadcastResult(m_disabledPalette, &StyleManagerRequests::FindColorPalette, "DisabledColorPalette");
            }            

            if (m_disabledPalette)
            {
                QtDrawingUtils::FillArea((*painter), path.boundingRect(), (*m_disabledPalette));
            }
        }
        else if (m_colorOverride)
        {
            QtDrawingUtils::FillArea((*painter), path.boundingRect(), (*m_colorOverride));
        }
        else if (m_paletteOverride)
        {
            QtDrawingUtils::FillArea((*painter), path.boundingRect(), (*m_paletteOverride));
        }
        else
        {
            QtDrawingUtils::FillArea((*painter), path.boundingRect(), m_styleHelper);
        }

        QLinearGradient gradient(bounds.bottomLeft(), bounds.topLeft());
        gradient.setColorAt(0, QColor(0, 0, 0, 102));
        gradient.setColorAt(1, QColor(0, 0, 0, 77));
        painter->fillPath(path, gradient);

        painter->restore();

        QGraphicsWidget::paint(painter, option, widget);
    }
}