// SPDX-FileCopyrightText: 2023 Devin Lin <devin@kde.org>
// SPDX-License-Identifier: LGPL-2.0-or-later

import QtQuick 2.12
import QtQuick.Window 2.12
import QtQuick.Layouts 1.1

import org.kde.plasma.components 3.0 as PC3
import org.kde.plasma.private.mobileshell.state as MobileShellState
import org.kde.plasma.private.mobileshell.shellsettingsplugin as ShellSettings
import org.kde.taskmanager as TaskManager
import plasma.applet.org.kde.plasma.mobile.homescreen.folio as Folio
import org.kde.plasma.private.mobileshell as MobileShell
import org.kde.kirigami as Kirigami
import QtQuick.Controls as Controls
import QtQuick.Templates as T

import "./private"
import "./delegate"

MouseArea {
    id: root
    property Folio.HomeScreen folio
    property MobileShell.MaskManager maskManager

    property var homeScreen

    signal delegateDragRequested(var item)

    // Convergence mode: show running apps alongside favourites
    readonly property bool convergenceMode: ShellSettings.Settings.convergenceModeEnabled
    readonly property int totalItemCount: repeater.count + (convergenceMode ? taskRepeater.count : 0)

    // In convergence mode, size icons to fit the dock bar instead of using page grid cells
    readonly property real dockCellWidth: convergenceMode ? root.height : folio.HomeScreenState.pageCellWidth
    readonly property real dockCellHeight: convergenceMode ? root.height : folio.HomeScreenState.pageCellHeight

    // Navigation buttons width (used to offset center positioning)
    readonly property real navButtonWidth: convergenceMode ? root.height : 0

    // Center x for dock items (offset between nav buttons in convergence mode)
    readonly property real dockCenterX: convergenceMode
        ? navButtonWidth + (root.width - 2 * navButtonWidth) / 2
        : root.width / 2

    // Thumbnail popup hover tracking
    property int hoveredTaskIndex: -1

    // Home button (convergence mode, left end)
    Rectangle {
        id: homeButton
        visible: root.convergenceMode
        anchors.left: parent.left
        anchors.top: parent.top
        anchors.bottom: parent.bottom
        width: root.navButtonWidth
        color: homeMouseArea.containsPress
            ? Qt.rgba(255, 255, 255, 0.2)
            : (homeMouseArea.containsMouse ? Qt.rgba(255, 255, 255, 0.1) : "transparent")
        radius: Kirigami.Units.cornerRadius

        Kirigami.Icon {
            anchors.centerIn: parent
            width: Math.min(parent.width, parent.height) * 0.75
            height: width
            source: "start-here-kde"
            active: homeMouseArea.containsMouse
        }

        MouseArea {
            id: homeMouseArea
            anchors.fill: parent
            hoverEnabled: true
            onClicked: MobileShellState.ShellDBusClient.openHomeScreen()
        }

        Controls.ToolTip.text: i18n("Home")
        Controls.ToolTip.visible: homeMouseArea.containsMouse
        Controls.ToolTip.delay: Kirigami.Units.toolTipDelay
    }

    // Overview button (convergence mode, right end)
    Rectangle {
        id: overviewButton
        visible: root.convergenceMode
        anchors.right: parent.right
        anchors.top: parent.top
        anchors.bottom: parent.bottom
        width: root.navButtonWidth
        color: overviewMouseArea.containsPress
            ? Qt.rgba(255, 255, 255, 0.2)
            : (overviewMouseArea.containsMouse ? Qt.rgba(255, 255, 255, 0.1) : "transparent")
        radius: Kirigami.Units.cornerRadius

        Kirigami.Icon {
            anchors.centerIn: parent
            width: Math.min(parent.width, parent.height) * 0.75
            height: width
            source: "view-grid-symbolic"
            active: overviewMouseArea.containsMouse
        }

        MouseArea {
            id: overviewMouseArea
            anchors.fill: parent
            hoverEnabled: true
            onClicked: root.folio.triggerOverview()
        }

        Controls.ToolTip.text: i18n("Overview")
        Controls.ToolTip.visible: overviewMouseArea.containsMouse
        Controls.ToolTip.delay: Kirigami.Units.toolTipDelay
    }

    TaskManager.VirtualDesktopInfo {
        id: virtualDesktopInfo
    }

    TaskManager.ActivityInfo {
        id: activityInfo
    }

    TaskManager.TasksModel {
        id: tasksModel
        filterByVirtualDesktop: true
        filterByActivity: true
        filterNotMaximized: false
        filterByScreen: true
        filterHidden: false
        virtualDesktop: virtualDesktopInfo.currentDesktop
        activity: activityInfo.currentActivity
        groupMode: TaskManager.TasksModel.GroupDisabled
    }

    // Count how many windows share the same AppId as the task at the given index
    function windowCountForTask(taskIndex) {
        var appId = tasksModel.data(tasksModel.makeModelIndex(taskIndex), TaskManager.AbstractTasksModel.AppId)
        if (!appId) return 1
        var count = 0
        for (var i = 0; i < tasksModel.rowCount(); i++) {
            if (tasksModel.data(tasksModel.makeModelIndex(i), TaskManager.AbstractTasksModel.AppId) === appId)
                count++
        }
        return count
    }

    acceptedButtons: Qt.LeftButton | Qt.RightButton

    onPressAndHold: {
        folio.HomeScreenState.openSettingsView();
        haptics.buttonVibrate();
    }

    onClicked: (mouse) => {
        // Right-click opens settings view (wallpaper/widgets), same as long-press
        if (mouse.button === Qt.RightButton) {
            folio.HomeScreenState.openSettingsView();
        }
    }

    onDoubleClicked: {
        if (folio.FolioSettings.doubleTapToLock) {
            deviceLock.triggerLock();
        }
    }

    onActiveFocusChanged: {
        if (activeFocus) {
            // Focus on first delegate when favorites bar focused
            let firstDelegate = repeater.itemAt(0);
            if (!firstDelegate) {
                return;
            }
            firstDelegate.keyboardFocus();
        }
    }

    MobileShell.HapticsEffect {
        id: haptics
    }

    MobileShell.DeviceLock {
        id: deviceLock
    }

    Repeater {
        id: repeater
        model: folio.FavouritesModel

        delegate: Item {
            id: delegate

            readonly property var delegateModel: model.delegate
            readonly property int index: model.index

            readonly property var dragState: folio.HomeScreenState.dragState
            readonly property bool isDropPositionThis: dragState.candidateDropPosition.location === Folio.DelegateDragPosition.Favourites &&
                dragState.candidateDropPosition.favouritesPosition === delegate.index
            readonly property bool isAppHoveredOver: folio.HomeScreenState.isDraggingDelegate &&
                dragState.dropDelegate &&
                dragState.dropDelegate.type === Folio.FolioDelegate.Application &&
                isDropPositionThis

            readonly property bool isLocationBottom: folio.HomeScreenState.favouritesBarLocation === Folio.HomeScreenState.Bottom

            // get the normalized index position value from the center so we can animate it
            property double fromCenterValue: model.index - (root.totalItemCount / 2)
            Behavior on fromCenterValue {
                NumberAnimation { duration: Kirigami.Units.longDuration; easing.type: Easing.InOutQuad; }
            }

            // multiply the 'fromCenterValue' by the cell size to get the actual position
            readonly property int centerPosition: (isLocationBottom ? root.dockCellWidth : root.dockCellHeight) * fromCenterValue

            x: isLocationBottom ? centerPosition + root.dockCenterX : (parent.width - width) / 2
            y: isLocationBottom ? (parent.height - height) / 2 : parent.height / 2 - centerPosition - root.dockCellHeight

            implicitWidth: root.dockCellWidth
            implicitHeight: root.dockCellHeight
            width: root.dockCellWidth
            height: root.dockCellHeight

            // Keyboard navigation to other delegates
            Keys.onPressed: (event) => {
                switch (event.key) {
                case Qt.Key_Up:
                    if (!isLocationBottom) {
                        let nextDelegate = repeater.itemAt(delegate.index - 1);
                        if (nextDelegate) {
                            nextDelegate.keyboardFocus();
                            event.accepted = true;
                        }
                    }
                    break;
                case Qt.Key_Down:
                    if (!isLocationBottom) {
                        let nextDelegate = repeater.itemAt(delegate.index + 1);
                        if (nextDelegate) {
                            nextDelegate.keyboardFocus();
                            event.accepted = true;
                        }
                    }
                    break;
                case Qt.Key_Left:
                    if (isLocationBottom) {
                        let nextDelegate = repeater.itemAt(delegate.index - 1);
                        if (nextDelegate) {
                            nextDelegate.keyboardFocus();
                            event.accepted = true;
                        }
                    }
                    break;
                case Qt.Key_Right:
                    if (isLocationBottom) {
                        let nextDelegate = repeater.itemAt(delegate.index + 1);
                        if (nextDelegate) {
                            nextDelegate.keyboardFocus();
                            event.accepted = true;
                        }
                    }
                    break;
                }
            }

            function keyboardFocus() {
                if (loader.item) {
                    loader.item.keyboardFocus();
                }
            }

            Loader {
                id: loader
                anchors.fill: parent

                sourceComponent: {
                    if (delegate.delegateModel.type === Folio.FolioDelegate.Application) {
                        return appComponent;
                    } else if (delegate.delegateModel.type === Folio.FolioDelegate.Folder) {
                        return folderComponent;
                    } else {
                        // ghost entry
                        return placeholderComponent;
                    }
                }
            }

            Component {
                id: placeholderComponent

                // square that shows when hovering over a spot to drop a delegate on (ghost entry)
                PlaceholderDelegate {
                    id: dragDropFeedback
                    folio: root.folio
                    width: root.dockCellWidth
                    height: root.dockCellHeight
                }
            }

            Component {
                id: appComponent

                AppDelegate {
                    id: appDelegate
                    folio: root.folio
                    maskManager: root.maskManager
                    application: delegate.delegateModel.application
                    name: folio.FolioSettings.showFavouritesAppLabels ? delegate.delegateModel.application.name : ""
                    shadow: true

                    turnToFolder: delegate.isAppHoveredOver
                    turnToFolderAnimEnabled: folio.HomeScreenState.isDraggingDelegate

                    // do not show if the drop animation is running to this delegate
                    visible: !(root.homeScreen.dropAnimationRunning && delegate.isDropPositionThis)

                    // don't show label in drag and drop mode
                    labelOpacity: delegate.opacity

                    onPressAndHold: {
                        // prevent editing if lock layout is enabled
                        if (folio.FolioSettings.lockLayout) return;

                        let mappedCoords = root.homeScreen.prepareStartDelegateDrag(delegate.delegateModel, appDelegate.delegateItem);
                        folio.HomeScreenState.startDelegateFavouritesDrag(
                            mappedCoords.x,
                            mappedCoords.y,
                            appDelegate.pressPosition.x,
                            appDelegate.pressPosition.y,
                            delegate.index
                        );

                        contextMenu.open();
                        haptics.buttonVibrate();
                    }

                    onPressAndHoldReleased: {
                        // cancel the event if the delegate is not dragged
                        if (folio.HomeScreenState.swipeState === Folio.HomeScreenState.AwaitingDraggingDelegate) {
                            homeScreen.cancelDelegateDrag();
                        }
                    }

                    onRightMousePress: {
                        contextMenu.open();
                    }

                    ContextMenuLoader {
                        id: contextMenu
                        menuPopupType: root.convergenceMode ? T.Popup.Window : T.Popup.Item

                        // close menu when drag starts
                        Connections {
                            target: folio.HomeScreenState

                            function onDelegateDragStarted() {
                                contextMenu.close();
                            }
                        }

                        actions: [
                            Kirigami.Action {
                                icon.name: delegate.delegateModel.application.icon
                                text: i18n("Launch")
                                onTriggered: appDelegate.launchApp()
                            },
                            Kirigami.Action {
                                icon.name: "emblem-favorite"
                                text: i18n("Remove from Dock")
                                enabled: !folio.FolioSettings.lockLayout
                                onTriggered: folio.FavouritesModel.removeEntry(delegate.index)
                            }
                        ]
                    }
                }
            }

            Component {
                id: folderComponent

                AppFolderDelegate {
                    id: appFolderDelegate
                    folio: root.folio
                    maskManager: root.maskManager
                    shadow: true
                    folder: delegate.delegateModel.folder
                    name: folio.FolioSettings.showFavouritesAppLabels ? delegate.delegateModel.folder.name : ""

                    // do not show if the drop animation is running to this delegate, and the drop delegate is a folder
                    visible: !(root.homeScreen.dropAnimationRunning &&
                        delegate.isDropPositionThis &&
                        delegate.dragState.dropDelegate.type === Folio.FolioDelegate.Folder)

                    appHoveredOver: delegate.isAppHoveredOver

                    // don't show label in drag and drop mode
                    labelOpacity: delegate.opacity

                    onAfterClickAnimation: {
                        const pos = homeScreen.prepareFolderOpen(appFolderDelegate.contentItem);
                        folio.HomeScreenState.openFolder(pos.x, pos.y, delegate.delegateModel.folder);
                    }

                    onPressAndHold: {
                        let mappedCoords = root.homeScreen.prepareStartDelegateDrag(delegate.delegateModel, appFolderDelegate.delegateItem);
                        folio.HomeScreenState.startDelegateFavouritesDrag(
                            mappedCoords.x,
                            mappedCoords.y,
                            appFolderDelegate.pressPosition.x,
                            appFolderDelegate.pressPosition.y,
                            delegate.index
                        );

                        contextMenu.open();
                        haptics.buttonVibrate();
                    }

                    onPressAndHoldReleased: {
                        // cancel the event if the delegate is not dragged
                        if (folio.HomeScreenState.swipeState === Folio.HomeScreenState.AwaitingDraggingDelegate) {
                            root.homeScreen.cancelDelegateDrag();
                        }
                    }

                    onRightMousePress: {
                        contextMenu.open();
                    }

                    ContextMenuLoader {
                        id: contextMenu
                        menuPopupType: root.convergenceMode ? T.Popup.Window : T.Popup.Item

                        // close menu when drag starts
                        Connections {
                            target: folio.HomeScreenState

                            function onDelegateDragStarted() {
                                contextMenu.close();
                            }
                        }

                        actions: [
                            Kirigami.Action {
                                icon.name: "emblem-favorite"
                                text: i18n("Remove")
                                onTriggered: deleteDialog.open()
                            }
                        ]

                        ConfirmDeleteFolderDialogLoader {
                            id: deleteDialog
                            parent: root.homeScreen
                            onAccepted: folio.FavouritesModel.removeEntry(delegate.index)
                        }
                    }
                }
            }
        }
    }

    // Running-app task icons (convergence mode only)

    Timer {
        id: thumbnailShowTimer
        interval: Kirigami.Units.toolTipDelay
        onTriggered: {
            thumbnailPopup.visible = true
        }
    }

    Timer {
        id: thumbnailHideTimer
        interval: 300
        onTriggered: {
            thumbnailPopup.visible = false
            root.hoveredTaskIndex = -1
        }
    }

    Window {
        id: thumbnailPopup

        property var targetDelegate: null
        property string windowTitle: ""
        property string windowUuid: ""
        property bool popupHovered: false

        function open() { visible = true }
        function close() { visible = false }
        readonly property bool opened: visible

        flags: Qt.ToolTip | Qt.FramelessWindowHint | Qt.WindowDoesNotAcceptFocus
        color: "transparent"
        width: Kirigami.Units.gridUnit * 16
        height: popupContent.implicitHeight + 2 * Kirigami.Units.smallSpacing

        // Position above the hovered dock icon, in global coordinates
        x: {
            if (!targetDelegate) return 0
            var delegateGlobal = targetDelegate.mapToGlobal(0, 0)
            return Math.max(0, delegateGlobal.x + (targetDelegate.width - width) / 2)
        }
        y: {
            if (!targetDelegate) return 0
            var delegateGlobal = targetDelegate.mapToGlobal(0, 0)
            return delegateGlobal.y - height - Kirigami.Units.smallSpacing
        }

        onVisibleChanged: {
            if (!visible) {
                windowUuid = ""
                targetDelegate = null
            }
        }

        Rectangle {
            anchors.fill: parent
            color: Kirigami.Theme.backgroundColor
            border.color: Qt.rgba(
                Kirigami.Theme.textColor.r,
                Kirigami.Theme.textColor.g,
                Kirigami.Theme.textColor.b, 0.2)
            border.width: 1
            radius: Kirigami.Units.cornerRadius

            MouseArea {
                id: popupHoverArea
                anchors.fill: parent
                hoverEnabled: true

                onContainsMouseChanged: {
                    thumbnailPopup.popupHovered = containsMouse
                    if (containsMouse) {
                        thumbnailHideTimer.stop()
                    } else if (root.hoveredTaskIndex < 0) {
                        thumbnailHideTimer.restart()
                    }
                }

                onClicked: {
                    if (thumbnailPopup.targetDelegate) {
                        tasksModel.requestActivate(
                            tasksModel.makeModelIndex(thumbnailPopup.targetDelegate.index))
                        thumbnailPopup.close()
                    }
                }

                Column {
                    id: popupContent
                    anchors.fill: parent
                    anchors.margins: Kirigami.Units.smallSpacing
                    spacing: Kirigami.Units.smallSpacing

                    Item {
                        width: parent.width
                        height: width * 9 / 16

                        Loader {
                            id: pipeWireLoader
                            active: thumbnailPopup.visible
                                && thumbnailPopup.windowUuid !== ""
                            anchors.fill: parent
                            sourceComponent: PipeWireThumbnail {
                                windowUuid: thumbnailPopup.windowUuid
                            }
                        }

                        Kirigami.Icon {
                            anchors.centerIn: parent
                            width: Kirigami.Units.iconSizes.huge
                            height: width
                            source: thumbnailPopup.targetDelegate
                                ? thumbnailPopup.targetDelegate.model.decoration
                                : ""
                            visible: !pipeWireLoader.item
                                || !pipeWireLoader.item.hasThumbnail
                        }
                    }

                    PC3.Label {
                        width: parent.width
                        text: thumbnailPopup.windowTitle
                        elide: Text.ElideRight
                        horizontalAlignment: Text.AlignHCenter
                        maximumLineCount: 1
                    }
                }
            }
        }
    }

    Repeater {
        id: taskRepeater
        model: root.convergenceMode ? tasksModel : null

        delegate: Item {
            id: taskDelegate

            required property int index
            required property var model

            readonly property bool isLocationBottom: folio.HomeScreenState.favouritesBarLocation === Folio.HomeScreenState.Bottom

            // Position after all favourites
            property double fromCenterValue: (repeater.count + taskDelegate.index) - (root.totalItemCount / 2)
            Behavior on fromCenterValue {
                NumberAnimation { duration: Kirigami.Units.longDuration; easing.type: Easing.InOutQuad; }
            }

            readonly property int centerPosition: (isLocationBottom ? root.dockCellWidth : root.dockCellHeight) * fromCenterValue

            x: isLocationBottom ? centerPosition + root.dockCenterX : (parent.width - width) / 2
            y: isLocationBottom ? (parent.height - height) / 2 : parent.height / 2 - centerPosition - root.dockCellHeight

            implicitWidth: root.dockCellWidth
            implicitHeight: root.dockCellHeight
            width: root.dockCellWidth
            height: root.dockCellHeight

            // Hover highlight background
            Rectangle {
                anchors.fill: parent
                radius: Kirigami.Units.cornerRadius
                color: taskMouseArea.containsPress
                    ? Qt.rgba(255, 255, 255, 0.2)
                    : (taskMouseArea.containsMouse ? Qt.rgba(255, 255, 255, 0.1) : "transparent")
            }

            // Task icon
            Kirigami.Icon {
                anchors.centerIn: parent
                width: Math.min(parent.width, parent.height) * 0.6
                height: width
                source: taskDelegate.model.decoration
                active: taskMouseArea.containsMouse
            }

            // Window indicator dots (one per sibling window of the same app)
            Row {
                anchors.bottom: parent.bottom
                anchors.horizontalCenter: parent.horizontalCenter
                anchors.bottomMargin: Kirigami.Units.smallSpacing / 2
                spacing: Kirigami.Units.smallSpacing / 2

                Repeater {
                    model: root.windowCountForTask(taskDelegate.index)

                    Rectangle {
                        width: Kirigami.Units.smallSpacing * 1.5
                        height: width
                        radius: width / 2
                        color: Kirigami.Theme.highlightColor
                        opacity: taskDelegate.model.IsActive === true ? 1.0 : 0.4
                    }
                }
            }

            // Click to activate, hover for thumbnail preview
            MouseArea {
                id: taskMouseArea
                anchors.fill: parent
                hoverEnabled: true
                acceptedButtons: Qt.LeftButton | Qt.RightButton
                onClicked: (mouse) => {
                    if (mouse.button === Qt.RightButton) {
                        thumbnailPopup.close()
                        thumbnailShowTimer.stop()
                        taskContextMenu.open();
                    } else {
                        thumbnailPopup.close()
                        tasksModel.requestActivate(tasksModel.makeModelIndex(taskDelegate.index));
                    }
                }
                onContainsMouseChanged: {
                    if (containsMouse) {
                        thumbnailHideTimer.stop()
                        thumbnailPopup.targetDelegate = taskDelegate
                        thumbnailPopup.windowTitle = taskDelegate.model.display || ""
                        var winIds = taskDelegate.model.WinIdList
                        thumbnailPopup.windowUuid = (winIds && winIds.length > 0) ? winIds[0] : ""
                        root.hoveredTaskIndex = taskDelegate.index
                        if (!thumbnailPopup.opened) {
                            thumbnailShowTimer.restart()
                        }
                    } else {
                        root.hoveredTaskIndex = -1
                        if (!thumbnailPopup.popupHovered) {
                            thumbnailShowTimer.stop()
                            thumbnailHideTimer.restart()
                        }
                    }
                }
            }

            PC3.Menu {
                id: taskContextMenu
                popupType: T.Popup.Window

                property string taskStorageId: {
                    var id = taskDelegate.model.AppId || ""
                    if (id && !id.endsWith(".desktop"))
                        id += ".desktop"
                    return id
                }

                PC3.MenuItem {
                    icon.name: "window-pin"
                    text: i18n("Pin to Dock")
                    visible: taskContextMenu.taskStorageId !== "" && !folio.FavouritesModel.containsApplication(taskContextMenu.taskStorageId)
                    enabled: !folio.FolioSettings.lockLayout
                    onClicked: folio.FavouritesModel.addApplication(taskContextMenu.taskStorageId)
                }
                PC3.MenuItem {
                    icon.name: taskDelegate.model.IsMinimized ? "window-restore" : "window-minimize"
                    text: taskDelegate.model.IsMinimized ? i18n("Restore") : i18n("Minimize")
                    onClicked: tasksModel.requestToggleMinimized(tasksModel.makeModelIndex(taskDelegate.index))
                }
                PC3.MenuItem {
                    icon.name: taskDelegate.model.IsMaximized ? "window-restore" : "window-maximize"
                    text: taskDelegate.model.IsMaximized ? i18n("Restore") : i18n("Maximize")
                    onClicked: tasksModel.requestToggleMaximized(tasksModel.makeModelIndex(taskDelegate.index))
                }
                PC3.MenuItem {
                    icon.name: "window-close"
                    text: i18n("Close")
                    onClicked: tasksModel.requestClose(tasksModel.makeModelIndex(taskDelegate.index))
                }
            }
        }
    }
}
