// SPDX-License-Identifier: GPL-3.0-or-later

/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
*
* Copyright (C) 2013 - 2024, nymea GmbH
* Copyright (C) 2024 - 2025, chargebyte austria GmbH
*
* This file is part of nymea-app.
*
* nymea-app 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.
*
* nymea-app 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 nymea-app. If not, see <https://www.gnu.org/licenses/>.
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */

import QtQuick 2.9
import QtGraphicalEffects 1.0
import Nymea 1.0
import "../utils"

Item {
    id: root

    property Thing thing: null

    readonly property State colorState: thing ? thing.stateByName("color") : null
    readonly property State powerState: thing ? thing.stateByName("power") : null

    Connections {
        target: colorState
        onValueChanged: {
            if (actionQueue.pendingValue === null) {
                actionQueue.useStoredPoint = false
            }
        }
    }

    ActionQueue {
        id: actionQueue
        thing: root.thing
        stateType: thing.thingClass.stateTypes.findByName("color")

        property bool useStoredPoint: false
        property point storedPoint: Qt.point(0, 0)
    }

    ConicalGradient {
        id: gradient
        anchors.centerIn: parent
        width: Math.min(400, Math.min(parent.width, parent.height))
        height: width
        visible: false
        gradient: Gradient{
            id: g
            GradientStop { position: 0.000; color: Qt.rgba(1, 0, 0, 1) }
            GradientStop { position: 0.167; color: Qt.rgba(1, 1, 0, 1) }
            GradientStop { position: 0.333; color: Qt.rgba(0, 1, 0, 1) }
            GradientStop { position: 0.500; color: Qt.rgba(0, 1, 1, 1) }
            GradientStop { position: 0.667; color: Qt.rgba(0, 0, 1, 1) }
            GradientStop { position: 0.833; color: Qt.rgba(1, 0, 1, 1) }
            GradientStop { position: 1.000; color: Qt.rgba(1, 0, 0, 1) }
        }
        onWidthChanged: dragHandle.updatePoint()
        onHeightChanged: dragHandle.updatePoint()

        RadialGradient {
            anchors.fill: gradient
            gradient: Gradient{
                GradientStop { position: 0.05; color: Qt.rgba(1, 1, 1, 1) }
                GradientStop { position: 0.10; color: Qt.rgba(1, 1, 1, .9) }
                GradientStop { position: 0.20; color: Qt.rgba(1, 1, 1, .7) }
                GradientStop { position: 0.30; color: Qt.rgba(1, 1, 1, .5) }
                GradientStop { position: 0.40; color: Qt.rgba(1, 1, 1, .3) }
                GradientStop { position: 0.50; color: "transparent" }
            }
        }
    }

    Desaturate {
        id: colorizer
        anchors.fill: gradient
        source: gradient
        desaturation: root.powerState.value === true ? 0 : 1
        Behavior on desaturation { NumberAnimation { duration: Style.animationDuration } }
        visible: false
    }

    Rectangle {
        id: mask
        anchors.fill: gradient
        radius: width / 2
    }
    OpacityMask {
        anchors.fill: gradient
        source: colorizer
        maskSource: mask
    }


    Rectangle {
        id: dragHandle
        width: 20
        height: 20
        radius: height / 2
        color: Style.backgroundColor
        border.color: Style.foregroundColor
        border.width: 2

        x: point.x + gradient.width / 2 + gradient.x - width / 2
        y: point.y + gradient.height / 2 + gradient.y - height / 2

        property color shownColor: root.colorState ? actionQueue.pendingValue || root.colorState.value : "white`"
        onShownColorChanged: updatePoint()
//        Component.onCompleted: updatePoint()

        property point point: Qt.point(0,0);
        function updatePoint() {

            if (actionQueue.useStoredPoint) {
                point = actionQueue.storedPoint
                return
            }

//            print("current color:", shownColor.r, shownColor.g, shownColor.b)

            var whitePart = Math.min(Math.min(shownColor.r, shownColor.g), shownColor.b)

            var stopIndex = 0
            var progressInStop = 0
            if (shownColor.r === 1) {
                if (shownColor.g > shownColor.b) {
                    stopIndex = 0
                    progressInStop = shownColor.g - whitePart
                } else {
                    stopIndex = 5
                    progressInStop = 1 - shownColor.b + whitePart
                }
            }
            if (shownColor.g === 1) {
                if (shownColor.r > shownColor.b) {
                    stopIndex = 1
                    progressInStop = 1 - shownColor.r + whitePart
                } else {
                    stopIndex = 2
                    progressInStop = shownColor.b - whitePart
                }
            }
            if (shownColor.b === 1) {
                if (shownColor.r > shownColor.g) {
                    stopIndex = 4
                    progressInStop = shownColor.r - whitePart
                } else {
                    stopIndex = 3
                    progressInStop = 1-shownColor.g + whitePart
                }
            }

            var stopBefore = g.stops[stopIndex]
            var stopAfter = g.stops[stopIndex+1]

//            print("stopIndex", stopIndex)
//            print("stopBefore:", stopBefore.color.r, stopBefore.color.g, stopBefore.color.b)
//            print("stopAfter:", stopAfter.color.r, stopAfter.color.g, stopAfter.color.b)
//            print("progressInStop", progressInStop)


//            print("beforePosition", stopBefore.position)

            var positionInGradient = stopBefore.position + (stopAfter.position - stopBefore.position) * progressInStop

//            print("positionInGradient", positionInGradient)

            var degrees = 360 * positionInGradient;
            degrees -= 90;

            var radian = degrees * 0.0174532925

            var radius = gradient.height * 0.9 / 2 * (1-whitePart)


            var x = radius * Math.cos(radian)
            var y = radius * Math.sin(radian)

//            print("degrees", degrees)
//            print("radius", radius)

//            print("Setting point to", x, y)
            point = Qt.point(x, y)
        }
    }

    MouseArea {
        id: mouseArea
        anchors.fill: gradient
        onPositionChanged: {

            var angle = calculateAngle(mouseX, mouseY)
            var position = angle / 360;

            var stopBefore = null;
            var stopAfter = null;
            for (var i = 0; i < g.stops.length; i++) {
                var stop = g.stops[i];
                if (stop.position < position) {
                    stopBefore = stop;
                    continue
                }
                stopAfter = stop;
                break;
            }

            var colorBefore = stopBefore.color
            var colorAfter = stopAfter.color
            // p : 1 = pis : (a - b)
            var positionInStop = (position - stopBefore.position) / (stopAfter.position - stopBefore.position);

            var dr = stopAfter.color.r - stopBefore.color.r;
            var dg = stopAfter.color.g - stopBefore.color.g;
            var db = stopAfter.color.b - stopBefore.color.b;

            var distanceFromCenter = calculateDistance(mouseX, mouseY)

            // Reduce width a bit to keep outside circle for full color intensity
            var positionFromCenter = Math.min(distanceFromCenter / (width * 0.9 / 2), 1)
            // Invert it, The further we're away, the less impact this should have
            positionFromCenter = 1 - positionFromCenter;
            print("pos", positionFromCenter)

            var color = Qt.rgba((stopBefore.color.r + dr * positionInStop) + positionFromCenter,
                                (stopBefore.color.g + dg * positionInStop) + positionFromCenter,
                                (stopBefore.color.b + db * positionInStop) + positionFromCenter,
                                1)

            actionQueue.sendValue(color);

            // Store the coordinates (limited to the circle) as the above calculation is lossy so we can't precicely
            // calcuate the position from the color but we don't want the drag handle jumping while dragging.
            var rad = (angle - 90) / 180 * Math.PI
            var radius = Math.min(distanceFromCenter, width * 0.9 / 2)
            actionQueue.storedPoint = Qt.point(radius * Math.cos(rad), radius * Math.sin(rad))
            actionQueue.useStoredPoint = true
        }

        function calculateAngle(mouseX, mouseY) {
            // transform coords to center of dial
            mouseX -= mouseArea.width / 2
            mouseY -= mouseArea.height / 2

            var rad = Math.atan(mouseY / mouseX);
            var angle = rad * 180 / Math.PI

            angle += 90;

            if (mouseX < 0 && mouseY >= 0) angle = 180 + angle;
            if (mouseX < 0 && mouseY < 0) angle = 180 + angle;

            return angle;
        }

        function calculateDistance(mouseX, mouseY) {
            mouseX -= mouseArea.width / 2
            mouseY -= mouseArea.height / 2

            return Math.abs(Math.sqrt(Math.pow(mouseX, 2) + Math.pow(mouseY, 2)))
        }
    }
}
