(function (global, factory) {
    typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory(require('buffer'), require('rxjs'), require('@iotize/common'), require('@iotize/common/byte-converter')) :
    typeof define === 'function' && define.amd ? define('@iotize/device-client.js/relay/impl', ['buffer', 'rxjs', '@iotize/common', '@iotize/common/byte-converter'], 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.relay = global.iotize['device-client'].js.relay || {}, global.iotize['device-client'].js.relay.impl = factory(global.buffer, global.rxjs, global.common, global.byteConverter)));
}(this, (function (buffer, rxjs, common, byteConverter) { 'use strict';

    buffer = buffer && buffer.hasOwnProperty('default') ? buffer['default'] : buffer;
    rxjs = rxjs && rxjs.hasOwnProperty('default') ? rxjs['default'] : rxjs;
    common = common && common.hasOwnProperty('default') ? common['default'] : common;
    byteConverter = byteConverter && byteConverter.hasOwnProperty('default') ? byteConverter['default'] : byteConverter;

    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:relay';
    exports.debug = common.createDebugger(prefix);

    });

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

    var relayServer = createCommonjsModule(function (module, exports) {
    "use strict";
    var __awaiter = (commonjsGlobal && commonjsGlobal.__awaiter) || function (thisArg, _arguments, P, generator) {
        function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
        return new (P || (P = Promise))(function (resolve, reject) {
            function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
            function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
            function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
            step((generator = generator.apply(thisArg, _arguments || [])).next());
        });
    };
    var __generator = (commonjsGlobal && commonjsGlobal.__generator) || function (thisArg, body) {
        var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
        return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
        function verb(n) { return function (v) { return step([n, v]); }; }
        function step(op) {
            if (f) throw new TypeError("Generator is already executing.");
            while (_) try {
                if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
                if (y = 0, t) op = [op[0] & 2, t.value];
                switch (op[0]) {
                    case 0: case 1: t = op; break;
                    case 4: _.label++; return { value: op[1], done: false };
                    case 5: _.label++; y = op[1]; op = [0]; continue;
                    case 7: op = _.ops.pop(); _.trys.pop(); continue;
                    default:
                        if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
                        if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
                        if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
                        if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
                        if (t[2]) _.ops.pop();
                        _.trys.pop(); continue;
                }
                op = body.call(thisArg, _);
            } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
            if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
        }
    };
    Object.defineProperty(exports, "__esModule", { value: true });




    var TAG = 'Relay';
    var RelayServer = /** @class */ (function () {
        function RelayServer(_server, params) {
            this._server = _server;
            this._params = params || {
                port: 2000
            };
            this._clients = [];
        }
        Object.defineProperty(RelayServer.prototype, "server", {
            get: function () {
                return this._server;
            },
            enumerable: true,
            configurable: true
        });
        RelayServer.prototype.addClient = function (socket) {
            this._clients.push(socket);
            return this;
        };
        RelayServer.prototype.removeClient = function (client) {
            var index = this._clients.indexOf(client);
            if (index >= 0) {
                client.close();
                debug.debug(TAG, "Removing client " + client.getClientName() + "...");
                delete this._clients[index];
            }
            return this;
        };
        RelayServer.prototype.start = function () {
            var _this = this;
            this._subscription = new rxjs.Subject();
            this._server
                .connections()
                .subscribe(function (socket) { return __awaiter(_this, void 0, void 0, function () {
                var clientName;
                var _this = this;
                return __generator(this, function (_a) {
                    switch (_a.label) {
                        case 0:
                            clientName = socket.getClientName();
                            debug.debug(TAG, "[NEW_CLIENT] " + clientName);
                            return [4 /*yield*/, socket.ready()];
                        case 1:
                            _a.sent();
                            debug.debug(TAG, "[NEW_CLIENT] " + clientName + " IS READY");
                            // Put this new client in the list
                            this.addClient(socket);
                            socket.events().subscribe({
                                next: function (event) {
                                    switch (event.type) {
                                        case 'data':
                                            var messageString = _this.messageToLog(event.payload);
                                            debug.debug(TAG, "[MESSAGE] " + messageString + " from " + clientName);
                                            _this.broadcastMessage(event.payload, socket);
                                            break;
                                        case 'error':
                                            debug.debug(TAG, "[ERROR] " + clientName + " ", event.payload);
                                            // Do we have to remove client every time ?
                                            _this.removeClient(socket);
                                            break;
                                        case 'end':
                                            debug.debug(TAG, "[CLIENTEND] " + clientName);
                                            _this.removeClient(socket);
                                            break;
                                    }
                                }
                            });
                            return [2 /*return*/];
                    }
                });
            }); });
            return this._server.listen(this._params);
        };
        RelayServer.prototype.stop = function () {
            return __awaiter(this, void 0, void 0, function () {
                return __generator(this, function (_a) {
                    switch (_a.label) {
                        case 0:
                            this._clients.forEach(function (client) {
                                client.close();
                            });
                            if (!this._server) return [3 /*break*/, 2];
                            return [4 /*yield*/, this._server.stop()];
                        case 1:
                            _a.sent();
                            _a.label = 2;
                        case 2:
                            if (this._subscription) {
                                this._subscription.complete();
                            }
                            return [2 /*return*/];
                    }
                });
            });
        };
        RelayServer.prototype.broadcastMessage = function (message, sender) {
            var messageString = this.messageToLog(message);
            debug.debug(TAG, "[BROADCAST] " + messageString + " from " + sender.getClientName());
            if (this._clients.length == 0) {
                debug.debug(TAG, 'There is no client yet');
                return;
            }
            // Send a message to all clients
            this._clients.forEach(function (client) {
                // Don't want to send it to sender
                try {
                    if (client === sender) {
                        debug.debug(TAG, "\t- Skip sender " + client.getClientName());
                        return;
                    }
                    if (client.isWritable()) {
                        debug.debug(TAG, "\t- Sent to " + client.getClientName());
                        client.write(message);
                    }
                    else {
                        debug.debug(TAG, "Client " + client.getClientName() + " is not writable. Cannot send to it");
                    }
                }
                catch (err) {
                    debug.debug("Cannot broadcast to " + client.getClientName(), err);
                }
            });
        };
        RelayServer.prototype.messageToLog = function (message) {
            if (typeof message === 'string') {
                return "\"" + message + "\"";
            }
            else if (message instanceof buffer.Buffer) {
                return byteConverter.bufferToHexString(message);
            }
            else {
                return message;
            }
        };
        return RelayServer;
    }());
    exports.RelayServer = RelayServer;

    });

    var relayServer$1 = unwrapExports(relayServer);
    var relayServer_1 = relayServer.RelayServer;

    var comRelay = 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 __());
        };
    })();
    var __awaiter = (commonjsGlobal && commonjsGlobal.__awaiter) || function (thisArg, _arguments, P, generator) {
        function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
        return new (P || (P = Promise))(function (resolve, reject) {
            function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
            function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
            function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
            step((generator = generator.apply(thisArg, _arguments || [])).next());
        });
    };
    var __generator = (commonjsGlobal && commonjsGlobal.__generator) || function (thisArg, body) {
        var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
        return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
        function verb(n) { return function (v) { return step([n, v]); }; }
        function step(op) {
            if (f) throw new TypeError("Generator is already executing.");
            while (_) try {
                if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
                if (y = 0, t) op = [op[0] & 2, t.value];
                switch (op[0]) {
                    case 0: case 1: t = op; break;
                    case 4: _.label++; return { value: op[1], done: false };
                    case 5: _.label++; y = op[1]; op = [0]; continue;
                    case 7: op = _.ops.pop(); _.trys.pop(); continue;
                    default:
                        if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
                        if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
                        if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
                        if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
                        if (t[2]) _.ops.pop();
                        _.trys.pop(); continue;
                }
                op = body.call(thisArg, _);
            } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
            if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
        }
    };
    Object.defineProperty(exports, "__esModule", { value: true });


    var TAG = 'Relay';
    var ComRelayError = /** @class */ (function (_super) {
        __extends(ComRelayError, _super);
        function ComRelayError(code, msg) {
            var _this = _super.call(this, msg) || this;
            _this.code = code;
            return _this;
        }
        ComRelayError.illegalArgument = function (message) {
            return new ComRelayError(ComRelayError.Code.IllegalArgmentError, message);
        };
        return ComRelayError;
    }(Error));
    exports.ComRelayError = ComRelayError;
    (function (ComRelayError) {
        var Code;
        (function (Code) {
            Code["IllegalArgmentError"] = "IllegalArgmentError";
        })(Code = ComRelayError.Code || (ComRelayError.Code = {}));
    })(ComRelayError = exports.ComRelayError || (exports.ComRelayError = {}));
    exports.ComRelayError = ComRelayError;
    /**
     * This is an experimental feature.
     *
     * @experimental
     */
    var ComRelay = /** @class */ (function () {
        // private IRelayCallback mCallback;
        // public final RelayServiceInfoModel mRelayInfo;
        // private DefaultSocketServer mSocketServer;
        // private WebSocketServer mWebSocketServer;
        /**
         * @param sourceProtocol protocol that will received commands
         * @param targetProtocol protocol on wich commmand will be sent
         */
        function ComRelay(sourceProtocol, targetProtocol) {
            if (sourceProtocol) {
                this.setSourceProtocol(sourceProtocol);
            }
            if (targetProtocol) {
                this.setTargetProtocol(targetProtocol);
            }
        }
        ComRelay.prototype.setSourceProtocol = function (protocol) {
            if (this.mSourceProtocol) {
                this.removeSourceProtocol();
            }
            this.mSourceProtocol = protocol;
            return this;
        };
        ComRelay.prototype.removeSourceProtocol = function () {
            if (this._sourceProtocolSubscription) {
                this._sourceProtocolSubscription.unsubscribe();
                this._sourceProtocolSubscription = undefined;
            }
            this.mSourceProtocol = undefined;
            return this;
        };
        ComRelay.prototype.setTargetProtocol = function (protocol) {
            this.mTargetProtocol = protocol;
            return this;
        };
        ComRelay.prototype.start = function (options) {
            return __awaiter(this, void 0, void 0, function () {
                var _this = this;
                return __generator(this, function (_a) {
                    return [2 /*return*/, promiseTimout(options ? options.timeout || -1 : -1, function (resolve, reject) { return __awaiter(_this, void 0, void 0, function () {
                            var ex_1;
                            return __generator(this, function (_a) {
                                switch (_a.label) {
                                    case 0:
                                        _a.trys.push([0, 5, , 6]);
                                        if (!this.mSourceProtocol) {
                                            throw ComRelayError.illegalArgument('Missing source protocol');
                                        }
                                        if (!this.mTargetProtocol) {
                                            throw ComRelayError.illegalArgument('Missing target protocol');
                                        }
                                        if (!!this.mSourceProtocol.isConnected()) return [3 /*break*/, 2];
                                        debug.debug(TAG, 'Connecting to source protocol...');
                                        return [4 /*yield*/, this.mSourceProtocol.connect().toPromise()];
                                    case 1:
                                        _a.sent();
                                        _a.label = 2;
                                    case 2:
                                        if (!!this.mTargetProtocol.isConnected()) return [3 /*break*/, 4];
                                        debug.debug(TAG, 'Connecting to target protocol...');
                                        return [4 /*yield*/, this.mTargetProtocol.connect().toPromise()];
                                    case 3:
                                        _a.sent();
                                        _a.label = 4;
                                    case 4:
                                        debug.debug(TAG, 'Source/Target are now connected. Listening to messages...');
                                        // We need to reconnect
                                        this.listenToDataStream();
                                        resolve();
                                        return [3 /*break*/, 6];
                                    case 5:
                                        ex_1 = _a.sent();
                                        reject(ex_1);
                                        return [3 /*break*/, 6];
                                    case 6: return [2 /*return*/];
                                }
                            });
                        }); })];
                });
            });
        };
        ComRelay.prototype.stop = function () {
            if (this.mTargetProtocol && this.mTargetProtocol.isConnected()) {
                this.mSourceProtocol.disconnect();
            }
            if (this.mSourceProtocol && this.mSourceProtocol.isConnected()) {
                this.mSourceProtocol.disconnect();
            }
        };
        ComRelay.prototype.listenToDataStream = function () {
            var _this = this;
            // if (!this.mSourceProtocol!.isConnected()){
            //     await this.mSourceProtocol!.connect();
            // }
            return new Promise(function (resolve, reject) {
                _this._sourceProtocolSubscription = _this
                    .mSourceProtocol
                    .receiveStream()
                    .subscribe({
                    next: function (data) {
                        if (!data) {
                            debug.debug(TAG, 'Received null event...');
                            return;
                        }
                        if (!_this.mTargetProtocol) {
                            debug.debug(TAG, 'Target protocol is null. Cannot redirect message to it');
                            return;
                        }
                        try {
                            debug.debug(TAG, "Received new message", byteConverter.bufferToHexString(data));
                            _this.mTargetProtocol
                                .send(data)
                                .toPromise()
                                // .catch((err) => {
                                //     debug(TAG, `Source cannot send to target protocol`, err);
                                //     // TODO check if we are still connected
                                //     return Response.create();
                                // })
                                .then(function (response) {
                                debug.debug(TAG, "Target protocol responded with: ", byteConverter.bufferToHexString(response));
                                return _this.mSourceProtocol.write(response);
                            })
                                .then(function () {
                                debug.debug(TAG, "Successfully sent response!");
                            })
                                .catch(function (err) {
                                debug.debug(TAG, "Cannot transfer response to source protocol: " + err + " JSON=" + JSON.stringify(err), err);
                            });
                        }
                        catch (err) {
                            debug.debug(TAG, "Source cannot transfer data to target due to error: " + err, err);
                        }
                    },
                    error: function (err) {
                        debug.debug(TAG, "Error occured " + err);
                    },
                    complete: function () {
                        resolve();
                    }
                });
            });
        };
        ComRelay.prototype.onStop = function () {
            // TODO cleanup
            return this;
        };
        return ComRelay;
    }());
    exports.ComRelay = ComRelay;
    function promiseTimout(ms, callback) {
        return new Promise(function (resolve, reject) {
            debug.debug(TAG, "promiseTimout start with " + ms + " milliseconds");
            // Set up the real work
            callback(resolve, reject);
            // Set up the timeout
            if (ms >= 0) {
                setTimeout(function () {
                    reject('Promise timed out after ' + ms + ' ms');
                }, ms);
            }
        });
    }

    });

    var comRelay$1 = unwrapExports(comRelay);
    var comRelay_1 = comRelay.ComRelayError;
    var comRelay_2 = comRelay.ComRelay;

    var lib = 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(relayServer);
    __export(comRelay);

    });

    var index = unwrapExports(lib);

    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(lib);

    });

    var public_api$1 = unwrapExports(public_api);

    var iotizeDeviceClient_jsRelayImpl = 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_jsRelayImpl$1 = unwrapExports(iotizeDeviceClient_jsRelayImpl);

    return iotizeDeviceClient_jsRelayImpl$1;

})));
//# sourceMappingURL=iotize-device-client.js-relay-impl.umd.js.map
