"use strict";
var __extends = (this && this.__extends) || (function () {
    var extendStatics = function (d, b) {
        extendStatics = Object.setPrototypeOf ||
            ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
            function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
        return extendStatics(d, b);
    };
    return function (d, b) {
        extendStatics(d, b);
        function __() { this.constructor = d; }
        d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
    };
})();
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
var __importStar = (this && this.__importStar) || function (mod) {
    if (mod && mod.__esModule) return mod;
    var result = {};
    if (mod != null) for (var k in mod) if (Object.hasOwnProperty.call(mod, k)) result[k] = mod[k];
    result["default"] = mod;
    return result;
};
Object.defineProperty(exports, "__esModule", { value: true });
var Emitter_1 = __importDefault(require("../Emitter"));
var bluebird_1 = __importDefault(require("bluebird"));
var io = __importStar(require("socket.io-client"));
// Main/public page service
var api, auth, storage, config;
var Providers = /** @class */ (function () {
    function Providers(ippApi, ippAuth, ippStorage, ippConf) {
        api = ippApi;
        auth = ippAuth;
        storage = ippStorage;
        config = ippConf;
    }
    return Providers;
}());
exports.Providers = Providers;
// Page rest/polling service
var ProviderREST = /** @class */ (function (_super) {
    __extends(ProviderREST, _super);
    function ProviderREST(_pageId, _folderId, _uuid) {
        var _this = _super.call(this) || this;
        _this._pageId = _pageId;
        _this._folderId = _folderId;
        _this._uuid = _uuid;
        _this._stopped = false;
        _this._requestOngoing = false;
        _this._timeout = 1000;
        _this._seqNo = 0;
        _this._error = false;
        _this.start();
        return _this;
    }
    Object.defineProperty(ProviderREST.prototype, "seqNo", {
        set: function (seqNo) {
            this._seqNo = seqNo;
        },
        enumerable: true,
        configurable: true
    });
    Object.defineProperty(ProviderREST.prototype, "requestOngoing", {
        set: function (value) {
            this._requestOngoing = value;
        },
        enumerable: true,
        configurable: true
    });
    /**
     * Start polling for page updates
     */
    ProviderREST.prototype.start = function () {
        this._stopped = false;
        this.startPolling();
    };
    /**
     * Stop polling for page updates
     */
    ProviderREST.prototype.stop = function () {
        this._stopped = true;
        clearTimeout(this._timer);
    };
    /**
     * Stop polling for page updates and stop all events
     */
    ProviderREST.prototype.destroy = function () {
        this.stop();
        this.removeEvent();
    };
    ProviderREST.prototype.getData = function () {
        return this._data;
    };
    /**
     * Start the actual polling (loop)
     */
    // @todo Not great
    ProviderREST.prototype.startPolling = function () {
        var _this = this;
        this.load().catch(function (err) {
            console.warn(_this._requestOngoing ? 'Request in progress' : _this._stopped ? 'Polling has stopped' : err);
        });
        this._timer = setTimeout(function () {
            _this.startPolling();
        }, this._timeout);
    };
    /**
     * Load page data from service
     * @param ignoreSeqNo
     * @returns {Promise<IPage>}
     */
    ProviderREST.prototype.load = function (ignoreSeqNo) {
        var _this = this;
        if (ignoreSeqNo === void 0) { ignoreSeqNo = false; }
        var p = new bluebird_1.default(function (resolve, reject) {
            if (_this._requestOngoing || _this._stopped) {
                // @todo Should we emit something?
                reject(new Error('Error'));
                return;
            }
            _this._requestOngoing = true;
            var success = function (res) {
                // console.log("api.getPage", res);
                if (res.httpCode === 200 || res.httpCode === 204) {
                    // New update
                    if (res.httpCode === 200) {
                        _this._seqNo = res.data.seq_no;
                        _this._timeout = res.data.pull_interval * 1000;
                        _this._data = res.data;
                        _this.emit('meta_update', _this.tempGetSettingsOb(res.data));
                        _this.emit('content_update', _this.tempGetContentOb(res.data));
                    }
                    else {
                        // @todo do we need this?
                        _this.emit('empty_update');
                    }
                    _this._error = false;
                    resolve(res.data);
                }
                else {
                    _this.onError(res.data);
                    reject(new Error('Unable to load page'));
                }
            };
            if (_this._uuid) {
                api
                    .getPageByUuid({
                    uuid: _this._uuid,
                    seq_no: !ignoreSeqNo ? _this._seqNo : undefined
                })
                    .then(success, function (err) {
                    _this.onError(err);
                    reject(new Error(err));
                })
                    .finally(function () {
                    _this._requestOngoing = false;
                });
            }
            else {
                api
                    .getPage({
                    domainId: _this._folderId,
                    pageId: _this._pageId,
                    seq_no: !ignoreSeqNo ? _this._seqNo : undefined
                })
                    .then(success, function (err) {
                    _this.onError(err);
                    reject(new Error(err));
                })
                    .finally(function () {
                    _this._requestOngoing = false;
                });
            }
        });
        return p;
    };
    ProviderREST.prototype.onError = function (err) {
        this._error = true;
        this.emit('error', err);
    };
    /**
     * Temporary solution to get the required subset of data from full page object
     *
     * @param data
     * @returns {{id: number, seq_no: number, content_modified_timestamp: Date, content: any, content_modified_by: any, push_interval: number, pull_interval: number, is_public: boolean, description: string, encrypted_content: string, encryption_key_used: number, encryption_type_used: number, special_page_type: number}}
     */
    ProviderREST.prototype.tempGetContentOb = function (data) {
        return {
            id: data.id,
            name: data.name,
            uuid: data.uuid,
            domain_name: data.domain_name,
            background_color: data.background_color,
            domain_id: data.domain_id,
            seq_no: data.seq_no,
            content_modified_timestamp: data.content_modified_timestamp,
            content: data.content,
            content_modified_by: data.content_modified_by,
            push_interval: data.push_interval,
            pull_interval: data.pull_interval,
            is_public: data.is_public,
            description: data.description,
            encrypted_content: data.encrypted_content,
            encryption_key_used: data.encryption_key_used,
            encryption_type_used: data.encryption_type_used,
            record_history: data.record_history,
            special_page_type: data.special_page_type,
            symphony_sid: data.symphony_sid,
            show_gridlines: data.show_gridlines
        };
    };
    /**
     * Temporary solution to get the required subset of data from full page object
     *
     * @param data
     * @returns {any}
     */
    ProviderREST.prototype.tempGetSettingsOb = function (data) {
        return JSON.parse(JSON.stringify(data));
    };
    return ProviderREST;
}(Emitter_1.default));
exports.ProviderREST = ProviderREST;
// Page sockets service
var ProviderSocket = /** @class */ (function (_super) {
    __extends(ProviderSocket, _super);
    function ProviderSocket(_pageId, _folderId) {
        var _this = _super.call(this) || this;
        _this._pageId = _pageId;
        _this._folderId = _folderId;
        _this._stopped = false;
        _this._redirectCounter = 0;
        _this._redirectLimit = 10;
        _this._error = false;
        /**
         * onConnect event action
         */
        _this.onConnect = function () {
            return;
        };
        _this.onReconnectingError = function (attempt) {
            _this.emit('error', { message: 'Streaming is currently not available', code: 502, type: 'redirect' });
            _this.destroy(true);
            return;
        };
        /**
         * onReconnectError event action
         * Try and brake the previous redirect. Go back to root
         */
        _this.onReconnectError = function () {
            // Destroy current socket
            _this.destroy(false);
            _this._wsUrl = config.ws_url + '/page/' + _this._pageId;
            // Init new socket
            _this.start();
        };
        /**
         * onDisconnect event action
         */
        _this.onDisconnect = function () {
            return;
        };
        /**
         * onRedirect event action
         * @param msg
         */
        _this.onRedirect = function (msg) {
            // Destroy current socket
            _this._wsUrl = msg;
            _this._redirectCounter++;
            if (_this._redirectCounter >= _this._redirectLimit) {
                console.log('socket', _this._redirectCounter);
                _this.emit('error', { message: 'Streaming connection limit reached', code: 500, type: 'redirect' });
                _this.destroy(true);
                _this._redirectCounter = 0;
                return;
            }
            else {
                _this.destroy(false);
            }
            // Init new socket
            _this.start();
        };
        /**
         * onPageContent eent action
         * @param data
         */
        _this.onPageContent = function (data) {
            _this._data = data;
            if (!_this._stopped) {
                _this.emit('content_update', data);
            }
        };
        /**
         * onPageSettings event action
         * @param data
         */
        _this.onPageSettings = function (data) {
            _this.emit('meta_update', data);
        };
        /**
         * onPageError event action
         * @param data
         */
        _this.onPageError = function (data) {
            _this._error = true;
            if (data.code === 401) {
                // @todo This is wrong
                auth.emit(auth.EVENT_401);
            }
            else {
                _this.emit('error', data);
            }
        };
        /**
         * onOAuthError event action
         * @param data
         */
        _this.onOAuthError = function (data) {
            // @todo Do something
            // @todo should we watch auth service for re-logged and re-connect?
            // @todo Emit page error ?
        };
        /**
         * onAuthRefresh event action
         */
        _this.onAuthRefresh = function () {
            // let dummy: number = this._pageId; // This is here just to make the callback different, otherwise event emitter prevents duplicates
            _this.start();
        };
        /**
         * Determines if client supports websockets
         * @returns {boolean}
         */
        _this.supportsWebSockets = function () {
            return 'WebSocket' in window || 'MozWebSocket' in window;
        };
        _this._wsUrl = config.ws_url + '/page/' + _this._pageId;
        _this.start();
        return _this;
    }
    Object.defineProperty(ProviderSocket, "SOCKET_EVENT_PAGE_ERROR", {
        get: function () {
            return 'page_error';
        },
        enumerable: true,
        configurable: true
    });
    Object.defineProperty(ProviderSocket, "SOCKET_EVENT_PAGE_CONTENT", {
        get: function () {
            return 'page_content';
        },
        enumerable: true,
        configurable: true
    });
    Object.defineProperty(ProviderSocket, "SOCKET_EVENT_PAGE_PUSH", {
        get: function () {
            return 'page_push';
        },
        enumerable: true,
        configurable: true
    });
    Object.defineProperty(ProviderSocket, "SOCKET_EVENT_PAGE_SETTINGS", {
        get: function () {
            return 'page_settings';
        },
        enumerable: true,
        configurable: true
    });
    Object.defineProperty(ProviderSocket, "SOCKET_EVENT_PAGE_DATA", {
        get: function () {
            return 'page_data';
        },
        enumerable: true,
        configurable: true
    });
    Object.defineProperty(ProviderSocket, "SOCKET_EVENT_PAGE_USER_JOINED", {
        get: function () {
            return 'page_user_joined';
        },
        enumerable: true,
        configurable: true
    });
    Object.defineProperty(ProviderSocket, "SOCKET_EVENT_PAGE_USER_LEFT", {
        get: function () {
            return 'page_user_left';
        },
        enumerable: true,
        configurable: true
    });
    /**
     * Start listening for content updates. If socket not connected yet, it will initialize the socket connection
     */
    ProviderSocket.prototype.start = function () {
        this._stopped = false;
        if (!this._socket || !this._socket.connected) {
            auth.on(auth.EVENT_LOGIN_REFRESHED, this.onAuthRefresh);
            // Because socket is disconnected on lost access, we need to reconnect it
            this.init();
        }
        else {
            if (this._data) {
                // this.onPageContent(this._data);
            }
            // this._socket.on(ProviderSocket.SOCKET_EVENT_PAGE_CONTENT, this.onPageContent);
        }
    };
    /**
     * Stop receiving page content updates (still keeps receiving settings/meta updates and other events)
     * This will NOT disconnect the socket, merely it will just stop listening for updates - throws them away
     */
    ProviderSocket.prototype.stop = function () {
        // this._socket.disconnect();
        // this._socket.off(ProviderSocket.SOCKET_EVENT_PAGE_CONTENT, this.onPageContent);
        this._stopped = true;
    };
    /**
     * Remove listeners for all socket events, disconnects socket and destroys object
     * @param hard
     */
    ProviderSocket.prototype.destroy = function (hard) {
        if (hard === void 0) { hard = true; }
        this._socket.removeAllListeners();
        this._socket.disconnect();
        this.stop();
        auth.off(auth.EVENT_LOGIN_REFRESHED, this.onAuthRefresh);
        if (hard) {
            this.removeEvent();
        }
    };
    ProviderSocket.prototype.getData = function () {
        return this._data;
    };
    /**
     * Initialize socket connection with all listeners
     */
    ProviderSocket.prototype.init = function () {
        // Connect to socket
        this._socket = this.connect();
        // Register listeners
        this._socket.on('connect', this.onConnect);
        this._socket.on('reconnecting', this.onReconnectingError);
        this._socket.on('reconnect_error', this.onReconnectError);
        this._socket.on('redirect', this.onRedirect);
        this._socket.on(ProviderSocket.SOCKET_EVENT_PAGE_CONTENT, this.onPageContent);
        this._socket.on(ProviderSocket.SOCKET_EVENT_PAGE_SETTINGS, this.onPageSettings);
        this._socket.on(ProviderSocket.SOCKET_EVENT_PAGE_ERROR, this.onPageError);
        this._socket.on('oauth_error', this.onOAuthError);
        /*this._socket.on(ProviderSocket.SOCKET_EVENT_PAGE_DATA, this.onPageData);
             this._socket.on(ProviderSocket.SOCKET_EVENT_PAGE_USER_JOINED, this.onPageUserJoined);
             this._socket.on(ProviderSocket.SOCKET_EVENT_PAGE_USER_LEFT, this.onPageUserLeft);*/
        this._socket.on('disconnect', this.onDisconnect);
        this._stopped = false;
    };
    /**
     * Connect socket to a server. If client doesnt support websockets, it tries to fall back to long polling
     * @returns {SocketIOClient.Socket}
     */
    ProviderSocket.prototype.connect = function () {
        var token = api.tokens && api.tokens.access_token ? api.tokens.access_token : storage ? storage.persistent.get('access_token') : '';
        var query = ["access_token=" + token];
        query = query.filter(function (val) {
            return val.length > 0;
        });
        return io.connect(this._wsUrl, {
            query: query.join('&'),
            transports: this.supportsWebSockets() ? ['websocket'] : ['polling'],
            forceNew: true,
            reconnectionAttempts: 5
        });
    };
    return ProviderSocket;
}(Emitter_1.default));
exports.ProviderSocket = ProviderSocket;
//# sourceMappingURL=Provider.js.map