/*
 *  SPDX-FileCopyrightText: 2015 Marco Martin <mart@kde.org>
 *
 *  SPDX-License-Identifier: LGPL-2.0-or-later
 */

import QtQuick 2.15
import QtQuick.Controls 2.15 as QQC2
import QtQuick.Layouts 1.15
import org.kde.kirigami 2.20 as Kirigami

/**
 * @brief PassiveNotificationManager is meant to display small, passive and
 * inline notifications in the app.
 *
 * It is used to show notifications of limited importance that make sense only
 * when the user is using the application and wouldn't be suited as a global
 * system-wide notification.
*/
Item {
    id: root

    property int maximumNotificationWidth: {
        if (Kirigami.Settings.isMobile) {
            return applicationWindow().width - Kirigami.Units.largeSpacing * 4
        } else {
            return Math.min(Kirigami.Units.gridUnit * 25, applicationWindow().width / 1.5)
        }
    }

    readonly property int maximumNotificationCount: 4

    function showNotification(message, timeout, actionText, callBack) {
        if (!message) {
            return;
        }

        let interval = 7000;

        if (timeout === "short") {
            interval = 4000;
        } else if (timeout === "long") {
            interval = 12000;
        } else if (timeout > 0) {
            interval = timeout;
        }

        // this wrapper is necessary because of Qt casting a function into an object
        const callBackWrapperObj = callBackWrapper.createObject(listView, { callBack })

        // set empty string & function for qml not to complain
        notificationsModel.append({
            text: message,
            actionButtonText: actionText || "",
            closeInterval: interval,
            callBackWrapper: callBackWrapperObj
        })
        // remove the oldest notification if new notification count would exceed 3
        if (notificationsModel.count === maximumNotificationCount) {
            if (listView.itemAtIndex(0).hovered === true) {
                hideNotification(1)
            } else {
                hideNotification()
            }
        }
    }

    function hideNotification(index = 0) {
        if (index >= 0 && notificationsModel.count > index) {
            const callBackWrapperObj = notificationsModel.get(index).callBackWrapper
            if (callBackWrapperObj) {
                callBackWrapperObj.destroy()
            }
            notificationsModel.remove(index)
        }
    }

    // we have to set height to show more than one notification
    height: Math.min(applicationWindow().height, Kirigami.Units.gridUnit * 10)

    implicitHeight: listView.implicitHeight
    implicitWidth: listView.implicitWidth

    Kirigami.Theme.inherit: false
    Kirigami.Theme.colorSet: Kirigami.Theme.Complementary

    ListModel {
        id: notificationsModel
    }

    ListView {
        id: listView

        anchors.fill: parent
        anchors.bottomMargin: Kirigami.Units.largeSpacing

        implicitWidth: root.maximumNotificationWidth
        spacing: Kirigami.Units.smallSpacing
        model: notificationsModel
        verticalLayoutDirection: ListView.BottomToTop
        keyNavigationEnabled: false
        reuseItems: false  // do not resue items, otherwise delegates do not hide themselves properly
        focus: false
        interactive: false

        add: Transition {
            id: addAnimation
            ParallelAnimation {
                alwaysRunToEnd: true
                NumberAnimation {
                    property: "opacity"
                    from: 0
                    to: 1
                    duration: Kirigami.Units.longDuration
                    easing.type: Easing.OutCubic
                }
                NumberAnimation {
                    property: "y"
                    from: addAnimation.ViewTransition.destination.y - Kirigami.Units.gridUnit * 3
                    duration: Kirigami.Units.longDuration
                    easing.type: Easing.OutCubic
                }
            }
        }
        displaced: Transition {
            ParallelAnimation {
                alwaysRunToEnd: true
                NumberAnimation {
                    property: "y"
                    duration: Kirigami.Units.longDuration
                    easing.type: Easing.InOutCubic
                }
                NumberAnimation {
                    property: "opacity"
                    duration: 0
                    to: 1
                }
            }
        }
        remove: Transition {
            ParallelAnimation {
                alwaysRunToEnd: true
                NumberAnimation {
                    property: "opacity"
                    from: 1
                    to: 0
                    duration: Kirigami.Units.longDuration
                    easing.type: Easing.InCubic
                }
                NumberAnimation {
                    property: "y"
                    to: Kirigami.Units.gridUnit * 3
                    duration: Kirigami.Units.longDuration
                    easing.type: Easing.InCubic
                }
                PropertyAction {
                    property: "transformOrigin"
                    value: Item.Bottom
                }
                PropertyAnimation {
                    property: "scale"
                    from: 1
                    to: 0
                    duration: Kirigami.Units.longDuration
                    easing.type: Easing.InCubic
                }
            }
        }
        delegate: QQC2.Control {
            id: delegate

            hoverEnabled: true

            anchors.horizontalCenter: parent ? parent.horizontalCenter : undefined
            width: Math.min(implicitWidth, maximumNotificationWidth)
            z: {
                if (delegate.hovered) {
                    return 2;
                } else if (delegate.index === 0) {
                    return 1;
                } else {
                    return 0;
                }
            }

            leftPadding: Kirigami.Units.largeSpacing
            rightPadding: Kirigami.Units.largeSpacing
            topPadding: Kirigami.Units.largeSpacing
            bottomPadding: Kirigami.Units.largeSpacing

            contentItem: RowLayout {
                id: mainLayout

                Kirigami.Theme.inherit: false
                Kirigami.Theme.colorSet: root.Kirigami.Theme.colorSet

                spacing: Kirigami.Units.mediumSpacing

                TapHandler {
                    acceptedButtons: Qt.LeftButton
                    onTapped: eventPoint => hideNotification(index)
                }
                Timer {
                    id: timer
                    interval: model.closeInterval
                    running: !delegate.hovered
                    onTriggered: hideNotification(index)
                }

                QQC2.Label {
                    id: label
                    text: model.text
                    elide: Text.ElideRight
                    wrapMode: Text.Wrap
                    Layout.fillWidth: true
                    Layout.alignment: Qt.AlignVCenter
                }

                QQC2.Button {
                    id: actionButton
                    text: model.actionButtonText
                    visible: text.length > 0
                    Layout.alignment: Qt.AlignVCenter
                    onClicked: {
                        const callBack = model.callBackWrapper.callBack
                        hideNotification(index)
                        if (callBack && (typeof callBack === "function")) {
                            callBack();
                        }
                    }
                }
            }
            background: Kirigami.ShadowedRectangle {
                Kirigami.Theme.inherit: false
                Kirigami.Theme.colorSet: root.Kirigami.Theme.colorSet
                shadow {
                    size: Kirigami.Units.gridUnit/2
                    color: Qt.rgba(0, 0, 0, 0.4)
                    yOffset: 2
                }
                radius: Kirigami.Units.smallSpacing * 2
                color: Kirigami.Theme.backgroundColor
                opacity: 0.9
            }
        }
    }
    Component {
        id: callBackWrapper
        QtObject {
            property var callBack
        }
    }
}

