angular.module('app').service('ParametersGridService', function ($rootScope, $q, AppService, BackendService, SQConstants, OptimizationService, SQEvents, L) {

    SQEvents.add("PARAMETERS_GRID_RELOAD", "parameters-grid-reload");

    this.loadParameters = function () {
        instance.variables.length = 0;

        var paramObjs = getParamXMLObjs();
        if (paramObjs && paramObjs.length) {
            for (var i = 0; i < paramObjs.length; i++) {
                var paramObj = paramObjs[i];
                instance.variables.push(loadParamDetails(paramObj));
            }
        }
    }

    this.loadVariables = function (filePath, preserveParameters) {
        if (!valueFilled(filePath)) {
            OptimizationService.checkFileExists(null, function (fPath) {
                if (!valueFilled(fPath)) {
                    instance.variables.length = 0;
                    
                    if(OptimizationService.config.source == SQConstants.getConstants().optimization.sources.databank){
                        OptimizationService.getNumberOfTests();
                    }
                }
                else {
                    loadStrategy(fPath, preserveParameters);
                }
            });
        }
        else {
            loadStrategy(filePath, preserveParameters);
        }
    }

    function loadStrategy(filePath, preserveParameters) {
        var args = {
            projectName: AppService.getProject(),
            taskName: AppService.getTask().name,
            filePath: filePath,
            symmetricVariables: OptimizationService.config.symmetricVariables
        }

        resetAutoPreset();

        BackendService.getPromise("optimization/loadStrategy", args).then(function (result) {
            if (result.data.success) {
                OptimizationService.gui.symmetryDisabled = !result.data.symmetryEnabled;
                instance.refreshVariables(result.data.variables, preserveParameters);

                SQEvents.notifyListeners(SQEvents.get('TRADING_OPTIONS_CHANGED'), { forceSaving : true });

                AppService.updateTaskXML();

                OptimizationService.getNumberOfTests();

                $rootScope.resolveResources(result.data.xmlConfig, null, SQConstants.getConstants().projectResourceActionTypes.loadStrategyToOptimize);
            } else {
                instance.variables.length = 0;
            }
        });
    }

    function resetAutoPreset(){
        instance.autoPreset.distributionUp = 20;
        instance.autoPreset.distributionDown = 20;
        instance.autoPreset.maxSteps = 10;
    }

    this.refreshVariables = function(newVariables, preserveParameters) {
        var paramObjs = getParamXMLObjs();

        var symmetryChanged = lastSymmetry != null && lastSymmetry != OptimizationService.config.symmetricVariables;
        lastSymmetry = OptimizationService.config.symmetricVariables;

        instance.variables.length = 0;

        if (!preserveParameters) {
            loadToArray(instance.variables, newVariables);
            return;
        }

        if(!paramObjs){
            console.error("Manual settings not found in XML config");
            loadToArray(instance.variables, newVariables);
            return;
        }

        for (var i = 0; i < newVariables.length; i++) {
            var variable = newVariables[i];

            for (var p = 0; p < paramObjs.length; p++) {
                var paramObj = paramObjs[p];
                var paramName = getAttrStringValue(paramObj, 'name', 'Unknown');

                if (symmetryChanged) {
                    if (OptimizationService.config.symmetricVariables) {     //variables changed from non-symmetric to symmetric
                        if ('Lng' + variable.name == paramName || 'Long' + variable.name == paramName) {
                            variable = loadParamDetails(paramObj);
                            break;
                        }
                    }
                    else {                                      //variables changed from symmetric to non-symmetric
                        if (('Lng' + paramName == variable.name) || ('Shr' + paramName == variable.name) ||
                            ('Long' + paramName == variable.name) || ('Short' + paramName == variable.name)
                        ) {
                            variable = loadParamDetails(paramObj);
                            break;
                        }
                    }
                }
                else {
                    if (paramName == variable.name) {
                        variable = loadParamDetails(paramObj);
                        break;
                    }
                }
            }

            variable.id = newVariables[i].id;
            variable.name = newVariables[i].name;
            variable.paramType = newVariables[i].paramType;
            instance.variables.push(variable);
        }
    }

    this.onSettingsChange = function (callback) {
        instance.save(true);  //apply checkbox changes

        //set chosen XML params' attributes in order to be calculated on backend
        var optmizationObj = AppService.getCurrentTaskTabSettings("Optimization");
        if (optmizationObj) {
            for (var i = 0; i < instance.variables.length; i++) {
                var variable = instance.variables[i];

                if (variable.calculateValues) {
                    setParamToBeCalculated(optmizationObj, variable.name);
                }
            }
        }

        OptimizationService.getNumberOfTests(function (newSettings) {
            if (callback) {
                callback(newSettings);
            }
            instance.save();
        });
    }

    function setParamToBeCalculated(optmizationObj, paramName) {
        var elManualSettings = getChildElement(optmizationObj, "ManualSettings");
        var elParams = getChildElement(elManualSettings, "Params");
        var paramElements = getChildElements(elParams, "Param");

        for (var i = 0; i < paramElements.length; i++) {
            var paramElem = paramElements[i];
            if (getAttrStringValue(paramElem, "name") == paramName) {
                paramElem.setAttribute("calculateValues", true);
                paramElem.setAttribute("distributionUp", instance.autoPreset.distributionUp);
                paramElem.setAttribute("distributionDown", instance.autoPreset.distributionDown);
                paramElem.setAttribute("maxSteps", instance.autoPreset.maxSteps);
                break;
            }
        }
    }

    function loadParamDetails(paramObj) {
        return {
            id: getAttrStringValue(paramObj, 'id', 'Unknown'),
            use: getAttrBooleanValue(paramObj, 'use', false),
            name: getAttrStringValue(paramObj, 'name', 'Unknown'),
            type: getAttrStringValue(paramObj, 'type', 'Unknown'),
            start: getAttrStringValue(paramObj, 'start', ""),
            stop: getAttrStringValue(paramObj, 'stop', ""),
            step: getAttrStringValue(paramObj, 'step', ""),
            value: getAttrStringValue(paramObj, 'value', "")
        };
    }

    this.getParamTypeName = function (value) {
        for (var i = 0; i < instance.paramTypes.length; i++) {
            var paramType = instance.paramTypes[i];
            if (paramType.value == parseInt(value)) {
                return paramType.name;
            }
        }
    }

    this.getParamTypeValue = function (name) {
        for (var i = 0; i < instance.paramTypes.length; i++) {
            var paramType = instance.paramTypes[i];
            if (paramType.name == name) {
                return paramType.value;
            }
        }
    }

    this.getCategoryName = function (variable) {
        switch (variable.paramType) {
            case "ParamTypePeriod": return L.tsq("Periods");
            case "ParamTypeShift": return L.tsq("Shifts");
            case "ParamTypeOtherParam": return L.tsq("Others");
            case "ParamTypeConstant": return L.tsq("Constants");
            case "ParamTypeEntry": return L.tsq("Entry");
            case "ParamTypeExitUsed": return L.tsq("Exits / Protection");
            case "ParamTypeExitUnused": return L.tsq("Exits / Unused");
            case "ParamTypeTradingOptions": return L.tsq("Trading Options");
            default: return L.tsq("Default");
        }
    }

    this.getCategoryPosition = function (categoryName) {
        switch (categoryName) {
            case L.tsq("Periods"): return 1;
            case L.tsq("Shifts"): return 5;
            case L.tsq("Others"): return 6;
            case L.tsq("Constants"): return 4;
            case L.tsq("Entry"): return 2;
            case L.tsq("Exits / Protection"): return 3;
            case L.tsq("Exits / Unused"): return 9;
            case L.tsq("Trading Options"): return 7;
            default: return 8;
        }
    }

    this.getTradingOptionPosition = function(variable){
        switch(variable.id){
            case "ExitAtEndOfDay.ExitAtEndOfDay": return 0;
            case "ExitAtEndOfDay.EODExitTime": return 1;
            case "ExitOnFriday.ExitOnFriday": return 2;
            case "ExitOnFriday.FridayExitTime": return 3;
            case "LimitTimeRange.LimitTimeRange": return 4;
            case "LimitTimeRange.SignalTimeRangeFrom": return 5;
            case "LimitTimeRange.SignalTimeRangeTo": return 6;
            case "LimitTimeRange.ExitAtEndOfRange": return 7;
            case "MaxTradesPerDay.MaxTradesPerDay": return 8;
            case "MinMaxSLPT.MinimumSL": return 9;
            case "MinMaxSLPT.MaximumSL": return 10;
            case "MinMaxSLPT.MinimumPT": return 11;
            case "MinMaxSLPT.MaximumPT": return 12;
            default: return 20;
        }
    }

    function getParamXMLObjs() {
        var optimizationObj = AppService.getCurrentTaskTabSettings("Optimization");

        if (optimizationObj) {
            var manualSettingsObj = getChildElement(optimizationObj, "ManualSettings");
            if (manualSettingsObj) {
                var paramsObj = getChildElement(manualSettingsObj, "Params");
                if (paramsObj) {
                    return getChildElements(paramsObj, "Param");
                }
            }
        }
    }


    this.loadConfigFromFile = function (filePath, callback) {
        AppService.loadFile(filePath, function (fileContent) {
            //update xml Data object
            var optimizationObj = AppService.getCurrentTaskTabSettings("Optimization");
            var oldSettingsObj = getChildElement(optimizationObj, 'ManualSettings');
            var newSettingsObj = xmlToObject(fileContent).find('ManualSettings')[0];

            if (!oldSettingsObj) {
                $rootScope.showError(L.tsq("Cannot find ManualSettings object in task config. XML config seems to be corrupted"));
                return;
            }
            if (!newSettingsObj) {
                $rootScope.showError(L.tsq("Cannot find ManualSettings object in selected file. Have you selected the right config file?"));
                return;
            }

            oldSettingsObj.parentNode.replaceChild(newSettingsObj, oldSettingsObj);
            AppService.updateTaskXML().then(callback);
        });
    }

    this.saveVariablesSettings = function () {
        var optimizationObj = AppService.getCurrentTaskTabSettings("Optimization");
        if (!optimizationObj) {
            $rootScope.showError(L.tsq("Cannot save settings. Optimization element not found in xml config."));
            return;
        }

        var settingsObj = getChildElement(parametersObj, 'ManualSettings');
        if (!settingsObj) {
            $rootScope.showError(L.tsq("Cannot save settings. ManualSettings element not found in xml config."));
            return;
        }
        var fileContent = new XMLSerializer().serializeToString(settingsObj);
        var extension = { name: 'xml', description: 'XML Files' };

        $rootScope.saveFile(L.tsq("Save variables settings"), extension, "saveVariablesSettings", null, fileContent);
    }

    this.save = function (dontReloadGrids) {
        var optimizationObj = AppService.getCurrentTaskTabSettings("Optimization");
        if (!optimizationObj) {
            $rootScope.showError(L.tsq("Cannot save settings. Optimization element not found in xml config."));
            return;
        }

        var manualSettingsObj = getChildElements(optimizationObj, "ManualSettings")[0];
        if (!manualSettingsObj) {
            manualSettingsObj = createChild(optimizationObj, 'ManualSettings', AppService.xmlDoc, true);
        }

        var paramsObj = getChildElements(manualSettingsObj, "Params")[0];
        if (!paramsObj) {
            paramsObj = createChild(manualSettingsObj, 'Params', AppService.xmlDoc, true);
        }

        removeAllChildren(paramsObj);

        for (var i = 0; i < instance.variables.length; i++) {
            var variable = instance.variables[i];

            var paramObj = createChild(paramsObj, 'Param', AppService.xmlDoc, true);
            paramObj.setAttribute("id", variable.id);
            paramObj.setAttribute("use", variable.use == true);
            paramObj.setAttribute("name", variable.name);
            paramObj.setAttribute("type", variable.type);
            paramObj.setAttribute("start", variable.start);
            paramObj.setAttribute("stop", variable.stop);
            paramObj.setAttribute("step", variable.step);
            paramObj.setAttribute("value", variable.value);
        }

        if (!dontReloadGrids) SQEvents.notifyListeners(SQEvents.get("PARAMETERS_GRID_RELOAD"));
    }

    function onEvent(event, data) {
        switch (event) {
            case SQEvents.get('OPTIMIZATION_FILE_CHANGED'):
                if (!data.filePath) {
                    instance.variables.length = 0;
                    return;
                }
                instance.loadVariables(data.filePath, false);
                break;
            /*
            case SQEvents.get('OPTIMIZATION_SRC_CHANGED'):
                if(data){
                    instance.variables.length = 0;
                }
                else {
                    instance.loadVariables(null, false);
                }
                break;
            */
            default: return;
        }
    }


    var instance = this;

    var lastSymmetry = null;

    this.activeGridId = null;       //currently active grid

    this.paramTypes = [
        { name: "unknown", value: 0 },
        { name: "int", value: 1 },
        { name: "boolean", value: 2 },
        { name: "string", value: 3 },
        { name: "double", value: 4 }
    ];

    this.autoPreset = {
        distributionUp: 20,
        distributiondown: 20,
        maxSteps: 10
    };

    this.variables = [];

    this.loadVariables(null, true, true);

    var listenerId = "ParametersGridService";
    SQEvents.addListener(listenerId, [
        SQEvents.get("OPTIMIZATION_FILE_CHANGED"),
    ], onEvent);

});