//
//  PlotPerf.qml
//  examples/utilities/render/plotperf
//
//  Created by Sam Gateau on 3//2016
//  Copyright 2016 High Fidelity, Inc.
//
//  Distributed under the Apache License, Version 2.0.
//  See the accompanying file LICENSE or https://www.apache.org/licenses/LICENSE-2.0.html
//
import QtQuick 2.5
import QtQuick.Controls 1.4

Item {
    id: root
    width: parent.width
    height: 100

    // The title of the graph
    property string title

    // The object used as the default source object for the prop plots
    property var object

    property var backgroundOpacity: 0.6

    // Plots is an array of plot descriptor
    // a default plot descriptor expects the following object:
    //     prop: [ {
    //                object: {}             // Optional: this is the object from which the prop will be fetched, 
    //                                          if nothing than the object from root is used
    //                prop:"bufferCPUCount", // Needed the name of the property from the object to feed the plot
    //                label: "CPU",          // Optional: Label as displayed on the plot
    //                color: "#00B4EF"       // Optional: Color of the curve
    //                unit: "km/h"           // Optional: Unit added to the value displayed, if nothing then the default unit is used
    //                scale: 1               // Optional: Extra scaling used to represent the value, this scale is combined with the global scale.
    //            },
    property var plots

    // Default value scale used to define the max value of the chart
    property var valueScale: 1

    // Default value unit appended to the value displayed
    property var valueUnit: ""

    // Default number of digits displayed
    property var valueNumDigits: 0
    

    property var valueMax : 1
    property var valueMin : 0

    property var displayMinAt0 : true
    property var _displayMaxValue : 1
    property var _displayMinValue : 0

    property var _values
    property var tick : 0

    function createValues() {
        if (!_values) {
            _values = new Array();
        }
        for (var i =0; i < plots.length; i++) {

            var plot = plots[i];
            var object = plot["object"] || root.object;
            var value = plot["prop"];
            var isBinding = plot["binding"];
            if (isBinding) {
                object = root.parent;
                value = isBinding;
            }
            _values.push( {
                object: object,
                value: value,
                fromBinding: isBinding,
                valueMax: 1,
                valueMin: 0,
                numSamplesConstantMax: 0,
                numSamplesConstantMin: 0,
                valueHistory: new Array(),
                label: (plot["label"] !== undefined ? plot["label"] : ""),
                color: (plot["color"] !== undefined ? plot["color"] : "white"),
                scale: (plot["scale"] !== undefined ? plot["scale"] : 1),
                unit: (plot["unit"] !== undefined ? plot["unit"] : valueUnit)
            })
        }
        pullFreshValues();
    }

    Component.onCompleted: {
        createValues();   
    }
    function resetMax() {
        for (var i = 0; i < _values.length; i++) {
            _values[i].valueMax *= 0.25 // Fast reduce the max value  as we click                   
        }
    }
    function resetMin() {
        for (var i = 0; i < _values.length; i++) {
            _values[i].valueMin *= 0.25 // Fast reduce the min value  as we click                   
        }
    }

    function pullFreshValues() {
        // Wait until values are created to begin pulling
        if (!_values) { return; }

        var VALUE_HISTORY_SIZE = 100;
        tick++;
                
        var currentValueMax = 0
        var currentValueMin = 0
        for (var i = 0; i < _values.length; i++) {

            var currentVal = (+_values[i].object[_values[i].value]) * _values[i].scale;

            _values[i].valueHistory.push(currentVal)
            _values[i].numSamplesConstantMax++;

            if (_values[i].valueHistory.length > VALUE_HISTORY_SIZE) {
                var lostValue = _values[i].valueHistory.shift();
                if (lostValue >= _values[i].valueMax) {
                    _values[i].valueMax *= 0.99
                    _values[i].numSamplesConstantMax = 0
                }
                if (lostValue <= _values[i].valueMin) {
                    _values[i].valueMin *= 0.99
                    _values[i].numSamplesConstantMin = 0
                }
            }

            if (_values[i].valueMax < currentVal) {
                _values[i].valueMax = currentVal;
                _values[i].numSamplesConstantMax = 0 
            }                    
            if (_values[i].valueMin > currentVal) {
                _values[i].valueMin = currentVal;
                _values[i].numSamplesConstantMin = 0 
            }   

            if (_values[i].numSamplesConstantMax > VALUE_HISTORY_SIZE) {
                _values[i].numSamplesConstantMax = 0     
                _values[i].valueMax *= 0.95 // lower slowly the current max if no new above max since a while                      
            }
            if (_values[i].numSamplesConstantMin > VALUE_HISTORY_SIZE) {
                _values[i].numSamplesConstantMin = 0     
                _values[i].valueMin *= 0.95 // lower slowly the current min if no new above min since a while                      
            }

            if (currentValueMax < _values[i].valueMax) {
                currentValueMax = _values[i].valueMax
            }
            if (currentValueMin > _values[i].valueMin) {
                currentValueMin = _values[i].valueMin
            }
        }

        if ((valueMax < currentValueMax) || (tick % VALUE_HISTORY_SIZE == 0)) {
            valueMax = currentValueMax;
        }
        if ((valueMin > currentValueMin) || (tick % VALUE_HISTORY_SIZE == 0)) {
            valueMin = currentValueMin;
        }
        _displayMaxValue = valueMax;
        _displayMinValue = ( displayMinAt0 ? 0 : valueMin )

        mycanvas.requestPaint()
    }

    Timer {
        interval: 100; running: true; repeat: true
        onTriggered: pullFreshValues()
    }

    Canvas {
        id: mycanvas
        anchors.fill:parent
        
        onPaint: {
            var lineHeight = 12;

            function displayValue(val, unit) {
                 return (val / root.valueScale).toFixed(root.valueNumDigits) + " " + unit
            }

            function pixelFromVal(val, valScale) {
                return lineHeight + (height - lineHeight) * (1 - (0.99) * (val - _displayMinValue) / (_displayMaxValue - _displayMinValue));
            }
            function valueFromPixel(pixY) {
                return _displayMinValue + (((pixY - lineHeight) / (height - lineHeight) - 1) * (_displayMaxValue - _displayMinValue) / (-0.99));
            }
            function plotValueHistory(ctx, valHistory, color) {
                var widthStep= width / (valHistory.length - 1);

                ctx.beginPath();
                ctx.strokeStyle= color; // Green path
                ctx.lineWidth="2";
                ctx.moveTo(0, pixelFromVal(valHistory[0])); 
                   
                for (var i = 1; i < valHistory.length; i++) { 
                    ctx.lineTo(i * widthStep, pixelFromVal(valHistory[i])); 
                }

                ctx.stroke();
            }
            function displayValueLegend(ctx, val, num) {
                ctx.fillStyle = val.color;
                var bestValue = val.valueHistory[val.valueHistory.length -1];             
                ctx.textAlign = "right";
                ctx.fillText(displayValue(bestValue, val.unit), width, (num + 2) * lineHeight * 1);
                ctx.textAlign = "left";
                ctx.fillText(val.label, 0, (num + 2) * lineHeight * 1);
            }

            function displayTitle(ctx, text, maxVal) {
                ctx.fillStyle = "grey";
                ctx.textAlign = "right";
                ctx.fillText("max " + displayValue(_displayMaxValue, root.valueUnit), width, pixelFromVal(_displayMaxValue));
                
                ctx.fillText("min " + displayValue(_displayMinValue, root.valueUnit), width, pixelFromVal(_displayMinValue));
               
                ctx.fillStyle = "white";
                ctx.textAlign = "left";
                ctx.fillText(text, 0, lineHeight);
            }
            function displayBackground(ctx) {
                ctx.fillStyle = Qt.rgba(0, 0, 0, root.backgroundOpacity);
                ctx.fillRect(0, 0, width, height);
                
              /*  ctx.strokeStyle= "grey";
                ctx.lineWidth="2";

                ctx.beginPath();
                ctx.moveTo(0, lineHeight + 1); 
                ctx.lineTo(width, lineHeight + 1);
                ctx.moveTo(0, height); 
                ctx.lineTo(width, height); 
                ctx.stroke();*/
            }

            function displayMaxZeroMin(ctx) {
                var maxY = pixelFromVal(_displayMaxValue);
                
                ctx.strokeStyle= "LightSlateGray";
                ctx.lineWidth="1";
                ctx.beginPath();
                ctx.moveTo(0, maxY); 
                ctx.lineTo(width, maxY);
                ctx.stroke();

                if (_displayMinValue != 0) { 
                    var zeroY = pixelFromVal(0);
                    var minY = pixelFromVal(_displayMinValue);
                    ctx.beginPath();
                    ctx.moveTo(0, zeroY); 
                    ctx.lineTo(width, zeroY);
                    ctx.moveTo(0, minY); 
                    ctx.lineTo(width, minY);
                    ctx.stroke();
                } 
            }
            
            var ctx = getContext("2d");
            ctx.clearRect(0, 0, width, height);
            ctx.font="12px Verdana";

            displayBackground(ctx);
                
            for (var i = 0; i < _values.length; i++) {
                plotValueHistory(ctx, _values[i].valueHistory, _values[i].color)
                displayValueLegend(ctx, _values[i], i)
            }

            displayMaxZeroMin(ctx);

            displayTitle(ctx, title, _displayMaxValue)
        }
    }

    MouseArea {
        id: hitbox
        anchors.fill: mycanvas

        onClicked: {
            resetMax();
            resetMin();
        }
    }
}