/*!
 *
 * Copyright (c) 2025-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    BaseChart.qml
 * \author  (last)      Nico Ramirez
 * \date    (last)      2-Feb-2026
 * \author  (original)  Nico Ramirez
 * \date    (original)  2-Feb-2026
 *
 */

import QtQuick 2.15
import QtCharts 2.15
import QtQuick.Shapes 1.15 // Import the Shapes module

import "qrc:/globals"

Rectangle { id: _root
    color           : _root.chartColor
    radius          : 15

    readonly property color chartColor      : "#2A425A"
    readonly property color gridColor       : "#354F69"
    readonly property color labelColor      : "#6F7D8A"

    property int   lineSeries1Value         : 0
    property int   lineSeries2Value         : 0
    property int   lineSeries3Value         : 0
    property alias lineSeries1Color         : _line1.color
    property alias lineSeries2Color         : _line2.color
    property alias lineSeries3Color         : _line3.color

    readonly property int   minY            : -700
    readonly property int   maxY            : 600

    readonly property int   plotFrequency   : 1000 * 60 // plot every 1 min

     // plot current reading at current time
    function plotGraph() {
        let now = new Date()

        if ( isOutsideWindow( now ) ) { shiftWindow(15) }     // shift 15 min once out of scope

        _line1.append( now, lineSeries1Value )
        _line2.append( now, lineSeries2Value )
        _line3.append( now, lineSeries3Value )

        var pos = _chartView.mapToPosition( _line1.at( _line1.count - 1), _line1 )
        _mouseArea.updateLinePosition( pos.x, pos.y )  // move vertical line to newest plot
    }

    // set window
    function updateXAxis() {
        let now = new Date()
        let min = snapToPreviousFiveMinutes(now)
        let max = snapToPreviousFiveMinutes(new Date(now.getTime() + 60 * 60 * 1000)) // 1 hour window
        _xAxis.min = min
        _xAxis.max = max

        plotGraph() // plot first point
        _timer.start()
    }

   // Snap any date to the previous 5-minute mark
   function snapToPreviousFiveMinutes( vDate ) {
       let newDate = new Date(vDate)
       let minutes = newDate.getMinutes()

       let snappedMinutes = Math.floor(minutes / 5) * 5
       newDate.setMinutes(snappedMinutes)
       newDate.setSeconds(0)
       newDate.setMilliseconds(0)

       return newDate
   }

   // Check if next plot is outside of current window
    function isOutsideWindow( vTime ) {
        return vTime > _xAxis.max
    }

    // when out of scope shift window by given min
    function shiftWindow( vMin ) {
        const hourMs = 60 * vMin * 1000
        _xAxis.min = new Date(_xAxis.min.getTime() + hourMs)
        _xAxis.max = new Date(_xAxis.max.getTime() + hourMs)

        removeOldPoints()
    }

    // remove points old left of the window that are not shown
    function removeOldPoints() {
        var minX = _xAxis.min
        var i = 0

        // Find how many points to remove that are less thatn visible x axis min
        while (i < _line1.count && _line1.at(i).x < minX)
            i++

        if (i > 0)
            _line1.removePoints(0, i)  // remove in batch
            _line2.removePoints(0, i)  // remove in batch
            _line3.removePoints(0, i)  // remove in batch
    }

    Timer { id: _timer
        interval    : _root.plotFrequency
        running     : false
        repeat      : true
        onTriggered : plotGraph()
    }

    DateTimeAxis { id: _xAxis
        format              : "H:mm"
        tickCount           : 13
        gridVisible         : false
        color               : _root.gridColor
        labelsColor         : _root.labelColor
        labelsFont.pixelSize: 14
    }

    ValueAxis { id: _yAxis
        min                 : _root.minY
        max                 : _root.maxY
        tickCount           : 14
        labelFormat         : "%i"
        color               : _root.gridColor
        gridLineColor       : _root.gridColor
        labelsColor         : _root.labelColor
        labelsFont.pixelSize: 14
    }

    ChartView { id: _chartView
        anchors.fill    : parent
        antialiasing    : true
        legend.visible  : false
        backgroundColor :  _root.chartColor
        plotAreaColor   :  _root.chartColor

        LineSeries { id: _line1
            axisX       : _xAxis
            axisY       : _yAxis
            width       : 3
        }

        LineSeries { id: _line2
            axisX       : _xAxis
            axisY       : _yAxis
            width       : 3
        }

        LineSeries { id: _line3
            axisX       : _xAxis
            axisY       : _yAxis
            width       : 3
        }

        // --- The Vertical Line ---
        Shape { id: _verticalLine
            width   : 1
            height  : _chartView.plotArea.height
            y       : _chartView.plotArea.y

            ShapePath {
                strokeColor : "#CA8D36"
                strokeWidth : 1
                strokeStyle : ShapePath.DashLine
                dashPattern : [2, 4]
                capStyle    : ShapePath.RoundCap

                startX: 1; startY: 0
                PathLine { x: 1; y: _verticalLine.height }
            }
        }

        Row { id: _currentDataRow
            anchors.top: parent.top
            anchors.topMargin: 5
            anchors.horizontalCenter: parent.horizontalCenter
            spacing: Variables.defaultMargin

            Text { id: _timeText
                color: Colors.offWhite
                font.pixelSize: 22
                font.weight: Font.Medium
            }

            Text { id: _line1Text
                color: _root.lineSeries1Color
                font.pixelSize: 22
                font.weight: Font.Medium
            }

            Text { id: _line2Text
                color: _root.lineSeries2Color
                font.pixelSize: 22
                font.weight: Font.Medium
            }

            Text { id: _line3Text
                color: _root.lineSeries3Color
                font.pixelSize: 22
                font.weight: Font.Medium
            }
        }

        // --- Touch/Mouse Handling Area ---
        MouseArea { id: _mouseArea
            anchors.fill: parent

            onPressed: {
                updateLinePosition(mouse.x, mouse.y)
            }
            onPositionChanged: {
                if (pressed) { updateLinePosition(mouse.x, mouse.y) }
            }

            function updateLinePosition(mx, my) {
                if (mx < _chartView.plotArea.x || mx > _chartView.plotArea.x + _chartView.plotArea.width)
                    return

                _verticalLine.x = mx

                // Convert to data coordinates
                let line1Value = _chartView.mapToValue(Qt.point(mx, my), _line1)
                let line2Value = _chartView.mapToValue(Qt.point(mx, my), _line2)
                let line3Value = _chartView.mapToValue(Qt.point(mx, my), _line3)

                let newDate = new Date( line1Value.x )
                _timeText.text = qsTr("Time: %1").arg(Qt.formatTime(newDate, "H:mm"))

                let y1 = getYatX( _line1, line1Value.x )
                let y2 = getYatX( _line2, line2Value.x )
                let y3 = getYatX( _line3, line3Value.x )

                _line1Text.text = y1 ? y1.toFixed(0) : Variables.emptyEntry
                _line2Text.text = y2 ? y2.toFixed(0) : Variables.emptyEntry
                _line3Text.text = y3 ? y3.toFixed(0) : Variables.emptyEntry
            }

            function getYatX(series, x) {
                let count = series.count

                // need at least 2 points to interpolate between them
                if (count < 2)
                    return null

                // Clamp outside range
                let first = series.at(0)
                let last = series.at(count - 1)

                // set null if y not plotted yet in graph to display as --
                if ( x < first.x || x > last.x )
                    return null

                // Binary Search for a fast look ip
                let left = 0
                let right = count - 1

                while (left <= right) {
                    let mid = Math.floor((left + right) / 2)
                    let midPoint = series.at(mid)

                    // found y
                    if (midPoint.x === x)
                        return midPoint.y

                    if (midPoint.x < x)
                        left = mid + 1      // x is larger so discard left half
                    else
                        right = mid - 1     // x is smaller so discard right half
                }

                // if not found then interpolate betweeen them
                // series.at(right) and series.at(left)
                let p1 = series.at(right)
                let p2 = series.at(left)

                // Compute how far between them X is
                let ratio = (x - p1.x) / (p2.x - p1.x)
                return p1.y + ratio * (p2.y - p1.y) //compute y
            }
        }
    }
}
