/*globals console, angular, d3*/

angular.module('IOLabApp').directive('iolabChart', function ($timeout, IOLab, chartService, helperService, configService, BSON) {
    'use strict';
    return {
        restrict: 'E',
        replace: true,
        controller: 'chartCtrl',
        templateUrl: 'views/tpl-chart.html',
        link: function (scope, element) {

            // D3 class object
            var chart = chartService(scope.sensor);

            // html chart element
            var chartElement = d3.select(element[0]);

            // Draw the chart element
            chartElement.call(chart);  // idem 'chart(chartElement);'

            var remote = scope.sensor.remote,
                sensorType = scope.sensor.sensorType;

            var timeoutId,
                newData = [],
                overlap = 0,
                startTime = 0,
                previousLastPoint = 0,
                samplingRate = 1,
                computeSamplingRate = true,
                pathNbr = 1;

            // Over lap only for the wheel...
            if (scope.sensor.sensorType === 9) {
                overlap = 8; /*pt*/
            }

            // Used to compute loading time
            var t1, t2, t3;

            function updateChart() {
                if (scope.control.running) {
                    timeoutId = $timeout(function () {
                        IOLab.getSensorData(
                            addDataToChart,
                            { remote: remote, sensor: sensorType, startTime: startTime }
                        );
                    }, 25);
                } else {
                    console.log('*** Force cancel timeout !!! ***');
                    $timeout.cancel(timeoutId);
                }
            }

            function addDataToChart(bson) {
                newData = BSON.deserialize(bson);

                // if new data empty return
                if (!newData.datalength) {
                    updateChart();
                    return;
                }

                // Check the resampling the first time
                if (computeSamplingRate) {
                    computeSamplingRate = false;
                    samplingRate = chart.resampling(newData.sampleRate);
                }

                // First and last data point (time)
                var dataLength = newData.data[0].length;
                var firstPoint = newData.data[0][0][0];
                var lastPoint = newData.data[0][dataLength - 1][0];

                // Shift the time axis if we are at the end
                if (lastPoint >= chart.scale().x1) {
                    samplingRate = chart.resampling(newData.sampleRate);
                    startTime = chart.scale().x1;
                    chart.removeData();
                    chart.addData(newData.data, 'path' + (++pathNbr));
                    chart.shiftTimeScale(lastPoint);
                    chart(chartElement);
                    updateChart();
                    return;
                }

                // Redraw the preceding path if the new one overlaps
                if (firstPoint < previousLastPoint) {
                    chart.truncateData('path' + pathNbr, overlap);
                }

                // Draw a new path
                chart.addData(newData.data, 'path' + (++pathNbr), overlap);

                // Resampling correction
                var samplingOverlap = Math.max(dataLength - 1 - overlap, 0) % samplingRate + 1;
                // Keep the start time for the next data query
                startTime =  newData.data[0][Math.max(dataLength - 1 - overlap - samplingOverlap, 0)][0];
                // Keep the last drawn point for the next step
                previousLastPoint = newData.data[0][Math.max(dataLength - 1 - samplingOverlap, 0)][0];

                updateChart();
            }

            function loadDataToChart(bson) {
                t2 = new Date();
                newData = BSON.deserialize(bson);
                t3 = new Date();
                console.log('*** stopDataCallBack - getSensorData:', t2 - t1, 'ms - BSON.deserialize:', t3 - t2, 'ms ***');
                chart.removeData();
                chart.addData(newData.data, 'path0');
                chart.resampling(newData.sampleRate);
                chart.smoothingPath(scope.control.timeAverage);
                chart(chartElement);
                scope.$apply(scope.loading = false);
            }

            // Load the data if any when the chart is created !
            if (!IOLab.isNewAcquisition()) {
                if (scope.control.running) {
                    computeSamplingRate = true;
                    chart.smoothingPath(1);
                    chart.initScale(startTime);
                    chart(chartElement);
                    updateChart();
                } else {
                    t1 = new Date();
                    scope.loading = true;
                    IOLab.getSensorData(loadDataToChart,{ remote: remote, sensor: sensorType });
                }
            }

            scope.$on('dataStarted', function () {
                console.log('*** startData ***');
                computeSamplingRate = true;
                chart.smoothingPath(1);
                chart.initScale(startTime);
                chart(chartElement);
                updateChart();
            });

            scope.$on('dataStopped', function () {
                console.log('*** stopData ***');
                t1 = new Date();
                // Cancel the timeout
                $timeout.cancel(timeoutId);
                // Get all the data after calibration
                scope.loading = true;
                IOLab.getSensorData(loadDataToChart, { remote: remote, sensor: sensorType });
            });

            scope.$on('$destroy', function (){
                console.log('*** scope.$on => destroy Chart ***');
                chart.clean();
                chart.on('cursorPosition', null);
                chart.on('cursorRange', null);
                chart.on('fftResult', null);
                chart.on('fftInit', null);
                chart.on('scaleChanges', null);
                chart.on('setMode', null);
            });

            element.bind('$destroy', function () {
                $timeout.cancel(timeoutId);
            });


            /*
             *  Scale changed from the chart
             */
            var emittingChart = false;
            chart.on('scaleChanges', function (scale) {
                emittingChart = true;
                scope.$apply(scope.sharedChartObject.scales = scale);
            });
            scope.$watch(
                function () { return scope.sharedChartObject.scales; },
                function (newScales) {
                    if (!newScales) {
                        return;
                    }
                    if (emittingChart) {
                        emittingChart = !emittingChart;
                        return;
                    }
                    chart.scale(newScales);
                    chart();
                },
                true
            );

            /*
             *  Cursor position (update of the legend)
             */
            chart.on('cursorPosition', function (d) {
                scope.$apply(scope.sharedChartObject.cursorPosition = d);
            });
            scope.$watch(
                function () { return scope.sharedChartObject.cursorPosition; },
                function (newCursorPosition) {
                    if (newCursorPosition) {
                        chart.redrawLegend(newCursorPosition);
                    }
                }
            );

            /*
             *  Cursor range (update statistics)
             */
            chart.on('cursorRange', function (d) {
                scope.$apply(scope.sharedChartObject.cursorRange = d);
            });
            scope.$watch(
                function () { return scope.sharedChartObject.cursorRange; },
                function (newCursorRange) {
                    if (newCursorRange) {
                        chart.redrawStatistics(newCursorRange);
                    }
                },
                true
            );

            /*
             * BroadCast Event to the fft directive
             */
            chart.on('fftResult', function (data) {
                scope.$broadcast('fftData', data);
            });

            /*
             *  Mode changed from the chart
             */
            chart.on('setMode', function (newMode) {
                scope.chartModes.forEach(function (mode) {
                    if (mode.val === newMode) {
                        scope.control.chartMode = mode;
                    }
                });
            });

            /*
             *  Chart Controls
             */
            scope.$watch('control.chartMode', function (newVal) {
                chart.mode(newVal.val);
            });
            scope.$watch('control.timeAverage', function () {
                if (scope.control.running) { return; }
                chart.smoothingPath(scope.control.timeAverage);
            });
            scope.$watch('control.fftSampleRate', function () {
                chart.fftSampleRate(scope.control.fftSampleRate);
            });
            scope.$watch('control.legendSettings', function () {
                chartElement.call(chart.showPaths(scope.control.legendSettings));
            }, true);
        }
    };
});

angular.module('IOLabApp').directive('iolabFft', function (fftService) {
    'use strict';
    return {
        restrict: 'E',
        replace: true,
        templateUrl: 'views/tpl-chart-fft.html',
        link: function (scope, element) {

            var fft = fftService(scope.sensor);

            // link the service to the dom element
            fft(d3.select(element[0]));

            scope.$on('fftData', function (event, fftData) {
                fft.redraw(fftData);
            });

            scope.$watch('control.fftSampleRate', function () {
                fft.samplingRate(scope.control.fftSampleRate);
            });

            scope.$watch('control.legendSettings', function () {
                fft.showPaths(scope.control.legendSettings);
            }, true);
        }
    };
});

angular.module('IOLabApp').directive('iolabParamPlot', function ($timeout, IOLab, paramPlotService, helperService, configService, BSON) {
    'use strict';
    return {
        restrict: 'E',
        replace: true,
        controller: 'paramPlotCtrl',
        templateUrl: 'views/tpl-param-plot.html',
        link: function (scope, element) {

            var remote = scope.plotParams.remote,
                sensor1 = scope.plotParams.sensor1.sensorType,
                sensor2 = scope.plotParams.sensor2.sensorType;

            // D3 class object
            var paramPlot = paramPlotService(scope.plotParams);

            // html chart element
            var chartElement = d3.select(element[0]);

            // Draw the chart element
            chartElement.call(paramPlot);  // idem 'chart(chartElement);'

            var timeoutId,
                startTime = 0;

            // Used to compute loading time
            var t1, t2, t3;

            function updateChart() {
                if (scope.running) {
                    timeoutId = $timeout(function () {
                        IOLab.getSensorData2(
                            addDataToChart,
                            {
                                remote: remote,
                                sensor1: sensor1,
                                sensor2: sensor2,
                                startTime: startTime
                            }
                        );
                    }, 25);
                } else {
                    console.log('*** Force cancel timeout !!! ***');
                    $timeout.cancel(timeoutId);
                }
            }

            function addDataToChart(bson) {
                var newData = BSON.deserialize(bson);

                // if data ...
                if (!!newData.data && !!newData.data.length) {

                    // Keep the start time for the next data query
                    var lastTimeIndex = Math.max(newData.data.length - 1, 0)
                    startTime =  newData.data[lastTimeIndex][0];

                    // Draw a new path
                    paramPlot.addData(newData.data);
                }

                updateChart();
            }

            function loadDataToChart(bson) {
                t2 = new Date();
                var newData = BSON.deserialize(bson);
                t3 = new Date();
                console.log('*** stopDataCallBack - getSensorData:', t2 - t1, 'BSON.deserialize:', t3 - t2, ' ***');
                paramPlot.removeData();
                paramPlot.addData(newData.data);
                paramPlot.showTimeScale(true);
                scope.$apply(scope.loading = false);
            }

            // Load the data if any when the chart is created !
            if (!IOLab.isNewAcquisition()) {
                if (scope.running) {
                    updateChart();
                } else {
                    t1 = new Date();
                    scope.loading = true;
                    IOLab.getSensorData2(loadDataToChart, { remote: remote, sensor1: sensor1, sensor2: sensor2 });
                }
            }

            scope.$on('dataStarted', function () {
                console.log('*** startData ***');
                scope.running = true;
                paramPlot.showTimeScale(false);
                updateChart();
            });

            scope.$on('dataStopped', function () {
                console.log('*** stopData ***');
                scope.running = false;
                paramPlot.showTimeScale(true);
                $timeout.cancel(timeoutId);
            });

            scope.$on('$destroy', function (){
                console.log('*** scope.$on => destroy iolabParamPlot ***');
                paramPlot.clean();
            });

            element.bind('$destroy', function () {
                $timeout.cancel(timeoutId);
            });

            /*
             *  Plots Controls
             */
            scope.$watch('control.chartMode', function (newVal) {
                paramPlot.mode(newVal.val);
            });
            scope.$watch('control.coordinate1', function (newVal) {
                var index = scope.coordinates1.indexOf(newVal);
                if (scope.coordinates1[0] == 'Norm') { index--; } // Index = -1 if Norm!!!
                paramPlot.coordinate(1, index);
                paramPlot();
            });
            scope.$watch('control.coordinate2', function (newVal) {
                var index = scope.coordinates2.indexOf(newVal);
                if (scope.coordinates2[0] == 'Norm') { index--; } // Index = -1 if Norm!!!
                paramPlot.coordinate(2, index);
                paramPlot();
            });
            scope.$watch('control.timeRange', function (newVal) {
                paramPlot.timeRange(newVal);
                paramPlot();
            });
        }
    };
});

angular.module('IOLabApp').directive('iolabGauge', function ($timeout, IOLab, BSON, gaugeService, configService) {
    'use strict';
    return {
        restrict: 'E',
        replace: true,
        controller: 'gaugeCtrl',
        templateUrl: 'views/tpl-gauge.html',
        link: function (scope, element, attrs) {

            var remote = scope.sensor.remote,
                sensor = scope.sensor.sensorType,
                sampleSize = scope.sensor.sampleSize;

            var gauges = [];
            var gaugeElement = d3.select(element[0]);

            var createGauge = function (name, label) {

                var config = {
                    size: 150,
                    label: label,
                    min: -20,
                    max: 20,
                    minorTicks: 5
                };

                gauges[name] = gaugeService(name, config);

                d3.select(element[0]).each(function () {
                    gauges[name].render(this);
                });
            };

            var gaugeName = function (nbr) {
                return 'gauge-' + remote + '-' + sensor + '-' + nbr;
            };

            // Create the html tag and gauge object
            for (var i = 0; i < sampleSize; i++) {
                gaugeElement.select('.gauge').append("span").attr('id', gaugeName(i));
                createGauge(
                    gaugeName(i),
                    configService.getSensor(sensor).shortDesc + '-' + ((sampleSize == 1) ? 'y' : ((i == 0) ? 'x' : ((i == 1) ? 'y' : 'z')))
                );
            }

            var timeoutId, timeInteval = 50;
            var newData = [], dataLength, startTime = 0;

            function addDataToChart(bson) {
                newData = BSON.deserialize(bson);

                if (!newData.datalength) {
                    updateChart();
                    return;
                }

                dataLength = newData.data[0].length;
                for (var i = 0; i < sampleSize; i++) {
                    gauges[gaugeName(i)].redraw(newData.data[i][dataLength - 1][1], timeInteval);
                }
                startTime = newData.data[0][dataLength - 1][0];

                updateChart();
            }

            function updateChart() {
                timeoutId = $timeout(function () {
                    IOLab.getSensorData(addDataToChart, { remote: remote, sensor: sensor, startTime: startTime });
                }, timeInteval, 25);
            }

            element.bind('$destroy', function () {
                $timeout.cancel(timeoutId);
            });


            scope.$on('dataStarted', function () {
                    console.log('*** startData ***');
                    updateChart();
            });
            scope.$on('dataStopped', function () {
                    console.log('*** stopData ***');
                    $timeout.cancel(timeoutId);

                    for (var key in gauges) {
                        gauges[key].redraw(0, timeInteval);
                    }
            });

        }
    }
});

angular.module('IOLabApp').directive('sensorSelection', function() {
    'use strict';
    return {
        restrict: 'E',
        replace: true,
        scope: { remote: '=' },
        controller: 'sensorSelectionCtrl',
        templateUrl: 'views/tpl-sensor-selection.html',
        link: function (scope, element, attrs) {
            //console.log('remote', scope.remote);
        }
    }
});

angular.module('IOLabApp').directive('chartDraggable', function($document) {
    return function(scope, element) {

        var startX = 0, startY = 0, x = 0, y = 0;

        element.parent().parent().css({
            position: 'relative',
            backgroundColor: 'white'
        });

        element.css({
            cursor: 'pointer'
        });

        element.on('mousedown', function(event) {
            // Prevent default dragging of selected content
            event.preventDefault();
            startX = event.pageX - x;
            startY = event.pageY - y;
            $document.on('mousemove', mousemove);
            $document.on('mouseup', mouseup);

            element.parent().parent().css({
                zIndex: 1 // => z-index style !
            });
        });

        function mousemove(event) {
            y = event.pageY - startY;
            x = event.pageX - startX;
            element.parent().parent().css({
                top: y + 'px',
                left:  x + 'px'
            });
        }

        function mouseup() {
            $document.unbind('mousemove', mousemove);
            $document.unbind('mouseup', mouseup);
        }
    }
});