angular.module('app').service('SQConstants', function($rootScope, $q, $injector, SQEvents, BackendService, SQWebSocketService) {

    SQEvents.add("SESSIONS_CHANGED", 'sessions-changed'); //called when the list of sessions has changed
    SQEvents.add("INSTRUMENTS_CHANGED", 'instruments-changed'); //called when the list of instruments has changed
    SQEvents.add("BASKETS_CHANGED", 'baskets-changed'); //called when the list of baskets has changed	
	SQEvents.add("BROKERS_CHANGED", 'brokers-changed'); //called when the list of brokers has changed	
    SQEvents.add("DATA_CHANGED", 'data-changed'); //called when the list of data has changed
    SQEvents.add("CUSTOM_DATA_CHANGED", 'custom-data-changed'); //called when the list of custom data has changed
    SQEvents.add("TIMEFRAMES_CHANGED", 'timeframes-changed'); //called when the list of timeframes has changed
    SQEvents.add("EXCHANGES_CHANGED", 'exchanges-changed'); //called when the list of exchanges has changed
    SQEvents.add("COUNTRIES_CHANGED", 'countries-changed'); //called when the list of countries has changed
    SQEvents.add("SECTORS_CHANGED", 'sectors-changed'); //called when the list of sectors has changed
    SQEvents.add("TASK_TEMPLATES_CHANGED", 'task-templates-changed'); //called when the list of task template files has changed
    SQEvents.add("ZOOM_CHANGED", 'zoom-changed');
    SQEvents.add("LANGUAGE_CHANGED", 'language-changed');

    SQEvents.add("CB_CHANGED", 'custom-blocks-changed');
    SQEvents.add("RG_CHANGED", 'random-groups-changed');

    this.loadConstants = function () {
        var deferred = $q.defer();

        if(window.top.getConstants){                   //algowizard cloud uses this to prevent multiple same requests
            window.top.getConstants().then(result => {
                if(result.constants){
                    saveConstants(result.constants);
                    deferred.resolve({ success: true });
                }
            });
        }
        else {
            BackendService.sendRequest('/constants/getAll', null, function (result) {
                if (result.constants) {
                    saveConstants(result.constants);
                }
                else {
                    console.error("Cannot load constants");
                }
                deferred.resolve({ success: !!result.constants });
            });
        }

        return deferred.promise;
    }

    function saveConstants(constants){
        sortTaskTemplates(constants.taskConfigs);
        angular.merge(getCommonData().constants, constants);
    }

    this.loadSettings = function() {
        var deferred = $q.defer();

        BackendService.getPromise("/main/getSettings").then(function(result) {
            if (result.data.success) {
                angular.copy(result.data.settings, getCommonData().settings);
                $rootScope.developmentMode = result.data.settings.developmentMode;
                window.developmentMode = result.data.settings.developmentMode;

                $rootScope.pickerDebug = result.data.settings.pickerDebug;
                window.pickerDebug = result.data.settings.pickerDebug;

                console.log("Settings loaded.", getCommonData().settings);
            } else {
                console.log("Cannot load settings. " + result.data.error);
            }

            deferred.resolve({ success: result.data.success });
        });

        return deferred.promise;
    }

    this.loadColumns = function() {
        var deferred = $q.defer();

        BackendService.getPromise("/main/getColumns").then(function(result) {
            if (result.data.success) {
                getCommonData().columns = result.data.commonData;
            } else {
                console.error("Columns not loaded. Error: " + result.data.error);
            }

            deferred.resolve();
        });

        return deferred.promise;
    }

    this.loadLanguages = function() {
        var deferred = $q.defer();

        BackendService.getPromise("/language/list").then(function(result) {
            if (result.data.success) {
                var selectedLang = null;

                getCommonData().languages.length = 0;
                for (var i = 0; i < result.data.langs.length; i++) {
                    var lang = result.data.langs[i];
                    getCommonData().languages.push(lang);
                }
            } else {
                console.error("Languages not loaded. Error: " + result.data.error);
            }

            if (getCommonData().languages.length == 0) {
                getCommonData().languages.push(instance.defaultLanguage);
            }

            deferred.resolve();
        });

        return deferred.promise;
    }

    this.loadProjectConfigs = function() {
        var deferred = $q.defer();

        BackendService.getPromise("/main/getConfigs", null, 'GET').then(function(result) {
            if (result.data.success) {
                getCommonData().configs = result.data.configs;

                if (!mainAppExists()) {
                    var AppService = $injector.get('AppService');
                    AppService.initXMLObjects();
                }
            } else {
                console.error("Project configs not loaded. Error: " + result.data.error);
            }

            deferred.resolve();
        });

        return deferred.promise;
    }

    this.loadInfo = function() {
        BackendService.sendRequest('/main/getCommon', null, function(data) {
            getCommonData().chromiumBrowser = data.chromiumBrowser;
            getCommonData().product = data.product;
            getCommonData().appVersion = data.appVersion;

            $rootScope.chromiumBrowser = data.chromiumBrowser;
            $rootScope.product = data.product;
            $rootScope.appVersion = data.appVersion;
        });
    }

    this.loadTradelistViews = function() {
        var promise = BackendService.getPromise('/tradelistviews/getViews');
        promise.then(function(result) {
            instance.getConstants().tradelistViews = result.data.tradelistViews;
        });

        return promise;
    }

    this.loadDatabankViews = function() {
        var promise = BackendService.getPromise('/dbviews/getViews');
        promise.then(function(result) {
            instance.getConstants().databankViews = result.data.databankViews;
        });

        return promise;
    }

    function onWebSocketData(args) {
        if (args.sessions) {
            reloadConstants('sessions', "SESSIONS_CHANGED", args.sessions);
        }
        if (args.instruments) {
            reloadConstants('instruments', "INSTRUMENTS_CHANGED", args.instruments);
        }
        if (args.baskets) {
            reloadConstants('baskets', "BASKETS_CHANGED", args.baskets);
        }
        if (args.brokers) {
            reloadConstants('brokers', "BROKERS_CHANGED", args.brokers);
        }		
        if (args.data) {
            if(args.data.action && args.data.action=='download_single'){
                SQEvents.notifyListeners(SQEvents.get("DATA_CHANGED"), args.data, args);
            }else{
                instance.getConstants()['data'] = args.data.data;
                SQEvents.notifyListeners(SQEvents.get("DATA_CHANGED"), args.data, args);
            }
        }

        if (args.customdata) {
            instance.getConstants()['customdata'] = args.customdata.data;
            SQEvents.notifyListeners(SQEvents.get("CUSTOM_DATA_CHANGED"), args.customdata.data);
        }
        if (args.timeframes) {
            reloadConstants('timeframes', 'TIMEFRAMES_CHANGED', args.timeframes);
        }
        if (args.exchanges) {
            reloadConstants('exchanges', 'EXCHANGES_CHANGED', args.exchanges);
        }
        if (args.countries) {
            reloadConstants('countries', 'COUNTRIES_CHANGED', args.countries);
        }
        if (args.sectors) {
            reloadConstants('sectors', 'SECTORS_CHANGED', args.sectors);
        }
        if (args.tradelistViews) {
            reloadConstants('tradelistViews', 'TRADELIST_VIEWS_LOADED', args.tradelistViews);
            //if selected view was removed, select default tradelist view 
            if (!getItem(instance.getConstants().tradelistViews, 'name', instance.getSettings().tradelistView)) {
                instance.getSettings().tradelistView = 'Default';
                SQEvents.notifyListeners(SQEvents.get("TRADELIST_VIEW_CHANGED"), 'Default');
            }
        }
        if (args.databankViews) {
            reloadConstants('databankViews', 'DATABANK_VIEWS_LOADED', args.databankViews);
        }
        if (args.taskConfigs) {
            sortTaskTemplates(args.taskConfigs);
            reloadConstants('taskConfigs', 'TASK_TEMPLATES_CHANGED', args.taskConfigs);
        }
        if (args.customBlocks) {
            SQEvents.notifyListeners(SQEvents.get("CB_CHANGED"), args.customBlocks);
        }
        if (args.randomGroups) {
            SQEvents.notifyListeners(SQEvents.get("RG_CHANGED"), args.randomGroups);
        }
    }

    function sortTaskTemplates(templates) {
        try {
            templates.sort(function(a, b) {
                var taskTypeComparation = a.project.localeCompare(b.project);
                return taskTypeComparation == 0 ? a.name.localeCompare(b.name) : taskTypeComparation;
            });
        } catch (err) {
            console.error("Cannot sort task templates. " + err);
        }
    }

    function reloadConstants(constantName, eventName, data) {
        instance.getConstants()[constantName] = data;
        SQEvents.notifyListeners(SQEvents.get(eventName), data);
    }

    this.getConstants = function() {
        return getCommonData().constants;
    }

    this.getSettings = function() {
        return getCommonData().settings;
    }

    this.getColumns = function() {
        return getCommonData().columns;
    }

    //nastavi object project + tasky. Format je najlepsie vidno v java kode SQProject.toJSON();
    this.setProjectConfigObj = function(projectName, projectConfig){
        var configs = getCommonData().configs;
        configs[projectName] = projectConfig;
    }

    this.getProjectConfigObj = function(projectName){
        return getCommonData().configs[projectName];
    }

    //vrati xml hlavneho projektu 
    this.getProjectConfig = function(projectName, convertToXMLObject) {
        let configs = getCommonData().configs;

        if(!projectName  || projectName == 'TaskManager' || projectName == 'PORTFOLIOCOMPOSER') {
            return;
        }

        if(!configs[projectName]){
            console.error("Config for project " + projectName + " not found");
            return;
        }

        let projectXML = configs[projectName].xml;
        
        if(convertToXMLObject){
            return xmlToObject(projectXML).find("Project")[0];
        }
        return projectXML;
    }

    //nastavi xml hlavneho projektu
    this.setProjectConfig = function(projectName, projectXML, tasks = undefined) {
        var configs = getCommonData().configs;
        
        if(!configs[projectName]){
            console.error("Config for project " + projectName + " not found");
            return;
        }

        if(!projectXML) {
            delete configs[projectName];
        } else {
            configs[projectName].xml = projectXML;
            if(tasks){
                configs[projectName].tasks = tasks;
            }
        }        
    }

    //nastavi xml daneho tasku
    this.setTaskConfig = function(projectName, taskFileName, taskSettingsXML) {
        var config = getCommonData().configs[projectName];
        config.tasks = config.tasks || {};
        config.tasks[taskFileName] = taskSettingsXML;
    }

    this.getTaskConfig = function(projectName, taskFileName, convertToXMLObject){
        var config = getCommonData().configs[projectName];
        if(!config.tasks){
            console.error("Cannot get config for project " + projectName + " - tasks obj doesn't exist");
            return null;
        }

        if(convertToXMLObject){
            return xmlToObject(config.tasks[taskFileName]).find("Settings")[0];
        }
        return config.tasks[taskFileName];
    }

    this.getColumnsByType = function(types, exceptColumns, onlyNumbers) {
        var columns = [];

        var ColumnTypes = getCommonData().constants.columnValueTypes;

        for (var t = 0; t < types.length; t++) {
            var type = types[t];
            var category = type.category;
            var availableColumns = _getColumnsByType(type);

            var cols = angular.copy(availableColumns);
            for (var i = 0; i < cols.length; i++) {
                var column = cols[i];

                if (onlyNumbers && column.type == ColumnTypes.Text) {
                    continue;
                }

                column.category = category;
                column.columnType = type.value;
                column.value = column.class;

                if (type.dataType != 'default') {
                    column.type = type.dataType;
                }

                if (exceptColumns && exceptColumns.includes(column.class)) {
                    continue;
                }

                columns.push(column);
            }
        }

        return columns;
    }

    function _getColumnsByType(type) {
        if (type.value == instance.getConstants().databankColumnTypes.wfSpecial.value) {
            return instance.getColumns().wfColumns || [];
        }

        return instance.getColumns().databankColumns || [];
    }

    this.getLanguages = function() {
        return getCommonData().languages;
    }

    this.getTranslations = function() {
        return getCommonData().translations;
    }

    function getCommonData() {
        return isMainApp() ? window.commonData : window.top.commonData;
    }

    this.getCommonData = function() {
        return isMainApp() ? window.commonData : window.top.commonData;
    }

    this.getEngineKey = function(engine) {
        let engineKeys = getCommonData().constants.engineKeys;
    
        if (engineKeys && engineKeys.hasOwnProperty(engine)) {
            return engineKeys[engine];
        }

        return engine;
    }

    this.loadData = function() {
        console.log("Loading data...");

        var deferred = $q.defer();

        BackendService.getPromise("/main/getData").then(function(result) {
            if (result.data.success) {
                let data = result.data;

                let keys = Object.keys(data);

                for (let i = 0; i < keys.length; i++) {
                    let key = keys[i];
                    getCommonData().constants[key] = data[key];

                    if (key == 'data') {
                        getCommonData().constants.data = getCommonData().constants.data.data;
                    }
                }

                deferred.resolve();
            } else {
                console.error("Data not loaded. Error: " + result.data.error);
            }
        });

        return deferred.promise;
    }

    this.loadAll = function() {
        var promises = [
            instance.loadConstants(),
            instance.loadSettings(),
            instance.loadLanguages(),
            instance.loadInfo(),
            instance.loadData()
        ];


        if (!isQuantDataManager()) {
            promises.push(instance.loadColumns());
            promises.push(instance.loadTradelistViews());

            if (!isAlgoWizard()) {
                promises.push(instance.loadProjectConfigs());
                promises.push(instance.loadDatabankViews());
            }
        }

        return $q.all(promises);
    }

    this.NO_RESULT_CHOSEN = Ltsqt("No result chosen - Double-click on result on databank to see the details");
    this.ONLY_IN_PRO = Ltsqt("Only in Pro version");
    this.limitPrecisions = true;

    $rootScope.MaxOptimizationsWarning = Ltsqt("You chose an extreme number of optimizations. It will take a very long time and this number of tests might cause memory problems. Use it at your own risk.")

    var instance = this;

    function init(baseObj) {
        baseObj.constants = {};
        baseObj.reloadingConstants = [];
        baseObj.settings = {};
        baseObj.columns = {};
        baseObj.languages = [];
        baseObj.translations = [];
        baseObj.configs = [];
        baseObj.chromiumBrowser = false;
        baseObj.product = null;
        baseObj.appVersion = 0;
    }

    if (isMainApp()) {
        window.commonData = window.commonData || {};
        init(window.commonData);
    } else if (!mainAppExists()) { //special case for AlgoWizard
        window.top.commonData = window.top.commonData || {};
        init(window.top.commonData);
    } else {
        //broadcast constants changes to child apps

        var listenerId = "SQConstants-" + window.appConfig.product;
        var listeners = [];

        if (isQuantDataManager()) {
            listeners = [
                SQEvents.get('INSTRUMENTS_CHANGED'),
                SQEvents.get('DATA_CHANGED'),
                SQEvents.get('TIMEFRAMES_CHANGED'),
                SQEvents.get('EXCHANGES_CHANGED'),
                SQEvents.get('COUNTRIES_CHANGED'),
                SQEvents.get('SECTORS_CHANGED'),
                SQEvents.get('BROKERS_CHANGED')
            ]

        } else {
            listeners = [
                SQEvents.get('SESSIONS_CHANGED'),
                SQEvents.get('INSTRUMENTS_CHANGED'),
                SQEvents.get('BASKETS_CHANGED'),
                SQEvents.get('DATA_CHANGED'),
                SQEvents.get('CUSTOM_DATA_CHANGED'),
                SQEvents.get('TIMEFRAMES_CHANGED'),
                SQEvents.get('EXCHANGES_CHANGED'),
                SQEvents.get('COUNTRIES_CHANGED'),
                SQEvents.get('SECTORS_CHANGED'),
                SQEvents.get('TRADELIST_VIEWS_LOADED'),
                SQEvents.get('DATABANK_VIEWS_LOADED'),
                SQEvents.get('TASK_TEMPLATES_CHANGED'),
                SQEvents.get('BROKERS_CHANGED'),
                SQEvents.get('CB_CHANGED'),
                SQEvents.get('RG_CHANGED')
            ]
        }

        window.top.addListener(listenerId, listeners, function(event, data) {
            SQEvents.notifyListeners(event, data);
        });
    }

    SQWebSocketService.subscribeGeneral("SQConstants-" + window.appConfig.appCode, onWebSocketData);

});