"use strict";
var __extends = (this && this.__extends) || function (d, b) {
    for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p];
    function __() { this.constructor = d; }
    d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
};
var Subject_1 = require('../../Subject');
var Subscriber_1 = require('../../Subscriber');
var Observable_1 = require('../../Observable');
var Subscription_1 = require('../../Subscription');
var root_1 = require('../../util/root');
var ReplaySubject_1 = require('../../ReplaySubject');
var tryCatch_1 = require('../../util/tryCatch');
var errorObject_1 = require('../../util/errorObject');
var assign_1 = require('../../util/assign');
/**
 * We need this JSDoc comment for affecting ESDoc.
 * @extends {Ignored}
 * @hide true
 */
var WebSocketSubject = (function (_super) {
    __extends(WebSocketSubject, _super);
    function WebSocketSubject(urlConfigOrSource, destination) {
        if (urlConfigOrSource instanceof Observable_1.Observable) {
            _super.call(this, destination, urlConfigOrSource);
        }
        else {
            _super.call(this);
            this.WebSocketCtor = root_1.root.WebSocket;
            if (typeof urlConfigOrSource === 'string') {
                this.url = urlConfigOrSource;
            }
            else {
                // WARNING: config object could override important members here.
                assign_1.assign(this, urlConfigOrSource);
            }
            if (!this.WebSocketCtor) {
                throw new Error('no WebSocket constructor can be found');
            }
            this.destination = new ReplaySubject_1.ReplaySubject();
        }
    }
    WebSocketSubject.prototype.resultSelector = function (e) {
        return JSON.parse(e.data);
    };
    /**
     * @param urlConfigOrSource
     * @return {WebSocketSubject}
     * @static true
     * @name webSocket
     * @owner Observable
     */
    WebSocketSubject.create = function (urlConfigOrSource) {
        return new WebSocketSubject(urlConfigOrSource);
    };
    WebSocketSubject.prototype.lift = function (operator) {
        var sock = new WebSocketSubject(this, this.destination);
        sock.operator = operator;
        return sock;
    };
    // TODO: factor this out to be a proper Operator/Subscriber implementation and eliminate closures
    WebSocketSubject.prototype.multiplex = function (subMsg, unsubMsg, messageFilter) {
        var self = this;
        return new Observable_1.Observable(function (observer) {
            var result = tryCatch_1.tryCatch(subMsg)();
            if (result === errorObject_1.errorObject) {
                observer.error(errorObject_1.errorObject.e);
            }
            else {
                self.next(result);
            }
            var subscription = self.subscribe(function (x) {
                var result = tryCatch_1.tryCatch(messageFilter)(x);
                if (result === errorObject_1.errorObject) {
                    observer.error(errorObject_1.errorObject.e);
                }
                else if (result) {
                    observer.next(x);
                }
            }, function (err) { return observer.error(err); }, function () { return observer.complete(); });
            return function () {
                var result = tryCatch_1.tryCatch(unsubMsg)();
                if (result === errorObject_1.errorObject) {
                    observer.error(errorObject_1.errorObject.e);
                }
                else {
                    self.next(result);
                }
                subscription.unsubscribe();
            };
        });
    };
    WebSocketSubject.prototype._unsubscribe = function () {
        this.socket = null;
        this.source = null;
        this.destination = new ReplaySubject_1.ReplaySubject();
        this.isStopped = false;
        this.hasErrored = false;
        this.hasCompleted = false;
        this.observers = null;
        this.isUnsubscribed = false;
    };
    WebSocketSubject.prototype._subscribe = function (subscriber) {
        var _this = this;
        if (!this.observers) {
            this.observers = [];
        }
        var subscription = _super.prototype._subscribe.call(this, subscriber);
        // HACK: For some reason transpilation wasn't honoring this in arrow functions below
        // Doesn't seem right, need to reinvestigate.
        var self = this;
        var WebSocket = this.WebSocketCtor;
        if (self.source || !subscription || subscription.isUnsubscribed) {
            return subscription;
        }
        if (self.url && !self.socket) {
            var socket_1 = self.protocol ? new WebSocket(self.url, self.protocol) : new WebSocket(self.url);
            self.socket = socket_1;
            socket_1.onopen = function (e) {
                var openObserver = self.openObserver;
                if (openObserver) {
                    openObserver.next(e);
                }
                var queue = self.destination;
                self.destination = Subscriber_1.Subscriber.create(function (x) { return socket_1.readyState === 1 && socket_1.send(x); }, function (e) {
                    var closingObserver = self.closingObserver;
                    if (closingObserver) {
                        closingObserver.next(undefined);
                    }
                    if (e && e.code) {
                        socket_1.close(e.code, e.reason);
                    }
                    else {
                        self._finalError(new TypeError('WebSocketSubject.error must be called with an object with an error code, ' +
                            'and an optional reason: { code: number, reason: string }'));
                    }
                }, function () {
                    var closingObserver = self.closingObserver;
                    if (closingObserver) {
                        closingObserver.next(undefined);
                    }
                    socket_1.close();
                });
                if (queue && queue instanceof ReplaySubject_1.ReplaySubject) {
                    subscription.add(queue.subscribe(self.destination));
                }
            };
            socket_1.onerror = function (e) { return self.error(e); };
            socket_1.onclose = function (e) {
                var closeObserver = self.closeObserver;
                if (closeObserver) {
                    closeObserver.next(e);
                }
                if (e.wasClean) {
                    self._finalComplete();
                }
                else {
                    self._finalError(e);
                }
            };
            socket_1.onmessage = function (e) {
                var result = tryCatch_1.tryCatch(self.resultSelector)(e);
                if (result === errorObject_1.errorObject) {
                    self._finalError(errorObject_1.errorObject.e);
                }
                else {
                    self._finalNext(result);
                }
            };
        }
        return new Subscription_1.Subscription(function () {
            subscription.unsubscribe();
            if (!_this.observers || _this.observers.length === 0) {
                var socket = _this.socket;
                if (socket && socket.readyState < 2) {
                    socket.close();
                }
                _this.socket = undefined;
                _this.source = undefined;
                _this.destination = new ReplaySubject_1.ReplaySubject();
            }
        });
    };
    return WebSocketSubject;
}(Subject_1.Subject));
exports.WebSocketSubject = WebSocketSubject;
//# sourceMappingURL=WebSocketSubject.js.map