(function (global, factory) {
    typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('@iotize/tap/protocol/api'), require('rxjs'), require('@iotize/common/debug'), require('@iotize/common/promise'), require('@iotize/common/rxjs'), require('@iotize/common/task-manager')) :
    typeof define === 'function' && define.amd ? define('@iotize/tap/protocol/core', ['exports', '@iotize/tap/protocol/api', 'rxjs', '@iotize/common/debug', '@iotize/common/promise', '@iotize/common/rxjs', '@iotize/common/task-manager'], factory) :
    (global = global || self, factory((global.iotize = global.iotize || {}, global.iotize.tap = global.iotize.tap || {}, global.iotize.tap.protocol = global.iotize.tap.protocol || {}, global.iotize.tap.protocol.core = {}), global.iotize.tap.protocol.api, global.rxjs, global.debug$1, global.promise, global.rxjs$1, global.taskManager));
}(this, (function (exports, api, rxjs, debug$1, promise, rxjs$1, taskManager) { 'use strict';

    var prefix = '@iotize/tap/protocol/common';
    var debug = debug$1.createDebugger(prefix);

    var TAG = 'ComProtocol';
    var AbstractComProtocol = /** @class */ (function () {
        function AbstractComProtocol() {
            this.connectionState = api.ConnectionState.DISCONNECTED;
            this._options = {
                connect: {
                    timeout: 1000
                },
                disconnect: {
                    timeout: 1000
                },
                send: {
                    timeout: 500
                }
            };
        }
        Object.defineProperty(AbstractComProtocol.prototype, "options", {
            get: function () {
                return this._options;
            },
            set: function (options) {
                this._options = options;
            },
            enumerable: true,
            configurable: true
        });
        AbstractComProtocol.prototype.getConnectionState = function () {
            return this.connectionState;
        };
        AbstractComProtocol.prototype.isConnected = function () {
            return this.connectionState == api.ConnectionState.CONNECTED;
        };
        AbstractComProtocol.prototype.setConnectionState = function (connectionState) {
            debug(TAG, 'setConnectionState', connectionState, 'Old: ', this.connectionState);
            var event = {
                newState: connectionState,
                oldState: this.connectionState
            };
            this.connectionState = connectionState;
            if (this._connectionStateChange) {
                this._connectionStateChange.next(event);
            }
            return this;
        };
        /**
         * Must be implemented in childs
         */
        AbstractComProtocol.prototype.receiveStream = function () {
            throw new Error('Method not implemented.');
        };
        AbstractComProtocol.prototype.onConnectionStateChange = function () {
            if (!this._connectionStateChange) {
                this._connectionStateChange = new rxjs.Subject();
            }
            return this._connectionStateChange;
        };
        return AbstractComProtocol;
    }());

    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 TAG$1 = 'ComProtocol';
    /**
     *
     */
    var QueueComProtocol = /** @class */ (function (_super) {
        __extends(QueueComProtocol, _super);
        function QueueComProtocol() {
            var _this = _super.call(this) || this;
            _this._jobQueue = new taskManager.TaskQueue();
            return _this;
        }
        QueueComProtocol.prototype._send = function (data, options) {
            var _this = this;
            try {
                if (!this.isConnected()) {
                    var error = api.ComProtocol.Errors.notConnected({
                        protocol: this
                    });
                    return rxjs.throwError(error);
                }
                var startTime_1 = new Date();
                return rxjs.from(promise.promiseTimeout(options.timeout, this.write(data), function (info) {
                    return api.ComProtocol.Errors.timeoutError({
                        msg: "Communication timeout. Send request to device device timeout (" + info.timeout + "ms).",
                        protocol: _this,
                        timeout: info.timeout,
                        startTime: info.startTime
                    });
                }).then(function () {
                    var elapsedTime = new Date().getTime() - startTime_1.getTime();
                    var newTimeout = options.timeout - elapsedTime;
                    return promise.promiseTimeout(newTimeout, _this.read(), function (info) {
                        return api.ComProtocol.Errors.timeoutError({
                            msg: "Communication timeout. Read response from device timeout (" + newTimeout + "ms).",
                            protocol: _this,
                            timeout: newTimeout,
                            startTime: info.startTime
                        });
                    });
                }));
            }
            catch (error) {
                return rxjs.throwError(error);
            }
        };
        /**
         * Cancel pending requests
         */
        QueueComProtocol.prototype.cancel = function () {
            debug(TAG$1, "Canceling operations");
            this._jobQueue.cancelAll(api.ComProtocol.Errors.operationCanceled());
        };
        /**
         *
         * @param data
         * @param options
         */
        QueueComProtocol.prototype.send = function (data, options) {
            // debug(TAG, `QueueComProtocol`, `::send() ENQUEUE request (${data.length} bytes) ${toHexString(data)}. Queue position: ${this._queue.size}`);
            var _this = this;
            if (true) {
                // Copy data ? TODO can be removed ?
                data = data.slice(0);
            }
            return this._jobQueue.addExecutor(function () {
                var timeout = options
                    ? options.timeout
                    : _this._options.send.timeout;
                return _this._send(data, {
                    timeout: timeout
                });
            });
        };
        /**
         * Connect with timeout
         *
         * If connect has already been called or is in progress / no further action
         *
         * @param options
         */
        QueueComProtocol.prototype.connect = function (options) {
            var _this = this;
            if (this.connectionState === api.ConnectionState.CONNECTED) {
                debug(TAG$1, "QueueComProtocol is already connected");
                return rxjs.of(null);
            }
            if (this.connectionState == api.ConnectionState.CONNECTING) {
                return this._connect$;
            }
            var connectTimeout = options
                ? options.timeout
                : this._options.connect.timeout;
            var startTime = new Date();
            this._connect$ = rxjs$1.timeoutObservable(this._connect(options), connectTimeout, function (err) {
                return api.ComProtocol.Errors.timeoutError({
                    msg: 'Protocol connection timeout',
                    protocol: _this,
                    timeout: connectTimeout,
                    startTime: startTime
                });
            });
            this.setConnectionState(api.ConnectionState.CONNECTING);
            this._connectionSubscription = this._connect$.subscribe(function () {
                _this.setConnectionState(api.ConnectionState.CONNECTED);
            }, function (error) {
                _this.setConnectionState(api.ConnectionState.DISCONNECTED);
            });
            return this._connect$;
        };
        QueueComProtocol.prototype.disconnect = function (options) {
            var _this = this;
            if (this.connectionState === api.ConnectionState.DISCONNECTED) {
                return rxjs.of(null);
            }
            if (this.connectionState == api.ConnectionState.DISCONNECTING) {
                return this._disconnect$;
            }
            this._connect$ = undefined;
            var disconnectTimeout = options
                ? options.timeout
                : this._options.disconnect.timeout;
            var startTime = new Date();
            this._disconnect$ = rxjs$1.timeoutObservable(this._disconnect(options), disconnectTimeout, function (err) {
                return api.ComProtocol.Errors.timeoutError({
                    msg: 'Protocol disconnection timeout',
                    protocol: _this,
                    timeout: disconnectTimeout,
                    startTime: startTime
                });
            });
            this.setConnectionState(api.ConnectionState.DISCONNECTING);
            this._disconnect$.subscribe(function () {
                _this.setConnectionState(api.ConnectionState.DISCONNECTED);
            }, function (error) {
                _this.setConnectionState(api.ConnectionState.DISCONNECTED);
            });
            return this._disconnect$;
        };
        return QueueComProtocol;
    }(AbstractComProtocol));

    exports.AbstractComProtocol = AbstractComProtocol;
    exports.QueueComProtocol = QueueComProtocol;

    Object.defineProperty(exports, '__esModule', { value: true });

})));
//# sourceMappingURL=iotize-tap-protocol-core.umd.js.map
