/*!
 *
 * Copyright (c) 2021-2026 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    RangedValue.qml
 * \author  (last)      Stephen Quong
 * \date    (last)      06-Jan-2026
 * \author  (original)  Stephen Quong
 * \date    (original)  06-Jan-2026
 *
 */

import QtQuick 2.12

Item { id: _root
    objectName  : "_rangedValue"

    // fix floating-point precision issue
    readonly property real step             : _private.calculatePrecisionValue(rawStep)
    readonly property real min              : rawMin    !== undefined ? _private.calculatePrecisionValue(rawMin) : 0
    readonly property real max              : rawMax    !== undefined ? _private.calculatePrecisionValue(rawMax) : 0
    readonly property real value            : rawValue  !== undefined ? _private.calculatePrecisionValue(rawValue) : 0
    readonly property bool isValueInRange   : (canOff && value == 0) || (value >= min && value <= max)

    readonly property bool canIncrement     : value < _private.calculateMaximum()
    readonly property bool canDecrement     : canOff ? value > 0 : value > _private.calculateMinimum()

    property int  precision : 0
    property real rawValue  : 0
    property real rawMin    : rawValue
    property real rawMax    : rawValue
    property real rawStep   : 1
    property bool canOff    : false

    function incrementedValue() {
        let tValue = value
        // if value can be 'off' and value is below the min, then just set value to min
        if (canOff && value < min) {
            tValue = min
        }
        else {
            let fixedVal    = _private.fixedValue(value)
            let fixedStep   = _private.fixedValue(step)
            if (fixedStep != 0) {
                // if value is not step aligned, then fixedDelta will be amount to next higher step
                // otherwise fixedDelta will be a whole step
                let fixedDelta  = fixedStep - (fixedVal % fixedStep)
                tValue = (fixedVal + fixedDelta) / _private.multiplier
            }
            // clamp tValue between [min, max]
            // e.g. if value is 2 and range is [5, 10], incrementing will return 5
            tValue = Math.max(min, Math.min(max, tValue))
        }
        return tValue
    }

    function decrementedValue() {
        let tValue = value
        // if value can be 'off' and value is already at minimum, just set value to 0
        if (canOff && value === min) {
            tValue = 0
        }
        else {
            let fixedVal    = _private.fixedValue(value)
            let fixedStep   = _private.fixedValue(step)
            if (fixedStep != 0) {
                // if value is not step aligned, then fixedDelta will be amount to next lower step
                // otherwise fixedDelta will be a whole step
                let fixedDelta  = fixedVal % fixedStep
                tValue = (fixedVal - (fixedDelta > 0 ? fixedDelta : fixedStep)) / _private.multiplier
            }
            // clamp tValue between [minimum, maxVal]
            // e.g. if value is 13 and range is [5, 10], decrementing will return 10
            tValue = Math.min(max, Math.max(min, tValue))
        }
        return tValue
    }

    QtObject { id: _private
        objectName: "_private"
        readonly property int  multiplier   : Math.pow(10, precision)

        // 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 lower step size
        function calculateMinimum() {
            let fixedMin    = _private.fixedValue(min)
            let fixedStep   = _private.fixedValue(step)
            return (fixedStep != 0 ? (Math.ceil(fixedMin / fixedStep) * fixedStep) : fixedMin) / _private.multiplier
        }

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

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