/*
 *  Komplex Wallpaper Engine
 *  Copyright (C) 2025 @DigitalArtifex | github.com/DigitalArtifex
 *
 *  ShaderChannelConfiguration.qml
 *  
 *  This component is used to configure the shader channels for the output shader.
 *  It allows the user to select a file or folder for each channel type (Image,
 *  Video, Shader, Audio, CubeMap) and sets the appropriate properties on the
 *  ShaderChannel component.
 *
 *  --------------------------------------------------------------------------------------------------------
 *
 *  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program.  If not, see <https://www.gnu.org/licenses/>
 */
pragma ComponentBehavior: Bound

import QtCore
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import org.kde.kirigami as Kirigami
import org.kde.plasma.core as PlasmaCore
import QtCore
import Qt.labs.folderlistmodel 2.15

import QtQuick.Dialogs as Dialogs
import com.github.digitalartifex.komplex 1.0 as Komplex

Item
{
    signal accepted()
    signal rejected()

    property bool file: true
    property string selectionTitle: "Select a file"
    property var selectionFilter: ["All Files (*)"]

    // The current folder is used to set the initial folder for the file and folder dialogs
    property string currentFolder: StandardPaths.standardLocations(StandardPaths.HomeLocation)[0]

    // Individual folders for each channel type
    property string shaderFolder: shaderPackModel.shadersPath
    property string imageFolder: shaderPackModel.imagesPath
    property string videoFolder: shaderPackModel.videosPath
    property string cubemapFolder: shaderPackModel.cubeMapsPath
    property string sceneFolder: shaderPackModel.scenesPath

    property alias tmp_source: sourceEdit.text
    property int tmp_type: 1
    property alias tmp_timeScale: speedSlider.value
    property alias tmp_resolution_scale: resolutionScaleSlider.value
    property alias tmp_resolution_x: resolutionXEdit.value
    property alias tmp_resolution_y: resolutionYEdit.value
    property alias tmp_mirroring: textureMirroringSelector.currentIndex
    property alias tmp_wrap: textureWrapSelector.currentIndex
    property bool tmp_enabled: tmp_source !== ""
    property Palette palette

    property string source
    property int type
    property real timeScale
    property real resolution_scale
    property int resolution_x
    property int resolution_y
    property int textureMirroring
    property int textureWrapping
    property bool enabled
    property bool changed

    id: window

    Komplex.ShaderPackModel
    {
        id: shaderPackModel
    }

    // The selection model contains the list of available shader channel types
    // and their respective properties.
    ListModel
    {
        id: selectionModel

        ListElement
        {
            file: true
            name: "Audio"
            icon: "./icons/audio.svg"
            type: ShaderChannel.Type.AudioChannel
        }

        ListElement
        {
            file: false
            name: "Cubemap"
            icon: "./icons/cube.svg"
            title: "Select a CubeMap folder"
            filter: ""
            type: ShaderChannel.Type.CubeMapChannel
        }

        ListElement
        {
            file: true
            name: "Image"
            icon: "./icons/camera.svg"
            title: "Select an Image File"
            filter: "Image Files (*.jpg *.jpeg *.png *.svg *.gif *.tiff *.webp)"
            type: ShaderChannel.Type.ImageChannel
        }

        ListElement
        {
            file: true
            name: "Scene"
            icon: "./icons/image.svg"
            title: "Select a scene file"
            filter: "QML Scene Files (*.qml)"
            type: ShaderChannel.Type.SceneChannel
        }

        ListElement
        {
            file: true
            name: "Shader"
            icon: "./icons/3d-glasses.svg"
            title: "Select a Shader File"
            filter: "Shader Files (*frag.qsb)"
            type: ShaderChannel.Type.ShaderChannel
        }

        ListElement
        {
            file: true
            name: "Video"
            icon: "./icons/video.svg"
            title: "Select a Video File"
            filter: "Video Files (*.mov *.avi *.mkv *.mp4)"
            type: ShaderChannel.Type.VideoChannel
        }

        function indexOf(type) 
        {
            for(var i = 0; i < count; ++i) if (get(i).type == type) 
                return i

            return -1
        }
    }

    Component
    {
        id: selectionDelegate

        Item
        {
            required property int index
            required property int type
            required property string name
            required property string icon
            required property string title
            required property var filter
            required property bool file
                
            width: 100
            height: 75

            ColumnLayout
            {
                anchors.fill: parent
                Image
                {
                    source: icon

                    Layout.preferredHeight: 50
                    Layout.preferredWidth: 50
                    Layout.alignment: Qt.AlignTop | Qt.AlignHCenter
                }

                Text
                {
                    id: label
                    text: name
                    color: "white"

                    horizontalAlignment: Text.AlignHCenter

                    Layout.fillHeight: false
                    Layout.fillWidth: true
                    Layout.alignment: Qt.AlignBottom
                }
            }

            MouseArea
            {
                hoverEnabled: true
                anchors.fill: parent
                z: 9000 // this is dumb but I can't get the mouse area to propogate without it

                onClicked: 
                {
                    list.currentIndex = parent.index
                    window.tmp_type = parent.type
                    window.selectionFilter = parent.filter.split(':')
                    window.selectionTitle = parent.title
                    window.file = parent.file

                    var source = ""

                    if(parent.type === window.type)
                        source = window.source

                    switch(parent.type) 
                    {
                        case ShaderChannel.Type.CubeMapChannel:
                            window.currentFolder = window.cubemapFolder
                            window.tmp_source = source
                            break;
                        case ShaderChannel.Type.ImageChannel:
                            window.currentFolder = window.imageFolder
                            window.tmp_source = source
                            break;
                        case ShaderChannel.Type.ShaderChannel:
                            window.currentFolder = window.shaderFolder + "/generative"
                            window.tmp_source = source
                            break;
                        case ShaderChannel.Type.VideoChannel:
                            window.currentFolder = window.videoFolder
                            window.tmp_source = source
                            break;
                        case ShaderChannel.Type.SceneChannel:
                            window.currentFolder = window.sceneFolder
                            window.tmp_source = source
                            break;
                        case ShaderChannel.Type.AudioChannel:
                            window.currentFolder = window.videoFolder
                            window.tmp_source = "Desktop Audio Channel"
                            break;
                    }
                }
            }
        }
    }

    Component 
    {
        id: highlight
        Rectangle 
        {
            width: list.currentItem.width; height: list.currentItem.height
            color: "lightsteelblue"; radius: 5
            y: list.currentItem.y
            Behavior on y 
            {
                SpringAnimation 
                {
                    spring: 2
                    damping: 0.1
                }
            }
        }
    }

    ColumnLayout
    {
        anchors.fill: parent
        spacing: 10

        ListView
        {
            id: list
            model: selectionModel

            Layout.fillWidth: true
            Layout.preferredHeight: 100
            Layout.alignment: Qt.AlignTop

            delegate: selectionDelegate
            orientation: Qt.Horizontal
            clip: true

            highlight: highlight
            highlightFollowsCurrentItem: true
            focus: true
        }

        RowLayout
        {
            visible: window.tmp_type != ShaderChannel.Type.AudioChannel
            Layout.alignment: Qt.AlignTop

            Label
            {
                color: palette.text
                verticalAlignment: Text.AlignVCenter
                text: i18nd("@option:shader_source_label", "Source")

                Layout.preferredWidth: Kirigami.Units.gridUnit * 6
                Layout.preferredHeight: Kirigami.Units.gridUnit * 2
            }

            TextField
            {
                id: sourceEdit
                Layout.fillWidth: true
                Layout.preferredHeight: Kirigami.Units.gridUnit * 2
            }

            Button
            {
                icon.name: "folder-symbolic"
                Layout.preferredWidth: Kirigami.Units.gridUnit * 2
                Layout.preferredHeight: Kirigami.Units.gridUnit * 2

                onClicked: 
                {
                    if(window.file === true)
                        fileDialog.open()
                    else
                        folderDialog.open()
                }
            }

            Button
            {
                visible: window.tmp_type === ShaderChannel.ImageChannel
                icon.name: "network-symbolic"
                Layout.preferredWidth: Kirigami.Units.gridUnit * 2
                Layout.preferredHeight: Kirigami.Units.gridUnit * 2

                onClicked: 
                {
                    pexelsImageDialog.open()
                }
            }

            Button
            {
                visible: window.tmp_type === ShaderChannel.VideoChannel
                icon.name: "network-symbolic"
                Layout.preferredWidth: Kirigami.Units.gridUnit * 2
                Layout.preferredHeight: Kirigami.Units.gridUnit * 2

                onClicked: 
                {
                    pexelsVideoDialog.open()
                }
            }

            Button
            {
                visible: window.tmp_type === ShaderChannel.CubeMapChannel
                icon.name: "network-symbolic"
                Layout.preferredWidth: Kirigami.Units.gridUnit * 2
                Layout.preferredHeight: Kirigami.Units.gridUnit * 2

                onClicked: 
                {
                    cubemapHubDialog.open()
                }
            }
        }

        RowLayout 
        {
            Layout.alignment: Qt.AlignTop
            id: speedLayout

            Kirigami.FormData.label: i18nd("com.github.digitalartifex.komplex", "Shader speed:")

            Label
            {
                color: palette.text
                verticalAlignment: Qt.AlignVCenter
                Layout.preferredWidth: Kirigami.Units.gridUnit * 6

                text: i18nd("@option:time_scale_label", "Shader Speed")
            }

            Slider 
            {
                id: speedSlider
                Layout.fillWidth: true
                from: 0
                to: 4
                stepSize: 0.1
                onValueChanged: shaderSpeedField.text = String(value.toFixed(2));
            }

            TextField 
            {
                id: shaderSpeedField
                inputMethodHints: Qt.ImhFormattedNumbersOnly
                horizontalAlignment: Text.AlignRight
                Layout.preferredWidth: Kirigami.Units.gridUnit * 4
                onEditingFinished: () =>
                {
                    let inputValue = parseFloat(text);

                    if (isNaN(inputValue) || inputValue < speedSlider.from)
                        inputValue = speedSlider.from;
                    else if (inputValue > speedSlider.to)
                        inputValue = speedSlider.to;

                    text = inputValue.toFixed(2);
                    speedSlider.value = inputValue;
                }

                Keys.onPressed: (event) => 
                {
                    if (event.key === Qt.Key_Return || event.key === Qt.Key_Enter) 
                    {
                        shaderSpeedField.focus = false; // Unfocus the TextField
                        event.accepted = true; // Prevent further propagation of the key event
                    }
                }

                background: Rectangle 
                {
                    color: shaderSpeedField.activeFocus ? palette.base : "transparent"
                    border.color: shaderSpeedField.activeFocus ? palette.highlight : "transparent"
                    border.width: 1
                    radius: 4
                    anchors.fill: shaderSpeedField
                    anchors.margins: -2
                }
            }
        }

        RowLayout 
        {
            Layout.alignment: Qt.AlignTop
            id: resolutionScaleLayout

            Kirigami.FormData.label: i18nd("com.github.digitalartifex.komplex", "Shader speed:")

            Label
            {
                color: palette.text
                verticalAlignment: Qt.AlignVCenter
                text: i18nd("@option:time_scale_label", "Resolution Scale")
                Layout.preferredWidth: Kirigami.Units.gridUnit * 6
            }

            Slider 
            {
                id: resolutionScaleSlider
                Layout.fillWidth: true
                from: 0.125
                to: 1
                stepSize: 0.01
                onValueChanged: resolutionScaleField.text = String(value.toFixed(3));
            }

            TextField 
            {
                id: resolutionScaleField
                inputMethodHints: Qt.ImhFormattedNumbersOnly
                horizontalAlignment: Text.AlignRight

                Layout.preferredWidth: Kirigami.Units.gridUnit * 4

                onEditingFinished: () =>
                {
                    let inputValue = parseFloat(text);

                    if (isNaN(inputValue) || inputValue < resolutionScaleSlider.from)
                        inputValue = resolutionScaleSlider.from;
                    else if (inputValue > resolutionScaleSlider.to)
                        inputValue = resolutionScaleSlider.to;

                    text = inputValue.toFixed(3);
                    resolutionScaleSlider.value = inputValue;
                }
                Keys.onPressed: (event) => 
                {
                    if (event.key === Qt.Key_Return || event.key === Qt.Key_Enter) 
                    {
                        resolutionScaleField.focus = false; // Unfocus the TextField
                        event.accepted = true; // Prevent further propagation of the key event
                    }
                }
                background: Rectangle 
                {
                    color: resolutionScaleField.activeFocus ? palette.base : "transparent"
                    border.color: resolutionScaleField.activeFocus ? palette.highlight : "transparent"
                    border.width: 1
                    radius: 4
                    anchors.fill: resolutionScaleField
                    anchors.margins: -2
                }
            }
        }

        RowLayout
        {
            Layout.alignment: Qt.AlignTop
            Layout.fillWidth: true

            Label
            {
                color: palette.text
                text: i18nd("@option:resolution_label_x", "Resolution X")
                Layout.preferredWidth: Kirigami.Units.gridUnit * 6
            }

            TextField
            {
                property int value
                
                Layout.preferredHeight: 35
                Layout.fillWidth: true

                id: resolutionXEdit
                text: value
                onEditingFinished: () =>
                {
                    var inputValue = parseInt(text);

                    if (isNaN(inputValue) || inputValue < 0)
                        inputValue = 0

                    value = inputValue;
                }
                Keys.onPressed: (event) => 
                {
                    if (event.key === Qt.Key_Return || event.key === Qt.Key_Enter) 
                    {
                        resolutionScaleField.focus = false; // Unfocus the TextField
                        event.accepted = true; // Prevent further propagation of the key event
                    }
                }
            }
            Label
            {
                color: palette.text
                text: i18nd("@option:resolution_label_y", "Resolution Y")
                Layout.preferredWidth: Kirigami.Units.gridUnit * 6
            }

            TextField
            {
                property int value
                
                Layout.preferredHeight: 35
                Layout.fillWidth: true

                id: resolutionYEdit
                text: value
                onEditingFinished: () =>
                {
                    var inputValue = parseInt(text);

                    if (isNaN(inputValue) || inputValue < 0)
                        inputValue = 0

                    value = inputValue;
                }
                Keys.onPressed: (event) => 
                {
                    if (event.key === Qt.Key_Return || event.key === Qt.Key_Enter) 
                    {
                        resolutionScaleField.focus = false; // Unfocus the TextField
                        event.accepted = true; // Prevent further propagation of the key event
                    }
                }
            }
        }

        RowLayout
        {
            Layout.alignment: Qt.AlignTop
            Layout.fillWidth: true

            RowLayout
            {
                Layout.fillWidth: true
                Label
                {
                    text: "Texture Mirroring"
                    color: palette.text
                    Layout.preferredHeight: 35
                    verticalAlignment: Text.AlignVCenter
                }

                ComboBox
                {
                    id: textureMirroringSelector
                    Layout.fillWidth: true
                    Layout.preferredHeight: 35

                    model: ListModel {
                        ListElement { text: "No Mirroring" }
                        ListElement { text: "Mirror Horizontally" }
                        ListElement { text: "Mirror Vertically" }
                    }

                    onCurrentIndexChanged: () =>
                    {
                        mainItem.tmp_mirroring = currentIndex
                    }
                }
            }

            RowLayout
            {
                Layout.fillWidth: true
                Label
                {
                    text: "Texture Wrap Mode"
                    color: palette.text
                    Layout.preferredHeight: 35
                    verticalAlignment: Text.AlignVCenter
                }

                ComboBox
                {
                    id: textureWrapSelector
                    Layout.fillWidth: true
                    Layout.preferredHeight: 35

                    model: ListModel {
                        ListElement { text: "Clamp To Edge" }
                        ListElement { text: "Repeat Horizontally" }
                        ListElement { text: "Repeat Vertically" }
                        ListElement { text: "Repeat" }
                    }

                    onCurrentIndexChanged: () =>
                    {
                        mainItem.tmp_wrap = currentIndex
                    }
                }
            }
        }

        Rectangle
        {
            color: "transparent"
            Layout.fillHeight: true
        }

        RowLayout
        {
            Layout.fillWidth: true
            Layout.alignment: Qt.AlignBottom | Qt.AlignRight
            Layout.bottomMargin: 5


            Button
            {
                id: okayButton
                text: "Okay"
                onClicked: window.accept()

                Layout.alignment: Qt.AlignRight
            }

            Button
            {
                id: cancelButton
                text: "Cancel"
                onClicked: window.reject()

                Layout.alignment: Qt.AlignRight
            }
        }
    }

    // FileDialog is used to select a file for the Image, Shader, and Video channels
    Dialogs.FileDialog
    {
        id: fileDialog
        currentFolder: "file://" + window.currentFolder
        nameFilters: window.selectionFilter
        title: window.selectionTitle

        onAccepted: window.tmp_source = selectedFile
    }

    // FolderDialog is used to select a folder for the CubeMap channel
    Dialogs.FolderDialog
    {
        id: folderDialog
        currentFolder: window.cubemapFolder
        title: window.selectionTitle

        onAccepted: window.tmp_source = selectedFolder
    }

    Kirigami.OverlaySheet
    {
        title: "Pexels Image Import"
        id: pexelsImageDialog
        implicitWidth: 960
        implicitHeight: 480
        parent: applicationWindow().overlay

        PexelsImageHub
        {
            id: pexelsImageHub 
            width: pexelsVideoDialog.width - 10
            height: pexelsVideoDialog.height - 40

            onSelectedFileChanged: () =>
            {
                window.tmp_source = pexelsImageHub.selectedFile

                if(selectedFile === "" || selectedFile === undefined)
                    return;

                pexelsImageDialog.close()
            }
        }
    }

    Kirigami.OverlaySheet
    {
        title: "Cubemap Import"
        id: cubemapHubDialog
        implicitWidth: 960
        implicitHeight: 480
        parent: applicationWindow().overlay

        CubemapHub
        {
            id: cubemapHub 
            width: cubemapHubDialog.width - 10
            height: cubemapHubDialog.height - 40

            onSelectedFileChanged: () =>
            {
                window.tmp_source = cubemapHub.selectedFile

                if(selectedFile === "" || selectedFile === undefined)
                    return;

                cubemapHubDialog.close()
            }
        }
    }

    Kirigami.OverlaySheet
    {
        title: "Pexels Video Import"
        id: pexelsVideoDialog
        implicitWidth: 960
        implicitHeight: 480
        parent: applicationWindow().overlay

        PexelsVideoHub
        {
            id: pexelsVideoHub 
            width: pexelsVideoDialog.width - 10
            height: pexelsVideoDialog.height - 40

            onSelectedFileChanged: () =>
            {
                window.tmp_source = pexelsVideoHub.selectedFile

                if(selectedFile === "" || selectedFile === undefined)
                    return;

                pexelsVideoDialog.close()
            }
        }
    }

    function accept()
    {
        // copy over temp values
        type = tmp_type
        source = tmp_source
        timeScale = tmp_timeScale
        resolution_scale = tmp_resolution_scale
        resolution_x = tmp_resolution_x
        resolution_y = tmp_resolution_y
        enabled = tmp_enabled
        textureMirroring = tmp_mirroring
        textureWrapping = tmp_wrap

        // Emit the accepted signal and reset the selection
        window.accepted()
    }

    function reject()
    {
        // Emit the rejected signal and reset the selection
        resetSelection()
        window.rejected()
    }

    function configureChannel()
    {
        resetSelection()
    }

    // Function to update the current selection based on the channel type
    function updateCurrentSelection()
    {
        // Set the dialog properties based on the channel properties
        switch(type) 
        {
            case ShaderChannel.Type.CubeMapChannel:
                window.currentFolder = window.cubemapFolder
                break;
            case ShaderChannel.Type.ImageChannel:
                window.currentFolder = window.imageFolder
                break;
            case ShaderChannel.Type.ShaderChannel:
                window.currentFolder = window.shaderFolder
                break;
            case ShaderChannel.Type.VideoChannel:
                window.currentFolder = window.videoFolder
                break;
            case ShaderChannel.Type.AudioChannel:
                window.tmp_source = "Audio Channel"
                break;
        }

        // Set the current selection index
        list.currentIndex = selectionModel.indexOf(type)
    }

    // Function to reset the selection to default values
    function resetSelection()
    {
        if((tmp_source !== source) || (tmp_enabled !== enabled) ||
        (tmp_mirroring !== textureMirroring) || (tmp_resolution_scale !== resolution_scale) ||
        (tmp_resolution_x !== resolution_x) || (tmp_resolution_y !== resolution_y) ||
        (tmp_timeScale !== timeScale) || (tmp_type !== type) || (tmp_wrap !== textureWrapping))
            changed = true;

        tmp_source = source
        tmp_timeScale = timeScale
        tmp_resolution_scale = resolution_scale
        tmp_resolution_x = resolution_x
        tmp_resolution_y = resolution_y
        tmp_mirroring = textureMirroring
        tmp_wrap = textureWrapping

        tmp_type = type
        updateCurrentSelection()
    }
}