angular.module("app").directive("parametersGrid", function ($rootScope, $timeout, SQEvents, AppService, UniqueIdGenerator, ParametersGridService, L) {
    return {
        restrict: 'E',
        replace: true,
        templateUrl: "../app/directives/parametersGrid/parametersGrid.html",
        scope: {
            instance: '=',
            simple: '@',
            onChange: '&'
        },
        link: function ($scope, element, attrs) {

            $scope.instance.reloadVariables = function () {
                //console.error("reloadVariables");
                ParametersGridService.loadVariables(null, true);
            }

            $scope.instance.getVariables = function() {
                return $scope.variables;
            }

            $scope.instance.autoPreset = function(autoPresetSettings) {
                $scope.autoPreset.distributionUp = autoPresetSettings.distributionUp;
                $scope.autoPreset.distributionDown = autoPresetSettings.distributionDown;
                $scope.autoPreset.maxSteps = autoPresetSettings.maxSteps;
                
                for(var i=0; i<$scope.variables.length; i++){
                    var variable = $scope.variables[i];
                    if(variable.use){
                        variable.calculateValues = true;
                    }
                }

                ParametersGridService.onSettingsChange(function(newSettings){
                    //update XML config - contains new parameters settings
                    var oldOptimizationObj = AppService.getCurrentTaskTabSettings("Optimization");
                    
                    var newXmlConfig = xmlToObject(newSettings);
                    var newOptimizationObj = newXmlConfig.find("Optimization")[0];

                    if(oldOptimizationObj && newOptimizationObj){
                        oldOptimizationObj.parentNode.replaceChild(newOptimizationObj, oldOptimizationObj);
                    }

                    ParametersGridService.refreshVariables(angular.copy($scope.variables), true);

                    AppService.updateTaskXML().then($scope.onChange);
                });
            }

            function initParametersGrid() {
                parametersGrid = new sqGrid($scope.gridId);

                var columns = [
                    { title: L.tsq('ID'), type: "text", sort: 'text' },
                    { title: L.tsq('Name'), type: "text", sort: 'text', align: "left" },
                    { title: L.tsq('Type'), type: "text", sort: 'text' },
                    { title: L.tsq('Value'), type: "text", sort: 'text' },
                    { title: L.tsq('Start'), type: "text", sort: 'text' },
                    { title: L.tsq('Stop'), type: "text", sort: 'text' },
                    { title: L.tsq('Step'), type: "text", sort: 'text' },
                ];

                var widths = [10, 120];

                if ($scope.simple) {
                    columns.splice(2, 1);
                    widths.push(60, 65, 60, 60);
                }
                else {
                    widths.push(100, 80, 80, 80, 80);
                }

                parametersGrid.setFirstColumnAsId(true, true);
                parametersGrid.enableCheckboxes(true, false);
                parametersGrid.setColumns(columns, !!parametersGrid);
                parametersGrid.setWidths(widths, !!parametersGrid);
                parametersGrid.setEmptyGridText(L.tsq('No Parameters available.'));
                parametersGrid.disableSorting();

                parametersGrid.defineWidget('spinnerWidget', sqGridSpinner);
                parametersGrid.defineWidget('timepickerWidget', sqGridTimepicker);
                parametersGrid.defineWidget("selectWidget", sqGridSelect);

                parametersGrid.cellEventHandler = function (rowIndex, cellIndex, eventName, args) {
                    if (eventName == "spinnerUpdated" || eventName == "timepickerUpdated" || eventName == "selectUpdated") {
                        var variable = getVariableByRowId(rowIndex);
                        if (!variable) {
                            console.error(L.tsq("Cannot get variable on row") + " " + rowIndex);
                            return;
                        }
                        
                        if (checkVariableValue('' + args[0], variable.type)) {
                            let variableChanged = updateVariableValue(variable, cellIndex, args[0]);

                            ParametersGridService.activeGridId = $scope.gridId;
                            
                            if(variableChanged){
                                ParametersGridService.onSettingsChange();
                                $scope.onChange();
                            }
                        }
                        else {
                            let valuesName = "";

                            switch(variable.type){
                                case "time": valuesName = "cellsTimepickerValues"; break;
                                case "boolean": valuesName = "cellsSelectValues"; break;
                                default: valuesName = "cellsSpinnerValues"; break;
                            }

                            var spinnerValues = parametersGrid.getUserData(rowIndex, valuesName);
                            spinnerValues[cellIndex] = getVariableValue(variable, cellIndex);

                            parametersGrid.bodyRedraw();
                        }
                    }
                };

                parametersGrid.onSelectionChanged = function (checkedRows, values) {
                    if (!checkedRows || !checkedRows.length) return;

                    for(var i=0; i<checkedRows.length; i++){
                        var rowIndex = checkedRows[i];
                        var rowId = parametersGrid.getRowId(rowIndex);
                        var checked = values[i];

                        if (rowId.indexOf('c') == 0) {        //whole category
                            var index = parseInt(rowIndex) + 1;

                            while (true) {
                                if (index == parametersGrid.getNumberOfRows()) break;

                                var curRowId = parametersGrid.getRowId(index);
                                if (curRowId.indexOf('c') == 0) break;

                                parametersGrid.setRowChecked(index, checked);

                                var variable = getVariableByRowId(index);
                                if (variable) variable.use = checked;

                                index++;
                            }
                        }
                        else {                              //single variable
                            var variable = getVariableByRowId(rowIndex);
                            if (variable) variable.use = checked;
                        }
                    }

                    ParametersGridService.activeGridId = $scope.gridId;
                    ParametersGridService.onSettingsChange();

                    $scope.onChange();
                };

                parametersGrid.headerRedraw();

                ParametersGridService.loadParameters();
            }

            function refreshGrid() {
                if (ParametersGridService.activeGridId == $scope.gridId) {
                    ParametersGridService.activeGridId = null;
                    return;
                }

                parametersGrid.removeAllRows(true, true);

                var categories = [];
                var tradingOptionsCategory;

                for (var i = 0; i < $scope.variables.length; i++) {
                    var variable = $scope.variables[i];
                    var categoryName = ParametersGridService.getCategoryName(variable);
                    var targetCategory = getItem(categories, 'name', categoryName);

                    if (!targetCategory) {
                        targetCategory = {
                            name: categoryName,
                            position: ParametersGridService.getCategoryPosition(categoryName),
                            allChecked: variable.use,
                            variables: [variable]
                        };
                        categories.push(targetCategory);
                    }
                    else {
                        targetCategory.variables.push(variable);
                        targetCategory.allChecked = targetCategory.allChecked && variable.use;
                    }

                    if(categoryName == L.tsq("Trading Options")){
                        tradingOptionsCategory = targetCategory;
                    }
                }

                if(tradingOptionsCategory && tradingOptionsCategory.variables){
                    tradingOptionsCategory.variables.sort(function(a, b){
                        var aPos = ParametersGridService.getTradingOptionPosition(a);
                        var bPos = ParametersGridService.getTradingOptionPosition(b);
                        return aPos - bPos;
                    });
                }

                //sort categories order
                categories.sort(function(a, b){
                    return a.position - b.position;
                });

                for (var i = 0; i < categories.length; i++) {
                    var category = categories[i];
                    var catRowId = 'c' + i;

                    var catRowData = [catRowId, category.name, "", "", "", "", ""];
                    if ($scope.simple) catRowData.splice(2, 1);

                    parametersGrid.addRow(catRowData, true);

                    var rowIndex = parametersGrid.getRowIndex(catRowId);

                    parametersGrid.setRowChecked(rowIndex, category.allChecked);
                    parametersGrid.setRowClass(rowIndex, "category");

                    for (var v = 0; v < category.variables.length; v++) {
                        var variable = category.variables[v];
                        var rowId = 'v' + (1000 * i + v);

                        var rowData = [
                            rowId,
                            variable.name,
                            variable.type,
                            getValueCellValue(variable),
                            getStartCellValue(variable),
                            getStopCellValue(variable),
                            getStepCellValue(variable)
                        ];

                        if ($scope.simple) rowData.splice(2, 1);

                        parametersGrid.addRow(rowData, true);

                        parametersGrid.setRowChecked(parametersGrid.getRowIndex(rowId), variable.use);
                    }
                }

                parametersGrid.bodyRedraw();
                window.dispatchEvent(new Event('resize'));
            }

            function formatDefaultValue(type, value){
                if(type == 'time'){
                    var intValue = parseInt(value);
                    if(intValue === 0){
                        return "00:00";
                    }
                    else {
                        var finalValue = addLeadingZeros("" + intValue, 4);
                        return finalValue.substr(0, 2) + ":" + finalValue.substr(2, 2);
                    }
                }
                else return value;
            }

            function getValueCellValue(variable){
                switch(variable.type){
                    case "boolean": return '{{selectWidget options=\'{"true":"true","false":"false"}\' value=\'' + variable.value + '\'}}';
                    case "time": return "{{timepickerWidget value='" + variable.value + "' min='0' max='2359' step='1'}}";
                    default: return "{{spinnerWidget value='" + variable.value+ "' min='-999999999' max='999999999'}}";
                }
            }

            function getStartCellValue(variable){
                switch(variable.type){
                    case "boolean": return variable.value;
                    case "time": return "{{timepickerWidget value='" + variable.start + "' min='0' max='2359' step='1'}}";
                    default: return "{{spinnerWidget value='" + variable.start+ "' min='-999999999' max='999999999'}}";
                }
            }

            function getStopCellValue(variable){
                switch(variable.type){
                    case "boolean": return variable.value != 'true';
                    case "time": return "{{timepickerWidget value='" + variable.stop + "' min='0' max='2359' step='1'}}";
                    default: return "{{spinnerWidget value='" + variable.stop+ "' min='-999999999' max='999999999'}}";
                }
            }
            
            function getStepCellValue(variable){
                switch(variable.type){
                    case "boolean": return "";
                    case "time": return "{{timepickerWidget value='" + variable.step + "' min='0' max='2359' step='1'}}";
                    default: return "{{spinnerWidget value='" + variable.step+ "' min='-999999999' max='999999999'}}";
                }
            }

            function getVariableByRowId(rowIndex) {
                var variable = getItem($scope.variables, 'name', parametersGrid.getCellValue(rowIndex, 1));
                if (!variable) {
                    console.error("Cannot get variable at row index '" + rowIndex + "'");
                }

                return variable;
            }

            function getParamType(rowIndex) {
                return parametersGrid.getCellValue(rowIndex, 2);
            }

            function checkVariableValue(newValue, type) {
                if (type == 'int') {
                    if (newValue != parseInt(newValue) || newValue.indexOf(".") >= 0) {
                        $rootScope.showError(L.tsq("Invalid input for type '%s'", [type]));
                        return false;
                    }
                }
                else if (type == 'double') {
                    if (newValue != parseFloat(newValue)) {
                        $rootScope.showError(L.tsq("Invalid input for type '%s'", [type]));
                        return false;
                    }
                }
                else if(type == 'time'){
                    var intValue = parseInt(newValue);
                    if (newValue != intValue || intValue >= 2400 || intValue < 0) {
                        $rootScope.showError(L.tsq("Invalid input for type '%s'", [type]));
                        return false;
                    }
                }
                else if(type == 'boolean'){
                    if(newValue !== 'true' && newValue !== 'false'){
                        return false;
                    }
                }
                else {
                    return false;
                }

                return true;
            }

            function updateVariableValue(variable, colIndex, value) {
                colIndex = $scope.simple ? colIndex : (colIndex - 1);       //simple grid doesn't have Type column

                let oldValue = getVariableValue(variable, colIndex);

                switch (colIndex) {
                    case 2: variable.value = value; break;
                    case 3: variable.start = value; break;
                    case 4: variable.stop = value; break;
                    case 5: variable.step = value; break;
                    default:
                        console.error("unexpected column index " + cellIndex);
                        return false;
                }

                return oldValue != value;
            }

            function getVariableValue(variable, colIndex) {
                switch (colIndex) {
                    case 2: return variable.value;
                    case 3: return variable.start;
                    case 4: return variable.stop;
                    case 5: return variable.step;
                    default:
                        console.error("unexpected column index " + colIndex);
                        return 0;
                }
            }

            function checkSignLogic(rowId) {
                var rowIndex = parametersGrid.getRowIndex(rowId);

                var start = parseFloat(parametersGrid.cellById(rowId, 3).getValue());
                var stop = parseFloat(parametersGrid.cellById(rowId, 4).getValue());
                var step = parseFloat(parametersGrid.cellById(rowId, 5).getValue());

                if (step == 0) {
                    $rootScope.showError(L.tsq("Zero step used at row %d. Please correct the step value or leave the parameter unchecked.", [(rowIndex + 1)]));
                    return false;
                }
                else if ((stop - start) / step >= 0) return true;
                else {
                    $rootScope.showError(L.tsq("Invalid values logic at row %d. Please correct the start/stop/step values.", [(rowIndex + 1)]));
                    return false;
                }
            }

            function skipStringAndBoolParams(selectedRows) {
                for (var i = selectedRows.length - 1; i >= 0; i--) {
                    var rowId = selectedRows[i];
                    var type = getParamType(rowId);
                    if (type == 'boolean' || type == 'string') {
                        selectedRows.splice(i, 1);
                    }
                }
            }

            function onEvent(event, data) {
                if (event == SQEvents.get('PARAMETERS_GRID_RELOAD')) {
                    refreshGrid();
                }
                else return;

                try { $scope.$digest(); } catch (er) { }
            }

            var destroyVariablesWatch = $scope.$watch("variables", function (newValue, oldValue) {
                if (ignoreChanges) return;

                if (!loaded) {
                    initParametersGrid();
                }
                else {
                    refreshGrid();
                }
            });

            $scope.$on("$destroy", function () {
                destroyVariablesWatch();
                UniqueIdGenerator.removeId($scope.gridId);
                SQEvents.removeListener(listenerId);
            });

            var parametersGrid;
            var loaded = false;
            var ignoreChanges = false;

            $scope.gridId = UniqueIdGenerator.generateId();

            $scope.autoPreset = ParametersGridService.autoPreset;
            $scope.variables = ParametersGridService.variables;

            var listenerId = "ParametersGrid-" + $scope.gridId;
            SQEvents.addListener(listenerId, [SQEvents.get("PARAMETERS_GRID_RELOAD")], onEvent);

        }
    };
});