/*
    SPDX-FileCopyrightText: 2019 Marco Martin <mart@kde.org>
    SPDX-FileCopyrightText: 2022 ivan tkachenko <me@ratijas.tk>
    SPDX-FileCopyrightText: 2022 Niccolò Venerandi <niccolo@venerandi.com>

    SPDX-License-Identifier: LGPL-2.0-or-later
*/

import QtQuick 2.15
import QtQuick.Layouts 1.15
import QtQuick.Window 2.15
import Qt5Compat.GraphicalEffects
import QtQuick.Effects as Effects

import org.kde.plasma.plasmoid 2.0
import org.kde.plasma.core as PlasmaCore
import org.kde.ksvg 1.0 as KSvg
import org.kde.plasma.components 3.0 as PlasmaComponents
import org.kde.plasma.private.containmentlayoutmanager 1.0 as ContainmentLayoutManager
import org.kde.kirigami as Kirigami

ContainmentLayoutManager.AppletContainer {
    id: appletContainer
    editModeCondition: Plasmoid.immutable
        ? ContainmentLayoutManager.ItemContainer.Manual
        : ContainmentLayoutManager.ItemContainer.AfterPressAndHold

    Kirigami.Theme.inherit: false
    Kirigami.Theme.colorSet: (applet.plasmoid.effectiveBackgroundHints & PlasmaCore.Types.ShadowBackground)
        && !(applet.plasmoid.effectiveBackgroundHints & PlasmaCore.Types.StandardBackground)
        && !(applet.plasmoid.effectiveBackgroundHints & PlasmaCore.Types.TranslucentBackground)
            ? Kirigami.Theme.Complementary
            : Kirigami.Theme.Window

    onFocusChanged: {
        if (!focus && !dragActive) {
            editMode = false;
        }
    }
    Layout.minimumWidth: {
        if (!applet) {
            return leftPadding + rightPadding;
        }

        if (applet.preferredRepresentation !== applet.fullRepresentation
            && applet.compactRepresentationItem
        ) {
            return applet.compactRepresentationItem.Layout.minimumWidth + leftPadding + rightPadding;
        } else {
            return applet.Layout.minimumWidth + leftPadding + rightPadding;
        }
    }
    Layout.minimumHeight: {
        if (!applet) {
            return topPadding + bottomPadding;
        }

        if (applet.preferredRepresentation !== applet.fullRepresentation
            && applet.compactRepresentationItem
        ) {
            return applet.compactRepresentationItem.Layout.minimumHeight + topPadding + bottomPadding;
        } else {
            return applet.Layout.minimumHeight + topPadding + bottomPadding;
        }
    }

    Layout.preferredWidth: Math.max(applet.Layout.minimumWidth, applet.Layout.preferredWidth)
    Layout.preferredHeight: Math.max(applet.Layout.minimumHeight, applet.Layout.preferredHeight)

    Layout.maximumWidth: applet.Layout.maximumWidth
    Layout.maximumHeight: applet.Layout.maximumHeight

    leftPadding: background.margins.left
    topPadding: background.margins.top
    rightPadding: background.margins.right
    bottomPadding: background.margins.bottom

    // render via a layer if we're at an angle
    // resize handles are rendered outside this item, so also disable when they're showing to avoid clipping
    layer.enabled: (rotation % 90 !== 0) && !(configOverlayItem && configOverlayItem.visible)
    layer.smooth: true

    initialSize.width: applet.switchWidth + leftPadding + rightPadding
    initialSize.height: applet.switchHeight + topPadding + bottomPadding

    background: KSvg.FrameSvgItem {
        id: background

        property bool blurEnabled: false
        property Item maskItem: null

        prefix: blurEnabled ? "blurred" : ""

        imagePath: {
            if (!appletContainer.applet) {
                return "";
            }
            if (appletContainer.applet.plasmoid.effectiveBackgroundHints & PlasmaCore.Types.TranslucentBackground) {
                return "widgets/translucentbackground";
            } else if (appletContainer.applet.plasmoid.effectiveBackgroundHints & PlasmaCore.Types.StandardBackground) {
                return "widgets/background";
            } else {
                return "";
            }
        }

        function bindBlurEnabled() {
            // bind to api and hints automatically, refresh non-observable prefix manually
            blurEnabled = Qt.binding(() =>
                   GraphicsInfo.api !== GraphicsInfo.Software
                && (appletContainer.applet.plasmoid.effectiveBackgroundHints & PlasmaCore.Types.StandardBackground)
                && hasElementPrefix("blurred")
            );
        }

        Component.onCompleted: bindBlurEnabled()
        onRepaintNeeded: bindBlurEnabled()

        onBlurEnabledChanged: {
            if (blurEnabled) {
                if (maskItem === null) {
                    maskItem = maskComponent.createObject(this);
                }
            } else {
                if (maskItem !== null) {
                    maskItem.destroy();
                    maskItem = null;
                }
            }
        }

        DropShadow {
            anchors {
                fill: parent
                leftMargin: appletContainer.leftPadding
                topMargin: appletContainer.topPadding
                rightMargin: appletContainer.rightPadding
                bottomMargin: appletContainer.bottomPadding
            }
            z: -1
            horizontalOffset: 0
            verticalOffset: 1

            radius: 4
            samples: 9
            spread: 0.35

            color: Qt.rgba(0, 0, 0, 0.5)
            opacity: 1

            source: appletContainer.applet && appletContainer.applet.plasmoid.effectiveBackgroundHints & PlasmaCore.Types.ShadowBackground
                ? appletContainer.applet : null
            visible: source !== null
        }
    }

    Component {
        id: maskComponent

        Effects.MultiEffect {
            id: backgroundEffect

            readonly property rect appletContainerScreenRect: {
                const win = appletContainer.Window.window;
                let sceneSize = Qt.size(appletContainer.width, appletContainer.height)
                if (win) {
                    sceneSize = Qt.size(win.width, win.height)
                }
                const position = appletContainer.Kirigami.ScenePosition;
                return clipRect(
                    boundsForTransformedRect(
                        Qt.rect(
                            position.x - parent.Kirigami.ScenePosition.x,
                            position.y - parent.Kirigami.ScenePosition.y,
                            appletContainer.width,
                            appletContainer.height),
                        appletContainer.rotation,
                        appletContainer.scale),
                    sceneSize);
            }

            /** Apply geometry transformations, and return a bounding rectangle for a resulting shape. */
            // Note: It's basically a custom QMatrix::mapRect implementation, and for
            // simplicity's sake should be replaced when/if mapRect becomes available in QML.
            function boundsForTransformedRect(rect: rect, angle: real, scale: real): rect {
                if (angle === 0 && scale === 1) {
                    return rect; // hot path optimization
                }
                let cosa = Math.abs(Math.cos(angle * (Math.PI / 180))) * scale;
                let sina = Math.abs(Math.sin(angle * (Math.PI / 180))) * scale;
                let newSize = Qt.size(
                    rect.width * cosa + rect.height * sina,
                    rect.width * sina + rect.height * cosa);
                return Qt.rect(
                    rect.left + (rect.width - newSize.width) / 2,
                    rect.top + (rect.height - newSize.height) / 2,
                    newSize.width,
                    newSize.height);
            }

            /** Clip given rectangle to the bounds of given size, assuming bounds position {0,0}.
             * This is a pure library function, similar to QRect::intersected,
             * which Qt should've exposed in QML stdlib.
             */
            function clipRect(rect: rect, bounds: size): rect {
                return Qt.rect(
                    Math.max(0, Math.min(bounds.width, rect.x)),
                    Math.max(0, Math.min(bounds.height, rect.y)),
                    Math.max(0, rect.width
                                + Math.min(0, rect.x)
                                + Math.min(0, bounds.width - (rect.x + rect.width))),
                    Math.max(0, rect.height
                                + Math.min(0, rect.y)
                                + Math.min(0, bounds.height - (rect.y + rect.height))),
                );
            }

            parent: appletContainer.layout.containmentItem
            x: appletContainerScreenRect.x
            y: appletContainerScreenRect.y
            width: appletContainerScreenRect.width
            height: appletContainerScreenRect.height
            visible: appletContainer.visible

            z: -2

            maskEnabled: true
            maskSource: mask
            // In the config they are more or less from 0 to 2, here from -1 to 1
            contrast: ContainmentLayoutManager.PlasmaBackground.backgroundContrast - 1.0
            saturation: ContainmentLayoutManager.PlasmaBackground.backgroundSaturation - 1.0

            Item {
                id: mask
                // optimized (clipped) blurred-mask

                layer.enabled: true
                width: backgroundEffect.appletContainerScreenRect.width
                height: backgroundEffect.appletContainerScreenRect.height

                clip: true

                KSvg.FrameSvgItem {
                    imagePath: "widgets/background"
                    prefix: "blurred-mask"

                    x: appletContainer.Kirigami.ScenePosition.x - backgroundEffect.appletContainerScreenRect.x - appletContainer.layout.containmentItem.Kirigami.ScenePosition.x
                    y: appletContainer.Kirigami.ScenePosition.y - backgroundEffect.appletContainerScreenRect.y - appletContainer.layout.containmentItem.Kirigami.ScenePosition.y

                    width: background.width
                    height: background.height

                    rotation: appletContainer.rotation
                    scale: appletContainer.scale
                }
            }

            source: Effects.MultiEffect {
                // Blur needs to be a separed MultiEffect because otherwise it blurs
                // the mask as well
                width: backgroundEffect.appletContainerScreenRect.width
                height: backgroundEffect.appletContainerScreenRect.height

                blurEnabled: true
                blur: 1
                blurMax: 128

                source: ShaderEffectSource {
                    width: backgroundEffect.appletContainerScreenRect.width
                    height: backgroundEffect.appletContainerScreenRect.height
                    sourceRect: backgroundEffect.appletContainerScreenRect
                    sourceItem: appletContainer.layout.containmentItem.wallpaper
                }
            }
        }
    }

    busyIndicatorComponent: PlasmaComponents.BusyIndicator {
        anchors.centerIn: parent
        visible: applet.plasmoid.busy
        running: visible
    }
    configurationRequiredComponent: PlasmaComponents.Button {
        anchors.centerIn: parent
        text: i18nd("plasmashellprivateplugin", "Configure…")
        icon.name: "configure"
        visible: applet.plasmoid.configurationRequired
        onClicked: applet.plasmoid.internalAction("configure").trigger();
    }
}
