////// ----------------------- Connection "class" ---------------------- ////////////

/* jshint browser:true */
/* global document*/
/* global console*/
/* global session*/
/* global window*/
/* global location*/
/* global setTimeout*/
/* global clearTimeout*/
/* global io*/
/* global $*/

// The idea of servConn is to use this class later in every addon (Yahui, Control and so on).
// The addon just must say, what must be loaded (values, objects, indexes) and
// the class loads it for addon. Authentication will be done automatically, so addon does not care about it.
// It will be .js file with localData and servConn

var servConn = {
    _socket:            null,
    _hub:               null,
    _onConnChange:      null,
    _onUpdate:          null,
    _isConnected:       false,
    _disconnectedSince: null,
    _connCallbacks: {
        onConnChange: null,
        onUpdate:     null,
        onRefresh:    null,
        onAuth:       null
    },
    _authInfo:          null,
    _isAuthDone:        false,
    _isAuthRequired:    false,
    _authRunning:       false,
    _cmdQueue:          [],
    _connTimer:         null,
    _type:              1, // 0 - SignalR, 1 - socket.io, 2 - local demo
    _timeout:           0, // 0 - use transport default timeout to detect disconnect
    _reconnectInterval: 10000, // reconnect interval

    getIsConnected: function () {
        return this._isConnected;
    },
    getType: function () {
        return this._type;
    },
    init: function (connCallbacks, type) {
        if (typeof type == "string") {
            type = type.toLowerCase();
        }

        if (typeof session !== 'undefined') {
            var user = session.get('user');
            if (user) {
                this._authInfo = {
                    user: user,
                    hash: session.get('hash'),
                    salt: session.get('salt')
                };
            }
        }

        // If autodetect
        if (type === undefined) {
            type = visConfig.connType;

            if (type === undefined || type === null) {
                if (typeof io != "undefined") {
                    type = 1; // socket.io
                } else if (typeof $ != "undefined" && typeof $.connection != "undefined") {
                    type = 0; // SignalR
                } else {
                    type = 2; // local demo
                }
            }
        }

        this._connCallbacks = connCallbacks;

        var connLink = visConfig.connLink || window.localStorage.getItem("connLink");
        if (type === 0 || type == 'signalr') {
            this._type = 0;

            this.connection = $.hubConnection(connLink);
            this._hub = this.connection.createHubProxy("serverHub");
            //this._hub = $.connection.serverHub;
            if (!this._hub) {
                this._autoReconnect();
                return;
            }

            var that = this;
            this._hub.on("updatePointValue", function (model) {
                if (that._connCallbacks.onUpdate) {
                    that._connCallbacks.onUpdate({name: model.name, val: model.val, ts: model.ts, ack: model.ack});
                }
            });

            this._hub.on("authRequest", function (message, salt) {
                that._isAuthRequired = true;
                that._isAuthDone     = false;

                console.log('Auth request: ' + message);

                if (that._authInfo) {
                    // If we have auth information, send it automatically
                    that.authenticate();
                } else if (that._connCallbacks.onAuth) {
                    // Else request from GUI input of user, pass and data (salt)
                    that._connCallbacks.onAuth(message, salt);
                } else {
                    // TODO Translate
                    alert('server requires authentication, but no onAuth callback is installed!');
                }

            });
            this._hub.on("refresh", function () {
                if (that._connCallbacks.onRefresh) {
                    that._connCallbacks.onRefresh();
                }
            });
            this.connection.start().done(function () {
                that._isConnected = true;
                if (that._connCallbacks.onConnChange) {
                    that._connCallbacks.onConnChange(that._isConnected);
                }
                that._autoReconnect();
            });
            this.connection.reconnecting(function () {
                that._isConnected = false;
                if (that._connCallbacks.onConnChange) {
                    that._connCallbacks.onConnChange(that._isConnected);
                }
                that._autoReconnect();
            });
            this.connection.reconnected(function () {
                that._isConnected = true;
                if (that._connCallbacks.onConnChange) {
                    that._connCallbacks.onConnChange(that._isConnected);
                }
                that._autoReconnect();
            });
            this.connection.disconnected(function () {
                that._isConnected = false;
                if (that._connCallbacks.onConnChange) {
                    that._connCallbacks.onConnChange(that._isConnected);
                }
                that._autoReconnect();
            });
        } else if (type == 1 || type == "socket.io") {
            this._type = 1;
            if (typeof io != "undefined") {
                if (typeof socketSession == 'undefined') {
                    socketSession = 'nokey';
                }
                var url;
                if (connLink) {
                    url = connLink;
                } else {
                    url = jQuery(location).attr('protocol') + '//' + jQuery(location).attr('host');
                }

                this._socket = io.connect(url, {
                    'query': 'key=' + socketSession,
                    'reconnection limit': 10000,
                    'max reconnection attempts': Infinity
                });

                this._socket._myParent = this;

                this._socket.on('connect', function () {
                    //console.log("socket.io connect");
                    if (this._myParent._isConnected === true) {
                        // This seems to be a reconnect because we're already connected!
                        // -> prevent firing onConnChange twice
                        return;
                    }
                    this._myParent._isConnected = true;
                    if (this._myParent._connCallbacks.onConnChange) {
                        this._myParent._connCallbacks.onConnChange(this._myParent._isConnected);
                    }
                    //this._myParent._autoReconnect();
                });

                this._socket.on('disconnect', function () {
                    //console.log("socket.io disconnect");
                    this._myParent._disconnectedSince = (new Date()).getTime();
                    this._myParent._isConnected = false;
                    if (this._myParent._connCallbacks.onConnChange) {
                        this._myParent._connCallbacks.onConnChange(this._myParent._isConnected);
                    }
                    // Auto-Reconnect
                    //this._myParent._autoReconnect();
                });
                this._socket.on('reconnect', function () {
                    //console.log("socket.io reconnect");
                    var offlineTime = (new Date()).getTime() - this._myParent._disconnectedSince;
                    //console.log("was offline for " + (offlineTime / 1000) + "s");

                    // TODO does this make sense?
                    //if (offlineTime > 12000) {
                        //window.location.reload();
                    //}
                    this._myParent._isConnected = true;
                    if (this._myParent._connCallbacks.onConnChange) {
                        this._myParent._connCallbacks.onConnChange(this._myParent._isConnected);
                    }
                    //this._myParent._autoReconnect();
                });
                this._socket.on('refreshAddons', function () {
                    if (this._myParent._connCallbacks.onRefresh) {
                        this._myParent._connCallbacks.onRefresh();
                    }
                });

                this._socket.on('event', function (obj) {
                    if (obj === null) {
                        return;
                    }

                    var o = {};
                    o.name = obj[0] + "";
                    o.val  = obj[1];
                    o.ts   = obj[2];
                    o.ack  = obj[3];
                    o.lc   = obj[4];

                    if (this._myParent._connCallbacks.onUpdate) {
                        this._myParent._connCallbacks.onUpdate(o);
                    }

                });
            } //else {
                //console.log("socket.io not initialized");
            //}
        } else if (type == 2 || type == "local") {
            this._type       = 2;
            this._isAuthDone = true;

            this._isConnected = true;
            if (this._connCallbacks.onConnChange) {
                this._connCallbacks.onConnChange(this._isConnected);
            }
        }

        // start connection timer
        //this._autoReconnect();
        // Detect if running under cordova
        var app = document.URL.indexOf('http://') === -1 && document.URL.indexOf('https://') === -1;
        if (app) {
            $('body').append('<div id="system_menu" style="z-index:3000;width: 30%; height:30px; background-color: white; position:absolute; bottom: 0; left: 35%; display: none; border:1px solid black; text-align:center">' + _('Settings') + '</a></div>');
            $("#system_menu").click(function () {
                console.log("Goto settings");
                if (window.localStorage) {
                    window.localStorage.setItem("connSettings", true);
                }
                // Call settings window
                window.location.href = '../index.html';
            });
            // Install menu on menu button
            document.addEventListener("menubutton", function () {
                var menuDiv = $("#system_menu");
                if (servConn.menuOpen) {
                    console.log("close the menu");
                    menuDiv.hide();
                    servConn.menuOpen = false;
                } else {
                    console.log("open the menu");
                    menuDiv.show();
                    servConn.menuOpen = true;
                }
            }, false);
        }
    },
    // After 3 hours debugging...
    // It is questionable if ths function really required.
    // Socket.io automatically reconnects to the server and sets _isConnected to true, so
    // it will never happened...
    // And in the future we will have two servers - one for static pages and one for socket.io.

    // @Bluefox: i introduced this function because socket.io didn't reconnect sometimes after a long
    // offline period (several minutes/hours). Since 0.9beta97 it's buggy and causes a reload-loop with android
    // stock browser:
    // http://homematic-forum.de/forum/viewtopic.php?f=48&t=18271
    // so i deactivated it for now

    _autoReconnect: function () {
        // If connected
        if (this._isConnected) {
            if (window.localStorage) {
                window.localStorage.setItem("connCounter", 0);
            }
            // Stop connection timer
            if (this._connTimer) {
                clearInterval(this._connTimer);
                this._connTimer = null;
            }
        } else {
            // If not connected and the timer not yet started
            if (!this._connTimer) {
                // Start connection timer
                this._connTimer = _setInterval(function (conn) {
                    if (!conn._isConnected) {
                        var counter = 0;
                        if (window.localStorage) {
                            counter = parseInt(window.localStorage.getItem("connCounter") || 0);
                            window.localStorage.setItem("connCounter", counter++);
                        }
                        // Auto-Reconnect. DashUI can be located in any path.
                        var url = document.location.href;
                        var k = url.indexOf('#');
                        if (k != -1) {
                            url = url.substring(0, k);
                        }
                        if (url.indexOf('.html') == -1) {
                            url += 'index.html';
                        }
                        k = url.indexOf('?');
                        if (k != -1) {
                            url = url.substring(0, k);
                        }
                        url += '?random=' + Date.now().toString();
                        var parts = url.split('/');
                        parts = parts.slice(3);
                        url = '/' + parts.join('/');

                        // Detect if running under cordova
                        var app = document.URL.indexOf('http://') === -1 && document.URL.indexOf('https://') === -1;
                        if (app && counter > 3) {
                            url = url.replace('dashui/index.html', 'index.html');
                        }

                        $.ajax({
                            url: url,
                            cache: false,
                            success: function (data) {
                                // Check if it really index.html and not offline.html as fallback
                                if (data && data.length > 1000) {
                                    if (window.localStorage) {
                                        window.localStorage.setItem("connSettings", true);
                                    }
                                    window.location.reload();
                                }
                            }
                        });
                    } else {
                        clearInterval(conn._connTimer);
                        conn._connTimer = null;
                    }
                }, this._reconnectInterval, this);
            }
        }
    },
    getVersion: function (callback) {
        if (!this._isConnected) {
            console.log("No connection!");
            return;
        }

        if (this._queueCmdIfRequired('getVersion', callback)) {
            return;
        }

        //SignalR
        if (this._type === 0) {
            this._hub.invoke('getVersion').done(function (version) {
                if (callback) {
                    callback(version);
                }
            });
        } else if (this._type == 1) {
            //socket.io
            if (this._socket === null) {
                console.log('socket.io not initialized');
                return;
            }
            this._socket.emit('getVersion', function (version) {
                if (callback) {
                    callback(version);
                }
            });
        }
    },
    _checkAuth: function (callback) {
        if (!this._isConnected) {
            console.log("No connection!");
            return;
        }
        if (this._type === 0) {
            //SignalR
            this._hub.invoke('getVersion').done(function (version) {
                if (callback)
                    callback(version);
            });
        } else if (this._type == 1) {
            //socket.io
            if (this._socket === null) {
                console.log('socket.io not initialized');
                return;
            }
            this._socket.emit('getVersion', function (version) {
                if (callback)
                    callback(version);
            });
        }
    },
    readFile: function (filename, callback) {
        if (!this._isConnected) {
            console.log('No connection!');
            return;
        }

        if (this._queueCmdIfRequired("readFile", filename, callback)) {
            return;
        }

        if (this._type === 0) {
            //SignalR
            this._hub.invoke('readFile', filename).done(function (data) {
                if (callback) {
                    callback(data);
                }
            });
        } else if (this._type == 1) {
            //socket.io
            if (this._socket === null) {
                console.log('socket.io not initialized');
                return;
            }
            this._socket.emit('readFile', filename, function (data) {
                if (callback) {
                    callback(data);
                }
            });
        } else if (this._type == 2) {
            //local

            // Try load views from local storage
            if (filename.indexOf('dashui-views') != -1) {
                if (typeof storage !== 'undefined') {
                    vis.views = storage.get(filename);
                    if (vis.views) {
                        callback(vis.views);
                        return;
                    } else {
                        vis.views = {};
                    }
                }
            }

            // Load from ../datastore/dashui-views.json the demo views
            jQuery.ajax({
                url: '../datastore/' + filename,
                type: 'get',
                async: false,
                dataType: 'text',
                cache: true,
                success: function (data) {
                    try {
                        vis.views = jQuery.parseJSON(data);
                        if (typeof vis.views == 'string') {
                            vis.views = (JSON && JSON.parse(vis.views)) || jQuery.parseJSON(vis.views);
                        }
                    } catch (e) {
                        // TODO Translate
                        alert('Invalid ' + filename + ' json format');
                    }
                    callback(vis.views);
                    if (!vis.views) {
                        alert(_('No Views found on Server'));
                    }
                },
                error: function (state) {
                    // TODO Translate
                    alert('Cannot get ' + location.href + '/../datastore/' + filename + '\n' + state.statusText);
                    callback([]);
                }
            });
        }
    },
    touchFile: function (filename) {
        if (!this._isConnected) {
            console.log("No connection!");
            return;
        }

        if (this._type === 0) {
            //SignalR
            this._hub.invoke('touchFile', filename);
        } else if (this._type == 1) {
            //socket.io
            if (this._socket === null) {
                console.log('socket.io not initialized');
                return;
            }
            this._socket.emit('touchFile', filename);
        }
    },
    writeFile: function (filename, data, callback) {
        if (this._type === 0) {
            //SignalR
            this._hub.invoke('writeFile', filename, JSON.stringify(data)).done(function (isOk) {
                if (callback) {
                    callback(isOk);
                }
            });
        } else if (this._type == 1) {
            //socket.io
            if (this._socket === null) {
                console.log('socket.io not initialized');
                return;
            }
            this._socket.emit('writeFile', filename, data, function (isOk) {
                if (callback) {
                    callback(isOk);
                }
            });
        } else if (this._type == 2) {
            if (filename.indexOf('dashui-views') != -1) {
                if (typeof storage !== 'undefined') {
                    storage.set(filename, vis.views);
                    if (!storage.get('localWarnShown')) {
                        alert(_('All changes are saved locally. To reset changes clear the cache.'));
                        storage.set('localWarnShown', true);
                    }
                    if (callback) {
                        callback(true);
                    }
                }
            }
        }
    },
    readDir: function (dirname, callback) {
        if (this._type === 0) {
            //SignalR
            this._hub.invoke('readDir', dirname).done(function (jsonString) {
                var data;
                try {
                    data = JSON.parse(jsonString);
                } catch (e) {
                    servConn.logError('readDir: Invalid JSON string - ' + e);
                    data = null;
                }

                if (callback) {
                    callback (data);
                }
            });
        } else if (this._type == 1) {
            //socket.io
            if (this._socket === null) {
                console.log('socket.io not initialized');
                return;
            }
            this._socket.emit('readdir', dirname, function (data) {
                if (callback) {
                    callback(data);
                }
            });
        } else if (this._type == 2) {
            if (dirname.indexOf('www/dashui/img') != -1) {
                // Load from img the list of files. To make it possible, call "dir /B /O > list.txt" in every img directory.
                jQuery.ajax({
                    url: dirname.replace('www/dashui/', '') + '/list.txt',
                    type: 'get',
                    async: false,
                    dataType: 'text',
                    cache: true,
                    success: function (data) {
                        var files = (data) ? data.split('\n') : [];
                        if (callback) {
                            callback(files);
                        }
                    },
                    error: function (state) {
                        // TODO Translate
                        alert('Cannot get ' + location.href + dirname.replace('www/dashui/', '') + '/list.txt' + '\n' + state.statusText);
                        callback([]);
                    }
                });
            }
        }
    },
    setPointValue: function (pointId, value) {
        if (!this._isConnected) {
            console.log("No connection!");
            return;
        }

        if (this._queueCmdIfRequired("setPointValue", pointId, value)) {
            return;
        }

        if (this._type === 0) {
            //SignalR
            this._hub.invoke('setDataPoint', {id: pointId, val: value});
        } else if (this._type == 1) {
            //socket.io
            if (this._socket === null) {
                console.log('socket.io not initialized');
                return;
            }
            this._socket.emit('setState', [pointId, value]);
        } else if (this._type == 2) {
            //local
            console.log('This is only demo. No point will be controlled.');
        }
    },
    getDataPoints: function (callback) {
        if (!this._isConnected) {
            console.log("No connection!");
            return;
        }
        if (this._queueCmdIfRequired("getDataPoints", callback)) {
            return;
        }

        if (this._type === 0) {
            //SignalR

            this._hub.invoke('getDataPoints').done(function (jsonString) {
                var data = {};
                if (jsonString === null) {
                    if (callback) {
                        callback('Authentication required');
                    }
                } else if (jsonString !== undefined) {
                    try {
                        data = (JSON && JSON.parse(jsonString)) || jQuery.parseJSON(jsonString);
                    } catch (e) {
                        servConn.logError('getDataPoints: Invalid JSON string - ' + e);
                        data = null;
                        if (callback) {
                            callback('getDataPoints: Invalid JSON string - ' + e);
                        }
                    }
                }
                // Convert array to mapped object {name1: object1, name2: object2}
                for (var i = 0, len = data.length; i < len; i++) {
                    if (data[i]) {
                        var obj  = data[i];
                        var dp   = obj.id;
                        var o;

                        data[dp] = obj;
                        if (localData.uiState['_' + dp + '.Value'] === undefined) {
                            o = {};
                            o['_' + dp] = {Value: data[dp].val, Timestamp: data[dp].ts, Certain: data[dp].ack, LastChange: data[dp].lc};
                            localData.uiState.attr(o);
                        } else {
                            o = {};
                            var id = ' ' + dp;//.replace(/\./g, '\\.');
                            o[id + '.Value']      = obj.val;
                            o[id + '.Timestamp']  = obj.ts;
                            o[id + '.Certain']    = obj.ack;
                            o[id + '.LastChange'] = obj.lc;
                        }
                    }
                }

                if (callback) {
                    callback();
                }
            });
        } else if (this._type == 1) {
            //socket.io
            if (this._socket === null) {
                console.log('socket.io not initialized');
                return;
            }
            this._socket.emit('getDatapoints', function (data) {
                if (data === null) {
                    if (callback) {
                        callback('Authentication required');
                    }
                } else if (data !== undefined) {
                    for (var dp in data) {
                        var obj = data[dp];
                        var o = {};
                        var id = dp;//.replace(/\./g, '\\.');
                        if (localData.uiState['_' + dp/*.replace(/\./g, '\\.')*/ + '.Value'] === undefined) {
                            try {
                                o['_' + dp + '.Value'] = obj[0];
                                o['_' + dp + '.Timestamp'] = obj[1];
                                localData.uiState.attr(o);
                            } catch (e) {
                                servConn.logError('Error: can\'t create uiState object for ' + dp + '(' + e + ')');
                            }
                        } else {
                            o['_' + id + '.Value']      = obj[0];
                            o['_' + id + '.Timestamp']  = obj[1];
                            o['_' + id + '.Certain']    = obj[2];
                            o['_' + id + '.LastChange'] = obj[3];
                            console.log(o);
                            localData.uiState.attr(o);
                        }
                    }
                }
                if (callback) {
                    callback();
                }
            });
        } else if (this._type == 2) {
            // local
            // Load from ../datastore/local-data.json the demo views
            jQuery.ajax({
                url: '../datastore/local-data' + vis.viewFileSuffix + '.json',
                type: 'get',
                async: false,
                dataType: 'text',
                cache: vis.useCache,
                success: function (data) {
                    var _localData = (JSON && JSON.parse(data)) || jQuery.parseJSON(data);
                    localData.metaIndex   = _localData.metaIndex;
                    localData.metaObjects = _localData.metaObjects;
                    for (var dp in _localData.uiState) {
                        try {
                            // TODO possible problem with legacy
                            console.log(dp);
                            localData.uiState.attr(dp, _localData.uiState[dp]);
                        } catch (e) {
                            servConn.logError('Cannot export ' + dp);
                        }
                    }
                    callback(null);
                },
                error: function (state) {
                    console.log(state.statusText);
                    localData.uiState.attr('_no', {Value: false, Timestamp: null, Certain: true, LastChange: null});
                    // Local
                    if(callback) {
                        callback(null);
                    }
                }
            });

        }
    },
    getDataObjects: function (callback) {
        if (!this._isConnected) {
            console.log('No connection!');
            return;
        }

        if (this._queueCmdIfRequired("getDataObjects", callback)) {
            return;
        }

        if (this._type === 0) {
            //SignalR
            this._hub.invoke('getDataObjects').done(function (jsonString) {
                var data = {};
                try {
                    data = JSON.parse(jsonString);
                    // Convert array to mapped object {name1: object1, name2: object2}
                    for (var i = 0, len = data.length; i < len; i++) {
                        if (data[i]) {
                            data[data[i].id] = data[i];
                            delete data[data[i].id].id;
                        }
                    }
                } catch (e) {
                    servConn.logError('getDataObjects: Invalid JSON string - ' + e);
                    data = null;
                }

                if (callback) {
                    callback(data);
                }
            });
        } else if (this._type == 1) {
            //socket.io
            if (this._socket === null) {
                console.log('socket.io not initialized');
                return;
            }
            this._socket.emit('getObjects', function (data) {
                if (callback) {
                    callback(data);
                }
            });
        } else if (this._type == 2) {
            if (callback) {
                callback(localData.metaObjects);
            }
        }
    },
    getDataIndex: function (callback) {
        if (!this._isConnected) {
            console.log('No connection!');
            return;
        }
        if (this._type === 0) {
            //SignalR
            if (callback) {
                callback([]);
            }
        } else if (this._type == 1) {
            //socket.io
            if (this._socket === null) {
                console.log('socket.io not initialized');
                return;
            }
            this._socket.emit('getIndex', function (data) {
                if (callback)
                    callback(data);
            });
        } else if (this._type == 2) {
            if (callback) {
                callback(localData.metaIndex);
            }
        }
    },
    addObject: function (objId, obj, callback) {
        if (!this._isConnected) {
            console.log("No connection!");
            return;
        }
        if (this._type === 0) {
            //SignalR
            this._hub.invoke('addObject', objId, obj).done(function (cid) {
                if (callback) {
                    callback(cid);
                }
            });
        } else if (this._type == 1) {
            //socket.io
            if (this._socket === null) {
                console.log('socket.io not initialized');
                return;
            }
            this._socket.emit('setObject', objId, obj, function (cid) {
                if (callback) {
                    callback(cid);
                }
            });
        }
    },
    delObject: function (objId) {
        if (!this._isConnected) {
            console.log("No connection!");
            return;
        }
        if (this._type === 0) {
            //SignalR
            this._hub.invoke('deleteObject', objId);
        } else if (this._type == 1) {
            //socket.io
            if (this._socket === null) {
                console.log('socket.io not initialized');
                return;
            }
            this._socket.emit('delObject', objId);
        }
    }, // Deprecated
    // TODO @Bluefox: Why deprecated? Ursprüngliches Konzept war dass der Value eines Homematic-Programms true/false ist und angibt
    // ob ein Programm aktiv/inaktiv ist -> HM-Script Methode .Active()
    // Eigentlich wirft der Umbau den Du da vor geraumer Zeit in CCU.IO vorgenommen hast (damit über den Value ein Programm
    // angetriggert werden kann) dieses Konzept über den Haufen. Wirkt sich bei mir persönlich an der Stelle nicht aus
    // da ich keine Homematic-Programme verwende, aber ...
    // ... LessonsLearned: Wir müssen häufiger und mehr kommunizieren bevor wir solche Änderungen vornehmen :)
    execProgramm: function (objId) {
        if (!this._isConnected) {
            console.log("No connection!");
            return;
        }
        if (this._type == 1) {
            //socket.io
            if (this._socket === null) {
                console.log('socket.io not initialized');
                return;
            }
            this._socket.emit('programExecute', [objId]);
        }
    },
    httpGet: function (url, callback) {
        if (!this._isConnected) {
            console.log("No connection!");
            return;
        }
        if (this._type === 0) {
            //SignalR
            this._hub.invoke('httpGet', url).done(function (jsonString) {
                if (callback) {
                    callback(jsonString);
                }
            });
        } else if (this._type == 1) {
            //socket.io
            if (this._socket === null) {
                console.log('socket.io not initialized');
                return;
            }
            this._socket.emit('httpGet', url, function (data) {
                if (callback) {
                    callback(data);
                }
            });
        } else if (this._type == 2) {
            if (callback) {
                callback('');
            }
        }
    },
    getStringtable: function (callback) {
        if (!this._isConnected) {
            console.log("No connection!");
            return;
        }
        if (this._type === 0) {
            //SignalR
            //this._hub.invoke('getUrl(url).done(function (jsonString) {
            if (callback) {
                callback(null);
            }
            //});
        } else if (this._type == 1) {
            //socket.io
            if (this._socket === null) {
                console.log('socket.io not initialized');
                return;
            }
            this._socket.emit('getStringtable', function (data) {
                if (callback) {
                    callback(data);
                }
            });
        } else if (this._type == 2) {
            if (callback) {
                callback(null);
            }
        }
    },
    alarmReceipt: function (alarm) {
        if (!this._isConnected) {
            console.log("No connection!");
            return;
        }
        //if (this._type === 0) {
            //SignalR
            //this._hub.invoke('getUrl(url).done(function (jsonString) {
            //});
        //} else
        if (this._type == 1) {
            //socket.io
            if (this._socket === null) {
                console.log('socket.io not initialized');
                return;
            }
            this._socket.emit('alarmReceipt', alarm);
        } else if (this._type == 2) {
            if (callback) {
                callback(null);
            }
        }
    },
    logError: function (errorText) {
        console.log("Error: " + errorText);
        if (!this._isConnected) {
            //console.log("No connection!");
            return;
        }
        //if (this._type === 0) {
            //SignalR
            //this._hub.server.log(errorText);
        //} else
        if (this._type == 1) {
            //socket.io
            if (this._socket === null) {
                console.log('socket.io not initialized');
                return;
            }
            this._socket.emit('log', 'error', 'Addon DashUI  ' + errorText);
        } else if (this._type == 2) {
            // Do nothing
        }
    },
    _queueCmdIfRequired: function (func, arg1, arg2, arg3, arg4) {
        if (!this._isAuthDone) {
            // Queue command
            this._cmdQueue.push({func: func, args:[arg1, arg2, arg3, arg4]});

            if (!this._authRunning) {
                this._authRunning = true;
                var that = this;
                // Try to read version
                this._checkAuth(function (version) {
                    // If we have got version string, so there is no authentication, or we are authenticated
                    that._authRunning = false;
                    if (version) {
                        that._isAuthDone  = true;
                        // Repeat all stored requests
                        var __cmdQueue = that._cmdQueue;
                        // Trigger GC
                        that._cmdQueue = null;
                        that._cmdQueue = [];
                        for (var t = 0, len = __cmdQueue.length; t < len; t++) {
                            that[__cmdQueue[t].func](__cmdQueue[t].args[0], __cmdQueue[t].args[1], __cmdQueue[t].args[2], __cmdQueue[t].args[3]);
                        }
                    } else {
                        // Auth required
                        that._isAuthRequired = true;
                        // What for AuthRequest from server
                    }
                });
            }

            return true;
        } else {
            return false;
        }
    },
    authenticate: function (user, password, salt) {
        this._authRunning = true;

        if (user !== undefined) {
            this._authInfo = {
                user: user,
                hash: password + salt,
                salt: salt
            };
        }

        if (!this._isConnected) {
            console.log("No connection!");
            return;
        }

        if (!this._authInfo) {
            console.log("No credentials!");
        }

        //SignalR
        if (this._type === 0) {
            var that = this;
            this._hub.invoke('authenticate', that._authInfo.user, that._authInfo.hash, that._authInfo.salt).done(function (error) {
                this._authRunning = false;
                if (!error) {
                    that._isAuthDone  = true;
                    if (typeof session !== 'undefined') {
                        session.set("user", that._authInfo.user);
                        session.set("hash", that._authInfo.hash);
                        session.set("salt", that._authInfo.salt);
                    }

                    // Repeat all stored requests
                    var __cmdQueue = that._cmdQueue;
                    // Trigger garbage collector
                    that._cmdQueue = null;
                    that._cmdQueue = [];
                    for (var t = 0, len = __cmdQueue.length; t < len; t++) {
                        that[__cmdQueue[t].func](__cmdQueue[t].args[0], __cmdQueue[t].args[1], __cmdQueue[t].args[2], __cmdQueue[t].args[3]);
                    }
                } else {
                    // Another authRequest should come, wait for this
                    console.log("Cannot authenticate: " + error);
                    this._authInfo = null;
                }
            });
        }
    }
};
