/*
 * SPDX-FileCopyrightText: 2021 Devin Lin <devin@kde.org>
 * SPDX-FileCopyrightText: 2018-2019 Kai Uwe Broulik <kde@privat.broulik.de>
 * 
 * SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
*/

import QtQuick 2.8
import QtQuick.Layouts 1.1
import QtQuick.Window 2.2
import Qt5Compat.GraphicalEffects

import org.kde.plasma.core 2.0 as PlasmaCore
import org.kde.plasma.plasma5support 2.0 as P5Support
import org.kde.plasma.components 3.0 as PlasmaComponents3
import org.kde.plasma.extras 2.0 as PlasmaExtras

import org.kde.notificationmanager 1.0 as NotificationManager

import org.kde.kcoreaddons 1.0 as KCoreAddons

Item {
    id: notificationItem
    
    required property var notificationsModel
    
    required property int notificationsModelType
    
    /**
     * Whether the notification is allowed to invoke any action, or if it should instead
     * emit the runActionRequested(action) signal, containing the code to run.
     * 
     * This is useful for cases like the lockscreen, where actions should only be run after
     * the user logs in.
     */
    property bool requestToInvoke: false
    
    property var model
    property int modelIndex
    
    property P5Support.DataSource timeSource
    
    readonly property int notificationType: model.type

    readonly property bool inGroup: model.isInGroup || false
    readonly property bool inHistory: true

    readonly property string applicationIconSource: model.applicationIconName
    readonly property string applicationName: model.applicationName
    readonly property string originName: model.originName || ""

    readonly property string summary: model.summary
    readonly property var time: model.updated || model.created

    readonly property bool hasReplyAction: model.hasReplyAction || false
    readonly property string replyActionLabel: model.replyActionLabel || ""
    readonly property string replyPlaceholderText: model.replyPlaceholderText || ""
    readonly property string replySubmitButtonText: model.replySubmitButtonText || ""
    readonly property string replySubmitButtonIconName: model.replySubmitButtonIconName || ""
    
    // configure button on every single notifications is bit overwhelming
    readonly property bool configurable: !inGroup && model.configurable

    readonly property bool dismissable: model.type === NotificationManager.Notifications.JobType
        && model.jobState !== NotificationManager.Notifications.JobStateStopped
        && model.dismissed
        && notificationSettings.permanentJobPopups
    readonly property bool dismissed: model.dismissed || false
    readonly property bool closable: model.closable

    readonly property string body: model.body || ""
    readonly property var icon: model.image || model.iconName

    readonly property var urls: model.urls || []

    readonly property int jobState: model.jobState || 0
    readonly property int percentage: model.percentage || 0
    readonly property int jobError: model.jobError || 0
    readonly property bool suspendable: !!model.suspendable
    readonly property bool killable: !!model.killable
    
    readonly property QtObject jobDetails: model.jobDetails || null

    readonly property string configureActionLabel: model.configureActionLabel || ""
    readonly property bool hasDefaultAction: model.hasDefaultAction
    readonly property bool addDefaultAction: (model.hasDefaultAction
                                            && model.defaultActionLabel
                                            && (model.actionLabels || []).indexOf(model.defaultActionLabel) === -1) ? true : false
    readonly property var actionNames: {
        var actions = (model.actionNames || []);
        if (addDefaultAction) {
            actions.unshift("default"); // prepend
        }
        return actions;
    }
    readonly property var actionLabels: {
        var labels = (model.actionLabels || []);
        if (addDefaultAction) {
            labels.unshift(model.defaultActionLabel);
        }
        return labels;
    }
    
    /**
     * This signal is emitted and intended for the parent to make its own decision
     * on whether to run the requested notification action.
     */
    signal runActionRequested()
    
    signal actionInvoked(string actionName)
    signal replied(string text)
    signal openUrl(string url)
    signal fileActionInvoked(QtObject action)

    signal suspendJobClicked
    signal resumeJobClicked
    signal killJobClicked
    
    function expire() {
        if (model.resident) {
            model.expired = true;
        } else {
            if (notificationsModelType === NotificationsModelType.WatchedNotificationsModel) {
                notificationsModel.expire(model.notificationId);
            } else if (notificationsModelType === NotificationsModelType.NotificationsModel) {
                notificationsModel.expire(notificationsModel.index(modelIndex, 0));
            }
        }
    }

    function close() {
        if (notificationsModelType === NotificationsModelType.WatchedNotificationsModel) {
            notificationsModel.close(model.notificationId);
        } else if (notificationsModelType === NotificationsModelType.NotificationsModel) {
            notificationsModel.close(notificationsModel.index(modelIndex, 0));
        }
    }
    
    // TODO call
    function configure() {
        notificationsModel.configure(notificationsModel.index(modelIndex, 0))
    }
    
    property var pendingAction: () => {}
    function runPendingAction() {
        pendingAction();
    }
    
    onActionInvoked: {
        let action = () => {
            if (notificationsModelType === NotificationsModelType.WatchedNotificationsModel) {
                if (actionName === "") {
                    notificationsModel.invokeDefaultAction(model.notificationId, NotificationManager.None);
                } else {
                    notificationsModel.invokeAction(notificationItem.model.notificationId, actionName, NotificationManager.None);
                }
            } else if (notificationsModelType === NotificationsModelType.NotificationsModel) {
                if (actionName === "default") {
                    notificationsModel.invokeDefaultAction(notificationsModel.index(modelIndex, 0),  NotificationManager.Close); // notification closes 
                } else {
                    notificationsModel.invokeAction(notificationsModel.index(modelIndex, 0), actionName,  NotificationManager.Close); // notification closes 
                }
            }
            expire();
        }
        
        if (notificationItem.requestToInvoke) {
            pendingAction = action;
            runActionRequested();
        } else {
            action();
        }
    }
    
    onOpenUrl: {
        let action = () => {
            Qt.openUrlExternally(url);
            expire();
        }
        
        if (notificationItem.requestToInvoke) {
            pendingAction = action;
            runActionRequested();
        } else {
            action();
        }
    }
    
    onFileActionInvoked: {
        let action = () => {
            if (action.objectName === "movetotrash" || action.objectName === "deletefile") {
                close();
            } else {
                expire();
            }
        }
        
        if (notificationItem.requestToInvoke) {
            pendingAction = action;
            runActionRequested();
        } else {
            action();
        }
    }
    
    onSuspendJobClicked: {
        let action = () => notificationsModel.suspendJob(notificationsModel.index(modelIndex, 0));
        
        if (notificationItem.requestToInvoke) {
            pendingAction = action;
            runActionRequested();
        } else {
            action();
        }
    }
    
    onResumeJobClicked: {
        let action = () => notificationsModel.resumeJob(notificationsModel.index(modelIndex, 0));
        
        if (notificationItem.requestToInvoke) {
            pendingAction = action;
            runActionRequested();
        } else {
            action();
        }
    }
    
    onKillJobClicked: {
        let action = () => notificationsModel.killJob(notificationsModel.index(modelIndex, 0));
    
        if (notificationItem.requestToInvoke) {
            pendingAction = action;
            runActionRequested();
        } else {
            action();
        }
    }
}

