/*!
 *
 * Copyright (c) 2021-2025 Diality Inc. - All Rights Reserved.
 * \copyright
 * THIS CODE MAY NOT BE COPIED OR REPRODUCED IN ANY FORM, IN PART OR IN
 * WHOLE, WITHOUT THE EXPLICIT PERMISSION OF THE COPYRIGHT OWNER.
 *
 * \file    ValueAdjuster.qml
 * \author  (last)      Nico Ramirez
 * \date    (last)      21-Aug-2025
 * \author  (original)  Nico Ramirez
 * \date    (original)  21-Aug-2025
 *
 */

import "qrc:/globals"
import "qrc:/components"

import QtQuick 2.12
import QtQuick.Controls 2.2

Item { id: _root
    property real   decimal     : 0
    property real   minimum     : 0
    property real   maximum     : 0
    property real   value       : 0
    property real   defaultValue: 0
    property real   step        : 0
    property bool   isActive    : false
    property bool   editable    : true
    property bool   canOff      : false
    property bool   canRefresh  : false
    property alias  textColor   : _text.color
    property bool   grabbed     : false

    QtObject { id: _private
        // fix floating-point precision issue
        readonly property int  multiplier   : Math.pow(10, decimal)
        readonly property real stepVal      : calculatePrecisionValue(step)
        readonly property real minVal       : calculatePrecisionValue(minimum)
        readonly property real maxVal       : calculatePrecisionValue(maximum)
        readonly property real val          : calculatePrecisionValue(value)

        readonly property bool canIncrement  : isActive ? val    < calculateMaximum() : true
        readonly property bool canDecrement  : isActive ? canOff ? val > 0 :
                                                                   val > calculateMinimum() : true
                                                           
        // round the value based on the given precision (not step)
        function calculatePrecisionValue(value) {
            return Math.round(value * _private.multiplier) / _private.multiplier
        }

        // calculate the minimum value rounded up to the next higher step size
        function calculateMinimum() {
            let fixedMin    = _private.fixedValue(_private.minVal)
            let fixedStep   = _private.fixedValue(_private.stepVal)
            return (Math.ceil(fixedMin / fixedStep) * fixedStep) / _private.multiplier
        }

        // calculate the maximum value rounded down to the next higher step size
        function calculateMaximum() {
            let fixedMax    = _private.fixedValue(_private.maxVal)
            let fixedStep   = _private.fixedValue(_private.stepVal)
            return (Math.floor(fixedMax / fixedStep) * fixedStep) / _private.multiplier
        }

        // return a fixed point int from the inputted float (using the set precision)
        function fixedValue(value) {
            return Math.round(value * _private.multiplier)
        }

        function increment() {
            if ( ! isActive ) { didActiveChange(true); return }

            let tValue = _private.val
            if (canOff && _private.val < _private.minVal) {
                tValue = _private.minVal
            }
            else {
                let fixedVal    = _private.fixedValue(_private.val)
                let fixedStep   = _private.fixedValue(_private.stepVal)
                let fixedDelta  = fixedStep - (fixedVal % fixedStep)
                tValue = (fixedVal + fixedDelta) / _private.multiplier
                tValue = Math.min(_private.maxVal, tValue)
            }

            didChange(tValue)
        }

        function decrement() {
            if ( ! isActive ) { didActiveChange(true); return }

            let tValue = _private.val
            if (canOff && _private.val === _private.minVal) {
                tValue = 0
            }
            else {
                let fixedVal    = _private.fixedValue(_private.val)
                let fixedStep   = _private.fixedValue(_private.stepVal)
                let fixedDelta  = fixedVal % fixedStep
                tValue = (fixedVal - (fixedDelta > 0 ? fixedDelta : fixedStep)) / _private.multiplier
                tValue = Math.max(_private.minVal, tValue)
            }

            didChange(tValue)
        }
    }

    signal didChange        (real vValue)
    signal didActiveChange  (bool vState)

    onIsActiveChanged             : {
        if ( canRefresh )   { canRefresh = false; return    }

        if ( isActive )     { didChange(_root.defaultValue) }
    }

    function refresh()      { canRefresh    = true          }

    function clear()        { didActiveChange(false)        }

    Text { id: _text
        anchors.centerIn: parent
        text            : _root.isActive ? _root.canOff ? _root.value === 0 ?
                                                          qsTr("OFF") :
                                                          _root.value.toFixed( _root.decimal ) :
                                        _root.value.toFixed( _root.decimal ) : Variables.emptyEntry
        color           : Colors.offWhite
        font.pixelSize  : Fonts.fontPixelValueControl
    }

    Slider { id: _slider
        property real pos           : 0

        anchors.fill        : parent
        anchors.rightMargin : Variables.defaultMargin * 3
        anchors.leftMargin  : Variables.defaultMargin * 2
        anchors.topMargin   : 5
        enabled             : _root.editable
        visible             : enabled
        opacity             : 0
        stepSize            : _root.step
        from                : _root.minimum .toFixed ( _root.decimal )
        to                  : _root.maximum .toFixed ( _root.decimal )
        value               : _root.value   .toFixed ( _root.decimal )
        snapMode            : Slider.SnapOnRelease

        background: Rectangle {
            color: "transparent"
            Rectangle {
                anchors{
                    top         : parent.top
                    topMargin   : 1.5
                    left        : parent.left
                    right       : parent.right
                    rightMargin : Variables.defaultMargin * -1
                }
                height  : 1
                width   : parent.width
                color   : Colors.panelBorderColor
            }
        }

        handle: Rectangle { id: _knob
            width   : 20
            height  : 4
            radius  : height
            color   : Colors.borderButton
            x       : _slider.pos * _slider.width
        }

        MouseArea { id: _sliderMouseArea

            anchors.fill        : parent
            pressAndHoldInterval: 0
            drag.axis           : Drag.XAxis      // only horizontal

            onClicked: {
                if ( _root.editable ) { didActiveChange(true); focus = true }

                _slider.opacity = 0
            }

            onReleased: {
                _root.grabbed = false
                _slider.opacity = 0
            }

            onPressAndHold: {
                if ( _root.editable ) { didActiveChange(true); focus = true }

                _root.grabbed = true
            }

            onPositionChanged: {
                if (_root.grabbed) {
                    if ( _slider.opacity === 0 ) { _animator.start() }

                    _slider.pos = Math.max(0, Math.min(1, mouse.x / parent.width))
                    let raw =  _slider.from + _slider.pos * (_slider.to - _slider.from)
                    let stepped = Math.round((raw - _slider.from) / _root.step) * _root.step + _slider.from
                    didChange(stepped)
                }
            }

            onExited: {
                if ( ! _root.grabbed ) {
                    _animator.stop()
                    _slider.opacity = 0
                }
            }
        }

        OpacityAnimator { id: _animator
            target  : _slider
            from    : 0
            to      : 1
            duration: 350
            running : _root.grabbed
        }

        Behavior on opacity { NumberAnimation { duration: 200 } }
    }

    IconButton { id: _leftArrow
        anchors {
            verticalCenter  : _root.verticalCenter
            left            : _root.left
            leftMargin      : Variables.defaultMargin
        }
        iconSize                : Variables.circleButtonDefaultDiameter
        enabled                 : _private.canDecrement
        visible                 : _root.editable
        iconImageSource         : enabled ? "qrc:/images/iArrowLeft" :
                                            "qrc:/images/iArrowLeftDisabled"
        onClicked               : _private.decrement()
    }

    IconButton { id: _rightArrow
        anchors {
            verticalCenter  : _root.verticalCenter
            right           : _root.right
            rightMargin     : Variables.defaultMargin
        }
        iconSize                : Variables.circleButtonDefaultDiameter
        enabled                 : _private.canIncrement
        visible                 : _root.editable
        iconImageSource         : enabled ? "qrc:/images/iArrowRight" :
                                            "qrc:/images/iArrowRightDisabled"
        onClicked               : _private.increment()
    }
}
