import * as i0 from '@angular/core';
import { Injectable, InjectionToken, NgModule } from '@angular/core';
import { tryParseJson, isFalsy } from '@angular-ru/cdk/utils';
import { Subject, ReplaySubject, throwError } from 'rxjs';
import { filter, map, catchError, takeUntil, take } from 'rxjs/operators';
import { webSocket } from 'rxjs/webSocket';

class WebsocketConfig {
    constructor() {
        this.reconnectionDelay = 2000;
        this.binaryType = 'arraybuffer';
    }
}
/** @nocollapse */ /** @nocollapse */ WebsocketConfig.ɵfac = function WebsocketConfig_Factory(t) { return new (t || WebsocketConfig)(); };
/** @nocollapse */ /** @nocollapse */ WebsocketConfig.ɵprov = /** @pureOrBreakMyCode */ i0.ɵɵdefineInjectable({ token: WebsocketConfig, factory: WebsocketConfig.ɵfac });
(function () { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(WebsocketConfig, [{
        type: Injectable
    }], null, null); })();

const PLAIN_TEXT = {};
const BINARY = {};
class AbstractWebsocketClient {
    constructor(wsConfig, ngZone) {
        this.wsConfig = wsConfig;
        this.ngZone = ngZone;
        this.connected = false;
        this.messages$ = new Subject();
        this.connected$ = new ReplaySubject(Number.POSITIVE_INFINITY);
        this.disconnected$ = new ReplaySubject(Number.POSITIVE_INFINITY);
        this.destroy$ = new Subject();
    }
    get isConnected() {
        return this.connected;
    }
    get isDisconnected() {
        return !this.connected;
    }
    get connectionPath() {
        return `${this.baseUrl}${this.handlerPath}`;
    }
    get webSocketSubjectConfig() {
        return {
            openObserver: { next: (event) => this.onOpenObserver(event) },
            closeObserver: { next: (event) => this.onCloseObserver(event) },
            serializer: (message) => this.serialize(message),
            deserializer: (event) => this.deserialize(event),
            url: this.connectionPath,
            binaryType: this.wsConfig.binaryType
        };
    }
    static isArrayBuffer(value) {
        return value instanceof ArrayBuffer || value instanceof Blob;
    }
    onConnected(_event) {
        // noop
    }
    onDisconnected(_event) {
        // noop
    }
    connect(path) {
        this.handlerPath = path;
        this.ngZone.runOutsideAngular(() => this._connect());
        this.destroy$ = new Subject();
    }
    disconnect() {
        var _a, _b;
        (_a = this.socketSubscription) === null || _a === void 0 ? void 0 : _a.unsubscribe();
        (_b = this.socket$) === null || _b === void 0 ? void 0 : _b.complete();
        this.destroy$.next(true);
        this.destroy$.complete();
    }
    on(type) {
        return this.messages$.pipe(filter((message) => message.type === type), map((message) => message.data), catchError((err) => throwError(() => err)), takeUntil(this.destroy$));
    }
    single(type) {
        return this.on(type).pipe(take(1));
    }
    sendMessage(type, data) {
        var _a;
        (_a = this.socket$) === null || _a === void 0 ? void 0 : _a.next({ type, data });
    }
    ngOnDestroy() {
        this.disconnect();
    }
    serialize(message) {
        switch (message.type) {
            case BINARY:
            case PLAIN_TEXT:
                return message.data;
            default:
                return JSON.stringify(message);
        }
    }
    deserialize({ data }) {
        var _a;
        if (AbstractWebsocketClient.isArrayBuffer(data)) {
            return { type: BINARY, data };
        }
        else {
            return (_a = tryParseJson(data)) !== null && _a !== void 0 ? _a : { type: PLAIN_TEXT, data };
        }
    }
    onOpenObserver(event) {
        this.connected = true;
        this.connected$.next(event);
        this.onConnected(event);
    }
    onCloseObserver(event) {
        this.connected = false;
        this.disconnected$.next(event);
        this.onDisconnected(event);
    }
    _connect() {
        this.socket$ = webSocket(this.webSocketSubjectConfig);
        this.socketSubscription = this.socket$.pipe(takeUntil(this.destroy$)).subscribe({
            next: (message) => {
                window.requestAnimationFrame(() => this.messages$.next(message));
            },
            error: () => this.reconnect()
        });
    }
    reconnect() {
        this.completeStoppedSocket();
        this.socketUnsubscribe();
        this.ngZone.runOutsideAngular(() => {
            // eslint-disable-next-line no-restricted-properties
            window.setTimeout(() => this._connect(), this.wsConfig.reconnectionDelay);
        });
    }
    completeStoppedSocket() {
        var _a, _b;
        // TODO: need replace isStopped
        // eslint-disable-next-line deprecation/deprecation
        if (isFalsy((_a = this.socket$) === null || _a === void 0 ? void 0 : _a.isStopped)) {
            (_b = this.socket$) === null || _b === void 0 ? void 0 : _b.complete();
        }
    }
    socketUnsubscribe() {
        var _a, _b;
        if (isFalsy((_a = this.socketSubscription) === null || _a === void 0 ? void 0 : _a.closed)) {
            (_b = this.socketSubscription) === null || _b === void 0 ? void 0 : _b.unsubscribe();
        }
    }
}
/** @nocollapse */ /** @nocollapse */ AbstractWebsocketClient.ɵfac = function AbstractWebsocketClient_Factory(t) { return new (t || AbstractWebsocketClient)(i0.ɵɵinject(WebsocketConfig), i0.ɵɵinject(i0.NgZone)); };
/** @nocollapse */ /** @nocollapse */ AbstractWebsocketClient.ɵprov = /** @pureOrBreakMyCode */ i0.ɵɵdefineInjectable({ token: AbstractWebsocketClient, factory: AbstractWebsocketClient.ɵfac });
(function () { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(AbstractWebsocketClient, [{
        type: Injectable
    }], function () { return [{ type: WebsocketConfig }, { type: i0.NgZone }]; }, null); })();

const WEBSOCKET_CONFIGS = new InjectionToken('WEBSOCKET_CONFIGS');

class WebsocketModule {
    static forRoot(config = {}) {
        return {
            ngModule: WebsocketModule,
            providers: [
                {
                    provide: WEBSOCKET_CONFIGS,
                    useValue: config
                },
                {
                    provide: WebsocketConfig,
                    useFactory: WebsocketModule.configFactory,
                    deps: [WEBSOCKET_CONFIGS]
                }
            ]
        };
    }
    static configFactory(options) {
        return Object.assign(Object.assign({}, new WebsocketConfig()), options);
    }
}
/** @nocollapse */ /** @nocollapse */ WebsocketModule.ɵfac = function WebsocketModule_Factory(t) { return new (t || WebsocketModule)(); };
/** @nocollapse */ /** @nocollapse */ WebsocketModule.ɵmod = /** @pureOrBreakMyCode */ i0.ɵɵdefineNgModule({ type: WebsocketModule });
/** @nocollapse */ /** @nocollapse */ WebsocketModule.ɵinj = /** @pureOrBreakMyCode */ i0.ɵɵdefineInjector({});
(function () { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(WebsocketModule, [{
        type: NgModule
    }], null, null); })();

/**
 * Generated bundle index. Do not edit.
 */

export { AbstractWebsocketClient, BINARY, PLAIN_TEXT, WEBSOCKET_CONFIGS, WebsocketConfig, WebsocketModule };
//# sourceMappingURL=angular-ru-cdk-websocket.js.map
