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

    api = api && api.hasOwnProperty('default') ? api['default'] : api;
    rxjs = rxjs && rxjs.hasOwnProperty('default') ? rxjs['default'] : rxjs;
    common = common && common.hasOwnProperty('default') ? common['default'] : common;
    promise = promise && promise.hasOwnProperty('default') ? promise['default'] : promise;
    rxjs$1 = rxjs$1 && rxjs$1.hasOwnProperty('default') ? rxjs$1['default'] : rxjs$1;
    taskManager = taskManager && taskManager.hasOwnProperty('default') ? taskManager['default'] : taskManager;

    var commonjsGlobal = typeof globalThis !== 'undefined' ? globalThis : typeof window !== 'undefined' ? window : typeof global !== 'undefined' ? global : typeof self !== 'undefined' ? self : {};

    function commonjsRequire () {
    	throw new Error('Dynamic requires are not currently supported by rollup-plugin-commonjs');
    }

    function unwrapExports (x) {
    	return x && x.__esModule && Object.prototype.hasOwnProperty.call(x, 'default') ? x['default'] : x;
    }

    function createCommonjsModule(fn, module) {
    	return module = { exports: {} }, fn(module, module.exports), module.exports;
    }

    function getCjsExportFromNamespace (n) {
    	return n && n['default'] || n;
    }

    var debug = createCommonjsModule(function (module, exports) {
    "use strict";
    Object.defineProperty(exports, "__esModule", { value: true });

    var prefix = '@iotize:device-client.js:protocol/common';
    exports.debug = common.createDebugger(prefix);

    });

    var debug$1 = unwrapExports(debug);
    var debug_1 = debug.debug;

    var abstractComProtocol = createCommonjsModule(function (module, exports) {
    "use strict";
    Object.defineProperty(exports, "__esModule", { value: true });



    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.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;
    }());
    exports.AbstractComProtocol = AbstractComProtocol;

    });

    var abstractComProtocol$1 = unwrapExports(abstractComProtocol);
    var abstractComProtocol_1 = abstractComProtocol.AbstractComProtocol;

    var queueComProtocol = createCommonjsModule(function (module, exports) {
    "use strict";
    var __extends = (commonjsGlobal && commonjsGlobal.__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 __());
        };
    })();
    Object.defineProperty(exports, "__esModule", { value: true });







    var TAG = '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.debug(TAG, "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.debug(TAG, "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.AbstractComProtocol));
    exports.QueueComProtocol = QueueComProtocol;

    });

    var queueComProtocol$1 = unwrapExports(queueComProtocol);
    var queueComProtocol_1 = queueComProtocol.QueueComProtocol;

    var public_api = createCommonjsModule(function (module, exports) {
    "use strict";
    function __export(m) {
        for (var p in m) if (!exports.hasOwnProperty(p)) exports[p] = m[p];
    }
    Object.defineProperty(exports, "__esModule", { value: true });
    __export(abstractComProtocol);
    __export(queueComProtocol);

    });

    var public_api$1 = unwrapExports(public_api);

    var iotizeDeviceClient_jsProtocolCore = createCommonjsModule(function (module, exports) {
    "use strict";
    /**
     * Generated bundle index. Do not edit.
     */
    function __export(m) {
        for (var p in m) if (!exports.hasOwnProperty(p)) exports[p] = m[p];
    }
    Object.defineProperty(exports, "__esModule", { value: true });
    __export(public_api);

    });

    var iotizeDeviceClient_jsProtocolCore$1 = unwrapExports(iotizeDeviceClient_jsProtocolCore);

    return iotizeDeviceClient_jsProtocolCore$1;

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