/*globals console, angular, d3*/

angular.module('IOLabApp').controller('mainCtrl', function ($scope, IOLab, configService) {
    'use strict';
    // Give access to the IOLab service
    $scope.IOLab = IOLab;

    $scope.iolabState = IOLab.returnHardwareState();
    $scope.dongleMode = 'IDLE';
    $scope.$on('hardwareStateChanged', function () {
        $scope.iolabState = IOLab.returnHardwareState();
        $scope.dongleMode = configService.getDongleMode($scope.iolabState.dongleMode).desc;
    });

    $scope.$watch(
        function () { return IOLab.returnHardwareState(); },
        function (iolabState) {
            $scope.r1Off = iolabState.remote1.rfStatus === 0;
            $scope.r2Off = iolabState.remote2.rfStatus === 0;
            $scope.r1Configured = iolabState.remote1.configured;
            $scope.r2Configured = iolabState.remote2.configured;
            $scope.r1Disabled = $scope.r1Off || $scope.r1Configured;
            $scope.r2Disabled = $scope.r2Off || $scope.r2Configured;
        },
        true
    );
});

angular.module('IOLabApp').controller('acquiCtrl', function ($scope, sensorSelectionService, configService, chartComService, IOLab) {
    'use strict';

    // Default acquisition type ...
    $scope.type = 'chart';
    $scope.chartSensors = [];
    $scope.parametricPlots = [];

    // Service to communicate between charts (cursor position, selection, ...)
    $scope.sharedChartObject = chartComService();

    // List of sensors identification variables (remote, config and type)
    $scope.sensors = [[], []];

    $scope.startData = function () {
        IOLab.startData();
    };

    $scope.stopData = function () {
        IOLab.stopData();
    };

    $scope.resetDataAndConfig = function () {
        sensorSelectionService.reset();
        IOLab.deleteAcquisition();
    };

    function updateSensorList(remote) {
        var acquisition = IOLab.returnAcquisitionStack()['default']['remote' + remote] || [];
        var acquisitionSensors = acquisition.sensors;

        angular.forEach(acquisitionSensors, function (sensor) {
            // Index of the sensor in the scope list
            var idx = _.findIndex($scope.sensors[remote], { sensorType: sensor.sensorType }),
                checked = sensorSelectionService.checked[remote][sensor.sensorType];

            // Add sensor to the sensor list
            if (checked && idx == -1) {
                sensor.desc = configService.getSensor(sensor.sensorType).desc;
                sensor.shortDesc = configService.getSensor(sensor.sensorType).shortDesc;
                $scope.sensors[remote].push(sensor);
            }
            // Remove sensor from the sensor list
            if (!checked && idx != -1) {
                $scope.sensors[remote].splice(idx, 1);
            }
        });

        if ($scope.type == 'chart') {
            $scope.parametricPlots[remote] = [];
            $scope.chartSensors[remote] = $scope.sensors[remote];
        } else {
            $scope.chartSensors[remote] = [];
            if ($scope.sensors[remote].length == 1) {
                $scope.parametricPlots[remote] = [{
                    remote: remote,
                    config: acquisition.config,
                    sensor1: $scope.sensors[remote][0],
                    sensor2: $scope.sensors[remote][0]
                }];
            } else if ($scope.sensors[remote].length > 1) {
                $scope.parametricPlots[remote] = [{
                    remote: remote,
                    config: acquisition.config,
                    sensor1: $scope.sensors[remote][0],
                    sensor2: $scope.sensors[remote][1]
                }];
            } else {
                $scope.parametricPlots[remote] = [];
            }
        }
    }

    // Look for fixed configuration change
    $scope.$watch(
        function() { return IOLab.returnHardwareState().remote1.fixedConfig; },
        function () {
            updateSensorList(1);
        }
    );

    $scope.$watch('type', function(newType, oldType) {
        if (newType != oldType) {
            updateSensorList(1);
        }
    });

    // If no data and fixConfig doesn't change when checking sensors, no need to
    // change the fixed config, but we need to update the sensor1 list !
    $scope.$on('updateSensors1', function () {
        updateSensorList(1);
    });
});

angular.module('IOLabApp').controller('chartCtrl', function ($scope, IOLab, configService, helperService) {
    'use strict';

    var sensorType = $scope.sensor.sensorType,
        config = $scope.sensor.config;

    $scope.isCollapsed = false;
    $scope.iolabState = IOLab.returnHardwareState();

    // TODO: return the result for the API (Async call with callback to be implemented)...
    // Possibly an error message => type: 'danger'
    $scope.exportData = function (sensor) {
        var fileName = helperService.getTime() + "_" + sensor.shortDesc;
        IOLab.exportData(sensor.remote, sensor.sensorType, fileName);
        // Broadcast to alert controller
        $scope.$broadcast('alert', { message: fileName, type: 'success' });
    };

    $scope.resetSensorOffset = function (sensor) {
        IOLab.resetSensorOffset(sensor.remote, sensor.sensorType);
    };

    // Drop down chart mode
    $scope.chartModes = [
        { val: 'stats', glyphicon: 'glyphicon-stats' },
        { val: 'zoom', glyphicon: 'glyphicon-zoom-in' },
        { val: 'move', glyphicon: 'glyphicon-move' }
    ];

    // Drop down time smoothing
    $scope.timeAverages = [1, 3, 5, 7, 9, 11, 13, 15, 25];

    // Drop down FFT
    $scope.fftSampleRates = [null, 512, 1024, 2048, 4096];

    // Scope injected in the chart directive and available in the chart view
    $scope.control = {
        dsShow: false,  //used in drop down
        running: $scope.iolabState.dongleMode === 16,
        chartMode: $scope.chartModes[0],
        timeAverage: $scope.timeAverages[0],
        fftSampleRate: $scope.fftSampleRates[0],
        frequency: configService.getSampleRate(sensorType, config),
        legendSettings: configService.getLegendSettings(sensorType),
        exportDataDisabled: IOLab.isNewAcquisition()
    };


    $scope.$on('dataStarted', function () {
        $scope.control.running = true;
    });
    $scope.$on('dataStopped', function () {
        $scope.loading = true;
        $scope.control.running = false;
        $scope.control.exportDataDisabled = IOLab.isNewAcquisition();
    });
});

angular.module('IOLabApp').controller('paramPlotCtrl', function ($scope, IOLab, configService, helperService) {
    'use strict';

    $scope.iolabState = IOLab.returnHardwareState();

    var remote = $scope.plotParams.remote,
        config = $scope.plotParams.config,
        sensor1 = $scope.plotParams.sensor1,
        sensor2 = $scope.plotParams.sensor2,
        param1 = configService.getSensor(sensor1.sensorType),
        param2 = configService.getSensor(sensor2.sensorType);

    // Drop down chart mode
    $scope.chartModes = [
        { val: 'stats', glyphicon: 'glyphicon-stats' },
        { val: 'zoom', glyphicon: 'glyphicon-zoom-in' },
        { val: 'move', glyphicon: 'glyphicon-move' }
    ];

    // Drop down for the coordinate selection
    $scope.coordinates1 = angular.copy(param1.legends);
    if ($scope.coordinates1.length > 1) {
        // TODO check also the different units (wheel)
        $scope.coordinates1.unshift('Norm')
    }
    $scope.coordinates2 = angular.copy(param2.legends);
    if ($scope.coordinates2.length > 1) {
        // TODO check also the different units (wheel)
        $scope.coordinates2.unshift('Norm')
    }

    // Drop down for time range selection
    $scope.timeRanges = [50, 100, 250, 500, 1000, 2000, 5000];

    $scope.isCollapsed = false;
    $scope.running = $scope.iolabState.dongleMode === 16;
    $scope.frequency = 100;
    $scope.sensor1 = sensor1;
    $scope.sensor2 = sensor2;
    $scope.control = {
        dsShow: false,  //used in drop down
        chartMode: $scope.chartModes[0],
        coordinate1: $scope.coordinates1[0],
        coordinate2: $scope.coordinates2[0],
        timeRange: $scope.timeRanges[0]
    };
});

angular.module('IOLabApp').controller('gaugeCtrl', function ($scope, IOLab, configService) {
    'use strict';

    $scope.sensorDesc = configService.getSensor($scope.sensor.sensorType).desc;
});

angular.module('IOLabApp').controller('sensorSelectionCtrl', function ($scope, IOLab, sensorSelectionService) {
    'use strict';

    var selector = sensorSelectionService,
        remote = $scope.remote;

    $scope.sensorChecked = function(sensor) {
        selector.sensorChecked(remote, sensor);
    };

    $scope.$watch(
        function() { return selector; },
        function() {
            $scope.sensors = selector.sensors[remote];
            $scope.checked = selector.checked[remote];
            $scope.notAvailable = selector.notAvailable[remote];
            $scope.sampleRates = selector.sampleRates[remote];
        },
        true
    );

    $scope.$watch(
        function() {return IOLab.returnHardwareState().dongleMode; },
        function (newValue, oldValue) {
            // Check if the new dongle state is CONNECTED
            if (newValue === 8 && oldValue !== 8) {
                selector.checkFixConfig(remote);
            }
        });
});

angular.module('IOLabApp').controller('alertCtrl', function ($scope) {
    'use strict';
    $scope.alerts = [];

    $scope.$on('alert', function (event, args) {
        $scope.alerts.push(args);
    });

    $scope.addAlert = function (message, type) {
        $scope.alerts.push({message: message, type: type});
    };

    $scope.closeAlert = function (index) {
        $scope.alerts.splice(index, 1);
    };
});

angular.module('IOLabApp').controller('ModalPairingCtrl', function ($scope, $modal) {
    'use strict';
    $scope.open = function (remoteNum) {
        var modalInstance = $modal.open({
            templateUrl: 'views/modal-pairing.html',
            backdrop: 'static',
            controller: function ($scope, $modalInstance, IOLab, configService) {
                $scope.remoteNum = remoteNum;
                $scope.iolabState = IOLab.returnHardwareState();
                $scope.dongleMode = 'IDLE';

                $scope.getPairStatus = function (remote) {
                    return (remote == 1) ? $scope.iolabState.remote1.pairStatus : $scope.iolabState.remote2.pairStatus;
                };
                $scope.pairRemote = function (remote) {
                    IOLab.pairRemote(parseInt(remote, 10), 1);
                    setTimeout(function () {
                        if ($scope.getPairStatus(remote) != 1) {
                            $scope.stopPairing();
                        }
                    }, 200);
                };
                $scope.unpairRemote = function (remote) {
                    $scope.showFindRemoteMsg = false;
                    IOLab.pairRemote(parseInt(remote, 10), 0);
                };
                $scope.findRemote = function () {
                    IOLab.sendCommand(19); // cmd 0x13
                    $scope.showFindRemoteMsg = true;
                };
                $scope.stopPairing = function () {
                    IOLab.sendCommand(17); // cmd 0x11
                };
                $scope.$watch('iolabState', function () {
                    $scope.r1Off = $scope.iolabState.remote1.rfStatus === 0;
                    $scope.r2Off = $scope.iolabState.remote2.rfStatus === 0;
                    $scope.remote1_Cfd = $scope.iolabState.remote1.configured;
                    $scope.remote2_Cfd = $scope.iolabState.remote2.configured;
                }, true);
                $scope.$on('hardwareStateChanged', function () {
                    $scope.iolabState = IOLab.returnHardwareState();
                    $scope.dongleMode = configService.getDongleMode($scope.iolabState.dongleMode).desc;
                });
                $scope.ok = function () {
                    $modalInstance.close();
                };

                $scope.findRemote();
                $scope.showFindRemoteMsg = false;
            }
        });
    };
});

angular.module('IOLabApp').controller('ModalDataBaseCtrl', function ($scope, $modal) {
    'use strict';

    $scope.open = function () {
        var modalInstance = $modal.open({
            templateUrl: 'view/modal-database.html',
            backdrop: 'static',
            controller: function ($scope, $modalInstance, IOLab, BSON) {

                $scope.collection = "Calibration";
                $scope.bson = "{}";
                $scope.query = "{}";
                $scope.result = "";

                $scope.saveBson = function (query) {
                    var json = eval("(" + query + ")");
                    IOLab.saveBson($scope.collection, json);
                };

                $scope.excuteQuery = function (query) {
                    var json = eval("(" + query + ")");

                    IOLab.executeQuery(function (res) {
                        $scope.result = JSON.stringify(BSON.deserialize(res).result);
                        if (!$scope.$$phase) {
                            $scope.$digest();
                        }
                    }, $scope.collection, json);
                };

                $scope.ok = function () {
                    $modalInstance.close();
                };
            }
        });
    };
});

angular.module('IOLabApp').controller('ModalCalibrationCtrl', function ($scope, $modal, IOLab, configService) {
    'use strict';

    // In the case we open the modal when the sensors are configured,
    // we need to know in the $watch if the configuration change is
    // due to a calibration process start, or due to the regular
    // acquisition process.
    var calibrationRunning = false,
        gaugeSensor,
        config, // Fix config selected
        sensors = [], // List of sensors from the fixed config
        sensorTypeList = []; // List of sensor to calibrate


    $scope.setCalibration = function (remote, conf) {
        calibrationRunning = true;
        config = conf;

        if (config === 3) { /*Orientation*/
            sensorTypeList = [
                { sensor: 1, desc: configService.getSensor(1).desc, incl: true },
                { sensor: 2, desc: configService.getSensor(2).desc, incl: true },
                { sensor: 3, desc: configService.getSensor(3).desc, incl: true }
            ];
        }
        if (config === 4) { /*MiniMotion*/
            sensorTypeList = [
                { sensor: 8, desc: configService.getSensor(8).desc, incl: true }   /*Force*/
            ];
        }
        IOLab.setFixedConfig(remote, config, false);
    };

    $scope.$watch(
        function() { return IOLab.returnHardwareState().remote1.fixedConfig},
        function (newValue) {
            if (newValue !== 0) {
                if (calibrationRunning) {
                    sensors = IOLab.returnAcquisitionStack()['default'].remote1.sensors;
                    gaugeSensor = config === 3 ? sensors[0] : sensors[1];
                    openModal(gaugeSensor, sensorTypeList, config, 1);
                }
            } else {
                config = 0;
                sensors = [];
                sensorTypeList = [];
            }
    });
    $scope.$watch(
        function() { return IOLab.returnHardwareState().remote2.fixedConfig},
        function (newValue) {
            if (newValue !== 0) {
                if (calibrationRunning) {
                    sensors = IOLab.returnAcquisitionStack()['default'].remote2.sensors;
                    gaugeSensor = config === 3 ? sensors[0] : sensors[1];
                    openModal(gaugeSensor, sensorTypeList, config, 2);
                }
            } else {
                config = 0;
                sensors = [];
                sensorTypeList = [];
            }
    });

    $scope.$on('dataStopped', function () {
        if (calibrationRunning) {
            IOLab.deleteAcquisition();
            calibrationRunning = false;
        }
    });

    var openModal = function (gaugeSensor, sensorTypeList, config, remote) {
        var modalInstance = $modal.open({

            templateUrl: 'views/modal-calibration.html',
            backdrop: 'static',
            resolve: {
                remote: function () { return remote; },
                config: function () { return config; },
                gaugeSensor: function () { return gaugeSensor; },
                sensorTypeList: function () { return sensorTypeList; }
            },
            controller: function ($scope, $modalInstance, $timeout, IOLab, configService, BSON,
                                  remote, config, gaugeSensor, sensorTypeList) {

                $scope.iolabState = IOLab.returnHardwareState();

                // Value injected in the controller
                $scope.remote = remote;
                $scope.config = config;
                $scope.sensor = gaugeSensor;
                $scope.sensorTypeList = sensorTypeList;

                // Indicate if we need to display the save button
                $scope.showSaveButton = false;

                // Current calibration step
                $scope.currentStep = 1;
                // Current step percentage
                $scope.currentPercentage = 0;

                // Calibration process itself
                $scope.calibrationData = {};
                $scope.calibrationDataList = [];

                if ($scope.config === 3) {
                    $scope.calibrationDataList = [
                        { data: [], validated: false, desc: 'Set the device with the x axis up', imagePath: 'images/iolab+x.png' },
                        { data: [], validated: false, desc: 'Set the device with the x axis down', imagePath: 'images/iolab-x.png' },
                        { data: [], validated: false, desc: 'Set the device with the y axis up', imagePath: 'images/iolab+y.png' },
                        { data: [], validated: false, desc: 'Set the device with the y axis down', imagePath: 'images/iolab-y.png' },
                        { data: [], validated: false, desc: 'Set the device with the z axis up', imagePath: 'images/iolab+z.png' },
                        { data: [], validated: false, desc: 'Set the device with the z axis down', imagePath: 'images/iolab-z.png' }
                    ];
                } else if ($scope.config === 4) {
                    $scope.calibrationDataList = [
                        { data: [], validated: false, desc: 'Set the device on its head (y down)', imagePath: 'images/iolab-y.png' },
                        { data: [], validated: false, desc: 'Hang the device from its force probe', imagePath: 'images/iolab-force.png' }
                    ];
                }

                // Set up the data array depending on the sensor types to calibrate
                $scope.calibrationDataList.forEach(function (calData) {
                    $scope.sensorTypeList.forEach(function (type) {
                        calData.data[type.sensor] = [];
                    });
                });

                // Number of steps in calibration
                $scope.numOfSteps = Object.getOwnPropertyNames($scope.calibrationDataList).length;
                $scope.currentPercentage = $scope.currentStep / $scope.numOfSteps * 100;

                // Function that returns the next calibration object
                // Set the variable showSaveButton to true if needed
                $scope.getNextcalibrationData = function () {
                    $scope.calibrationData = {};
                    $scope.calibrationDataList.forEach(function(sensorData) {
                        if (sensorData.validated === false && Object.getOwnPropertyNames($scope.calibrationData).length === 0) {
                            $scope.calibrationData = sensorData;
                        }
                    });

                    var count = 0;
                    $scope.calibrationDataList.forEach(function (sensorData) {
                        if (sensorData.validated === true) {
                            count++;
                        }
                    });
                    if (count === $scope.calibrationDataList.length) {
                        $scope.showSaveButton = true;
                    }
                };

                $scope.validateData = function () {
                    var timeoutId;

                    var callback = function (bson) {
                        var newData = BSON.deserialize(bson);
                        var dataMean = [];
                        var dataSigma = [];
                        var i = 0;

                        newData.data.forEach(function (entry) {
                            var mean = d3.mean(entry, function (d) { return d[2]; });
                            var variance = d3.sum(entry, function (d) {
                                return (d[1] - mean) * (d[1] - mean);
                            }) / (entry.length - 1);

                            dataMean.push(mean.toFixed(0));
                            dataSigma.push(Math.sqrt(variance).toFixed(0));

                            $scope.calibrationData.data[newData.sensorType][i++] = parseFloat(mean.toFixed(0));
                        });

                        // Check if completely fed (if data is empty => validated = false)
                        $scope.calibrationData.validated = true;
                        $scope.calibrationData.data.forEach(function(data){
                            if (data.length === 0) {
                                $scope.calibrationData.validated = false;
                            }
                        });

                        // get the next one if all sensors are validated (callback returned)
                        if ($scope.calibrationData.validated) {
                            $scope.getNextcalibrationData();
                            $scope.currentStep = $scope.currentStep + 1;
                            $scope.currentPercentage = $scope.currentStep / $scope.numOfSteps * 100;
                        }

                        if (!$scope.$$phase) {
                            $scope.$digest();
                        }
                    };

                    $scope.sensorTypeList.forEach(function (type) {
                        timeoutId = $timeout(function () {
                            var params = {
                                remote: $scope.remote,
                                sensor: type.sensor,
                                startTime: -1.8, // 1.8 seconds
                                endTime: -1,      // Last data
                                raw: true
                            };

                            IOLab.getSensorData(callback, params);

                        }, 2000);
                    });
                };

                $scope.sendCalibration = function () {
                    var i, calData, calDataJson;

                    $scope.sensorTypeList.forEach(function (type) {
                        if (type.sensor === 1 && type.incl) {
                            // Accelerometer calibration ...
                            calData = [];
                            for (i = 0; i < 6; i++) {
                                calData[i] = $scope.calibrationDataList[i].data[type.sensor];
                            }

                            calDataJson = {
                                sensor: type.sensor,
                                remoteID: $scope.iolabState['remote' + $scope.remote].remoteID,
                                calibrationDate: new Date(),
                                calOffset: [(calData[0][0] + calData[1][0]) / 2.0,
                                            (calData[2][1] + calData[3][1]) / 2.0,
                                            (calData[4][2] + calData[5][2]) / 2.0 ],
                                calScale:  [(calData[0][0] - calData[1][0]) / 2.0 / 9.81,
                                            (calData[2][1] - calData[3][1]) / 2.0 / 9.81,
                                            (calData[4][2] - calData[5][2]) / 2.0 / 9.81 ]
                            };
                            IOLab.saveCalibration(calDataJson);
                        }

                        if (type.sensor === 2 && type.incl) {
                            // http://www.ngdc.noaa.gov/geomag-web/#igrfwmm
                            // Magnetometer calibration ...
                            // Belgium  N:19.899 µT, E:  0.210 µT, D: 44.301 µT
                            // Chicago  N:19.054 µT, E: -1.249 µT, D: 50.694 µT
                            calData = [];
                            for (i = 0; i < 6; i++) {
                                calData[i] = $scope.calibrationDataList[i].data[type.sensor];
                            }

                            calDataJson = {
                                sensor: type.sensor,
                                remoteID: $scope.iolabState['remote' + $scope.remote].remoteID,
                                calibrationDate: new Date(),
                                calOffset: [(calData[0][0] + calData[1][0]) / 2.0,
                                            (calData[2][1] + calData[3][1]) / 2.0,
                                            (calData[4][2] + calData[5][2]) / 2.0 ],
                                calScale:  [(calData[0][0] - calData[1][0]) / 2.0 / (-50.7),
                                            (calData[2][1] - calData[3][1]) / 2.0 / (-50.7),
                                            (calData[4][2] - calData[5][2]) / 2.0 / (-50.7) ]
                            };
                            IOLab.saveCalibration(calDataJson);
                        }

                        if (type.sensor === 3 && type.incl) {
                            // Gyroscope calibration ...
                            calData = [];
                            for (i = 0; i < 6; i++) {
                                calData[i] = $scope.calibrationDataList[i].data[type.sensor];
                            }

                            calDataJson = {
                                sensor: type.sensor,
                                remoteID: $scope.iolabState['remote' + $scope.remote].remoteID,
                                calibrationDate: new Date(),
                                calOffset: [(calData[0][0] + calData[1][0]) / 2.0,
                                            (calData[2][1] + calData[3][1]) / 2.0,
                                            (calData[4][2] + calData[5][2]) / 2.0 ]
                            };
                            IOLab.saveCalibration(calDataJson);
                        }

                        if (type.sensor === 8 && type.incl) {
                            // Force probe calibration ... scale : 2.08 [N] = 0.212 [kg] * 9.81 [m/s^2]
                            calData = [];
                            for (i = 0; i < 2; i++) {
                                calData[i] = $scope.calibrationDataList[i].data[type.sensor];
                            }

                            calDataJson = {
                                sensor: type.sensor,
                                remoteID: $scope.iolabState['remote' + $scope.remote].remoteID,
                                calibrationDate: new Date(),
                                calOffset: (calData[0][0] - 0x7FF),
                                calScale: (calData[0][0] - calData[1][0]) / 2.08
                            };
                            IOLab.saveCalibration(calDataJson);
                        }
                    });

                    // Stop data and close modal
                    IOLab.stopData();
                    $modalInstance.close();
                };

                // Stop data and close modal
                $scope.ok = function () {
                    IOLab.stopData();
                    $modalInstance.close();
                };

                // Start the acquisition and get the first calibrationData object
                IOLab.startData();
                $scope.getNextcalibrationData();
            }
        });
    };
});