/**
 * Class definitions for Barometer page MVC.
 * 
 * @category Webserver
 * @package ATProcessor
 * @author Cameron Guest <cameron@aandtinstruments.com>
 * @version 0.2
 * 
 */

const eTimeUnits = {
    minutes: 1,
    hours: 60,
    days: 1440
}

class BaroModel extends ObservableModel 
{

    get PressureOffset() { return this.pressureOffset; }
    set PressureOffset(val) { this.pressureOffset = val; }

    get BarometerTimespan() { return this.timespan; }
    set BarometerTimespan(val) { this.timespan = val; }

    /**
     * Model Constructor.
     * @param {*} source -> field id to lookup chart data.
     * @param {*} chartRefresh_ms -> how oftern to update chart data.
     */
    constructor(source, chartRefresh_ms = 60000)
    {
        super();

        this.chartRefresh_ms = chartRefresh_ms;
        this.source = source;
        this.timespan = "1d";
        this.pressureOffset = 0;

        this.labels = [];
        this.chartData = [];

        this.init();
    }

    init()
    {
        let self = this;

        readSection("Environment").done(function(environmentJSON)
        {
            let environment = JSON.parse(environmentJSON);
            self.pressureOffset = environment.barooffset || "0.0";
            self.timespan = environment.barotimespan || "3h";

            self.readChartData().done(function()
            {   
                /* Setup the chart update timer */
                setInterval(self.updateChartData.bind(self), self.chartRefresh_ms);
                self.notifyAll();
            })
        });
    }

    /**
     * Generate timestamps along the x-axis of the chart.
     * @param {*} totalMinutes -> Total number of data points to show 
     * @param {*} minutesPerLabel  -> Space between labels expressed in terms of data-points.
     * @param {*} units -> What unit of time to use, see eTimeUnits.
     */
    getChartTimestamps(totalMinutes, minutesPerLabel, units)
    {

        let labels = [''];
        let suffix;

        switch (units)
        {
        case eTimeUnits.minutes:
            suffix = ' minutes';
            break;
        case eTimeUnits.hours:
            suffix = ' hours';
            break;
        case eTimeUnits.days:
            suffix = 'days';
            break;
        default:
            return labels;
        }

        for (let i = 1; i < totalMinutes; ++i)
        {
            if (i % minutesPerLabel == 0)
            {
                labels.unshift('-' + (i / units) + suffix);
            }
            else
            {
                labels.unshift('');
            }
        }
        return labels;
    }

    /* Read the graph data from the database */
    readChartData()
    {
        let self = this;

        return readSingle("ChartData", this.source).done(function(response) 
        {

            let timespan_m = 0; // timespan in minutes.
            let labels = [];
            let data = [];

            /* Generate the chart data labels. */
            switch (self.timespan)
            {
            case "1d":  // 1 Day
                timespan_m = 1440;
                labels = self.getChartTimestamps(timespan_m, 180, eTimeUnits.hours);
                break;

            case "3d":  // 3 Days
                timespan_m = 4320;
                labels = self.getChartTimestamps(timespan_m, 720, eTimeUnits.hours);
                break;

            case "7d": // 7 Days
                timespan_m = 10080;
                labels = self.getChartTimestamps(timespan_m, 1440, eTimeUnits.days);
                break;
            
            case "14d": // 14 Days
                timespan_m = 20160;
                labels = self.getChartTimestamps(timespan_m, 1440, eTimeUnits.days);
                break;

            case "3h":   // 3 Hours
            default:
                timespan_m = 180;
                labels = self.getChartTimestamps(timespan_m, 60, eTimeUnits.hours);
                break;
            }

            /* Parse the chart data string */
            data = response.split(',');

            if (data.length < timespan_m)
            {
                let extra = Array(timespan_m - data.length).fill(NaN);
                data = extra.concat(data);
            }
            else
            {
                data = response.split(',').slice(-timespan_m);
            }

            self.labels = labels;
            self.chartData = data;
        });
    }

    /* Called on a timer to update the graph data */
    updateChartData()
    {
        let self = this;

        this.readChartData().done(function()
        {
            self.notifyAll();
        });
    }

    /* Save the offset and graph timespan */
    persistData()
    {
        let dbArray = [
            {'section': 'Environment', 'key': 'BaroOffset', 'value': this.pressureOffset},
            {'section': 'Environment', 'key': 'BaroTimespan', 'value': this.timespan}
        ];

        return writeToDatabase(dbArray, 'Environment');
    }
}

class BaroController extends BasicController 
{

    constructor(model)
    {
        super(model);
    }

    /* Change the barometric pressure offset */
    offsetChanged(e)
    {
        validateRange( $('#pressure-offset'), 2, -50, 50, 0 );
        this.model.PressureOffset = $(e.target).val();
    }

    /* Change the graphs time period */
    periodChanged(e)
    {
        let self = this;

        this.model.BarometerTimespan = $(e.target).val();
        this.model.readChartData().done(function()
        {
            self.model.notifyAll();
        });
    }
}

class BaroView extends BasicView 
{

    constructor(controller, chart)
    {
        super(controller);

        this.chart = chart;
    }

    bindEvents()
    {
        super.bindEvents();

        $('#baro-timespan').on('change', this.controller.periodChanged.bind(this.controller));
        $('#pressure-offset').on('change', this.controller.offsetChanged.bind(this.controller));
    }

    update(model)
    {
        /* Only update if changed otherwise you run into issues where the field will reset
         * during edit as the model calls this after updating the graph data on a timer. */
        if ($('#pressure-offset').val() !== model.PressureOffset)
            $('#pressure-offset').val(model.PressureOffset);
        if ($('#baro-timespan').val() !== model.BarometerTimespan)
            $('#baro-timespan').val(model.BarometerTimespan);

        /* Update the chart data. */
        this.chart.data.labels = model.labels;
        this.chart.data.datasets[0].data = model.chartData;
        this.chart.update();
    }
}