(function (global, factory) {
    typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('@iotize/common/byte-converter'), require('@iotize/common/promise'), require('@iotize/tap/protocol/core'), require('@iotize/tap/client/impl'), require('@iotize/common/debug'), require('@iotize/common/error'), require('rxjs'), require('rxjs/operators')) :
    typeof define === 'function' && define.amd ? define('@iotize/tap/protocol/ble/common', ['exports', '@iotize/common/byte-converter', '@iotize/common/promise', '@iotize/tap/protocol/core', '@iotize/tap/client/impl', '@iotize/common/debug', '@iotize/common/error', 'rxjs', 'rxjs/operators'], factory) :
    (global = typeof globalThis !== 'undefined' ? globalThis : 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.ble = global.iotize.tap.protocol.ble || {}, global.iotize.tap.protocol.ble.common = {}), global.byteConverter, global.promise, global.iotize.tap.protocol.core, global.iotize.tap.client.impl, global.debug$1, global.error, global.rxjs, global.rxjs.operators));
})(this, (function (exports, byteConverter, promise, core, impl, debug$1, error, rxjs, operators) { 'use strict';

    var BleConfig = {
        maxPacketLengthWithoutOffset: 19,
        services: {
            upgrade: {
                service: '9e5d1e47-5c13-43a0-8635-82ad38a1386f',
                charac: '347f7608-2e2d-47eb-913b-75d4edc4de3b',
            },
            lwm2m: {
                // // SPP over BLE Service UUID (128-bit)
                service: '6c7b16c2-2a5b-8c9f-cf42-d31425470e7b',
                charac: 'cc5c5491-b3be-9287-cb42-f7a6a29a50d5',
            },
            fastLwm2m: {
                service: '7c7b16c2-2a5b-8c9f-cf42-d31425470e7b',
                charac: 'dc5c5491-b3be-9287-cb42-f7a6a29a50d5',
            },
            standardClientConfig: {
                service: '00002902-0000-1000-8000-00805f9b34fb',
            },
        },
    };

    /*! *****************************************************************************
    Copyright (c) Microsoft Corporation.

    Permission to use, copy, modify, and/or distribute this software for any
    purpose with or without fee is hereby granted.

    THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
    REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
    AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
    INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
    LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
    OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
    PERFORMANCE OF THIS SOFTWARE.
    ***************************************************************************** */
    /* global Reflect, Promise */
    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 (Object.prototype.hasOwnProperty.call(b, p))
                    d[p] = b[p]; };
        return extendStatics(d, b);
    };
    function __extends(d, b) {
        if (typeof b !== "function" && b !== null)
            throw new TypeError("Class extends value " + String(b) + " is not a constructor or null");
        extendStatics(d, b);
        function __() { this.constructor = d; }
        d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
    }
    var __assign = function () {
        __assign = Object.assign || function __assign(t) {
            for (var s, i = 1, n = arguments.length; i < n; i++) {
                s = arguments[i];
                for (var p in s)
                    if (Object.prototype.hasOwnProperty.call(s, p))
                        t[p] = s[p];
            }
            return t;
        };
        return __assign.apply(this, arguments);
    };
    function __rest(s, e) {
        var t = {};
        for (var p in s)
            if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
                t[p] = s[p];
        if (s != null && typeof Object.getOwnPropertySymbols === "function")
            for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
                if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
                    t[p[i]] = s[p[i]];
            }
        return t;
    }
    function __decorate(decorators, target, key, desc) {
        var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
        if (typeof Reflect === "object" && typeof Reflect.decorate === "function")
            r = Reflect.decorate(decorators, target, key, desc);
        else
            for (var i = decorators.length - 1; i >= 0; i--)
                if (d = decorators[i])
                    r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
        return c > 3 && r && Object.defineProperty(target, key, r), r;
    }
    function __param(paramIndex, decorator) {
        return function (target, key) { decorator(target, key, paramIndex); };
    }
    function __metadata(metadataKey, metadataValue) {
        if (typeof Reflect === "object" && typeof Reflect.metadata === "function")
            return Reflect.metadata(metadataKey, metadataValue);
    }
    function __awaiter$2(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());
        });
    }
    function __generator(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 };
        }
    }
    var __createBinding = Object.create ? (function (o, m, k, k2) {
        if (k2 === undefined)
            k2 = k;
        Object.defineProperty(o, k2, { enumerable: true, get: function () { return m[k]; } });
    }) : (function (o, m, k, k2) {
        if (k2 === undefined)
            k2 = k;
        o[k2] = m[k];
    });
    function __exportStar(m, o) {
        for (var p in m)
            if (p !== "default" && !Object.prototype.hasOwnProperty.call(o, p))
                __createBinding(o, m, p);
    }
    function __values(o) {
        var s = typeof Symbol === "function" && Symbol.iterator, m = s && o[s], i = 0;
        if (m)
            return m.call(o);
        if (o && typeof o.length === "number")
            return {
                next: function () {
                    if (o && i >= o.length)
                        o = void 0;
                    return { value: o && o[i++], done: !o };
                }
            };
        throw new TypeError(s ? "Object is not iterable." : "Symbol.iterator is not defined.");
    }
    function __read(o, n) {
        var m = typeof Symbol === "function" && o[Symbol.iterator];
        if (!m)
            return o;
        var i = m.call(o), r, ar = [], e;
        try {
            while ((n === void 0 || n-- > 0) && !(r = i.next()).done)
                ar.push(r.value);
        }
        catch (error) {
            e = { error: error };
        }
        finally {
            try {
                if (r && !r.done && (m = i["return"]))
                    m.call(i);
            }
            finally {
                if (e)
                    throw e.error;
            }
        }
        return ar;
    }
    /** @deprecated */
    function __spread() {
        for (var ar = [], i = 0; i < arguments.length; i++)
            ar = ar.concat(__read(arguments[i]));
        return ar;
    }
    /** @deprecated */
    function __spreadArrays() {
        for (var s = 0, i = 0, il = arguments.length; i < il; i++)
            s += arguments[i].length;
        for (var r = Array(s), k = 0, i = 0; i < il; i++)
            for (var a = arguments[i], j = 0, jl = a.length; j < jl; j++, k++)
                r[k] = a[j];
        return r;
    }
    function __spreadArray(to, from, pack) {
        if (pack || arguments.length === 2)
            for (var i = 0, l = from.length, ar; i < l; i++) {
                if (ar || !(i in from)) {
                    if (!ar)
                        ar = Array.prototype.slice.call(from, 0, i);
                    ar[i] = from[i];
                }
            }
        return to.concat(ar || Array.prototype.slice.call(from));
    }
    function __await(v) {
        return this instanceof __await ? (this.v = v, this) : new __await(v);
    }
    function __asyncGenerator(thisArg, _arguments, generator) {
        if (!Symbol.asyncIterator)
            throw new TypeError("Symbol.asyncIterator is not defined.");
        var g = generator.apply(thisArg, _arguments || []), i, q = [];
        return i = {}, verb("next"), verb("throw"), verb("return"), i[Symbol.asyncIterator] = function () { return this; }, i;
        function verb(n) { if (g[n])
            i[n] = function (v) { return new Promise(function (a, b) { q.push([n, v, a, b]) > 1 || resume(n, v); }); }; }
        function resume(n, v) { try {
            step(g[n](v));
        }
        catch (e) {
            settle(q[0][3], e);
        } }
        function step(r) { r.value instanceof __await ? Promise.resolve(r.value.v).then(fulfill, reject) : settle(q[0][2], r); }
        function fulfill(value) { resume("next", value); }
        function reject(value) { resume("throw", value); }
        function settle(f, v) { if (f(v), q.shift(), q.length)
            resume(q[0][0], q[0][1]); }
    }
    function __asyncDelegator(o) {
        var i, p;
        return i = {}, verb("next"), verb("throw", function (e) { throw e; }), verb("return"), i[Symbol.iterator] = function () { return this; }, i;
        function verb(n, f) { i[n] = o[n] ? function (v) { return (p = !p) ? { value: __await(o[n](v)), done: n === "return" } : f ? f(v) : v; } : f; }
    }
    function __asyncValues(o) {
        if (!Symbol.asyncIterator)
            throw new TypeError("Symbol.asyncIterator is not defined.");
        var m = o[Symbol.asyncIterator], i;
        return m ? m.call(o) : (o = typeof __values === "function" ? __values(o) : o[Symbol.iterator](), i = {}, verb("next"), verb("throw"), verb("return"), i[Symbol.asyncIterator] = function () { return this; }, i);
        function verb(n) { i[n] = o[n] && function (v) { return new Promise(function (resolve, reject) { v = o[n](v), settle(resolve, reject, v.done, v.value); }); }; }
        function settle(resolve, reject, d, v) { Promise.resolve(v).then(function (v) { resolve({ value: v, done: d }); }, reject); }
    }
    function __makeTemplateObject(cooked, raw) {
        if (Object.defineProperty) {
            Object.defineProperty(cooked, "raw", { value: raw });
        }
        else {
            cooked.raw = raw;
        }
        return cooked;
    }
    ;
    var __setModuleDefault = Object.create ? (function (o, v) {
        Object.defineProperty(o, "default", { enumerable: true, value: v });
    }) : function (o, v) {
        o["default"] = v;
    };
    function __importStar(mod) {
        if (mod && mod.__esModule)
            return mod;
        var result = {};
        if (mod != null)
            for (var k in mod)
                if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k))
                    __createBinding(result, mod, k);
        __setModuleDefault(result, mod);
        return result;
    }
    function __importDefault(mod) {
        return (mod && mod.__esModule) ? mod : { default: mod };
    }
    function __classPrivateFieldGet(receiver, state, kind, f) {
        if (kind === "a" && !f)
            throw new TypeError("Private accessor was defined without a getter");
        if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver))
            throw new TypeError("Cannot read private member from an object whose class did not declare it");
        return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
    }
    function __classPrivateFieldSet(receiver, state, value, kind, f) {
        if (kind === "m")
            throw new TypeError("Private method is not writable");
        if (kind === "a" && !f)
            throw new TypeError("Private accessor was defined without a setter");
        if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver))
            throw new TypeError("Cannot write private member to an object whose class did not declare it");
        return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
    }

    /**
     * Created by IoTize on 19/04/2018.
     * <p>
     * Glue packet chunk to build the original ble packet
     * @param {number} bufferLength
     * @class
     */
    var BLEPacketBuilder = /** @class */ (function () {
        function BLEPacketBuilder(bufferLength) {
            this.bufferOffset = 0;
            this.dataLength = 0;
            this.data = new Uint8Array(bufferLength);
            this.reset();
        }
        /**
         * Append new chunk of data into the builder
         * @param {Array} dataChunk the data chunk. The first byte must be the position offset of this chunk in the buffer (in bytes)
         */
        BLEPacketBuilder.prototype.append = function (dataChunk) {
            if (!dataChunk || dataChunk.length < 2) {
                return;
            }
            this.bufferOffset = (dataChunk[0] & 0xff) - 1;
            if (this.bufferOffset + dataChunk.length > this.data.length) {
                throw new Error(// TODO ble error
                "Buffer size exceeded. Maximum size is " + this.data.length + " byte(s).");
            }
            for (var i = 1; i < dataChunk.length; i++) {
                this.data[this.bufferOffset + i] = dataChunk[i];
            }
            this.dataLength += dataChunk.length - 1;
        };
        BLEPacketBuilder.prototype.reset = function () {
            this.dataLength = 0;
            this.bufferOffset = 0;
        };
        BLEPacketBuilder.prototype.hasAllChunks = function () {
            return this.bufferOffset === -1;
        };
        BLEPacketBuilder.prototype.isChecksumValid = function () {
            var computedChecksum = this.getComputedChecksum();
            var expectedChecksum = this.getExpectedChecksum();
            return expectedChecksum === computedChecksum;
        };
        BLEPacketBuilder.prototype.getComputedChecksum = function () {
            return impl.Checksum.compute(this.data.subarray(0, this.dataLength - 1)) & 0xff;
        };
        BLEPacketBuilder.prototype.getExpectedChecksum = function () {
            return this.data[this.dataLength - 1];
        };
        /**
         * @return {Array} the result data without the checksum
         */
        BLEPacketBuilder.prototype.getData = function () {
            return this.data.slice(0, this.dataLength - 1);
            // let result: number[] = (s => { let a = []; while (s-- > 0) a.push(0); return a; })(this.dataLength - 1);
            // /* arraycopy */((srcPts, srcOff, dstPts, dstOff, size) => { if (srcPts !== dstPts || dstOff >= srcOff + size) { while (--size >= 0) dstPts[dstOff++] = srcPts[srcOff++]; } else { let tmp = srcPts.slice(srcOff, srcOff + size); for (let i = 0; i < size; i++) dstPts[dstOff++] = tmp[i]; } })(this.data, 0, result, 0, result.length);
            // return result;
        };
        /**
         * @return {Array} the buffer (with the checksum)
         */
        BLEPacketBuilder.prototype.getBuffer = function () {
            return this.data;
        };
        return BLEPacketBuilder;
    }());

    /**
     * Created by IoTize on 19/04/2018.
     *
     * Split data into chunks with a size of maxPacketSize + 1
     * @param {Array} data
     * @param {number} maxPacketSize
     * @class
     */
    var BLEPacketSplitter = /** @class */ (function () {
        /**
         *
         * @param data
         * @param maxPacketSize
         */
        function BLEPacketSplitter(data, maxPacketSize) {
            this.lastPacketSize = 0;
            this.maxPacketSize = 0;
            this.currentPacketIndex = 0;
            if (maxPacketSize < 1) {
                throw new Error('Packet size must be greater than 0');
            }
            this.data = data;
            this.maxPacketSize = maxPacketSize;
            this.currentPacketIndex = this.getTotalNumberOfPacket() - 1;
            this.lastPacketSize = this.getLastPacketSize();
        }
        /**
         * Create a BLEPacketSplitter instance from data and add the checksum at the end
         * @param {Array} data body data
         * @param {number} maxPacketSize packet size
         * @return {BLEPacketSplitter} the new instance
         */
        BLEPacketSplitter.wrapWithChecksum = function (data, maxPacketSize) {
            var checkSum = BLEPacketSplitter.computeChecksum(data);
            var wrappedData = new Uint8Array(data.length + 1);
            wrappedData.set(data);
            wrappedData.set([checkSum & 0xff], data.length);
            return new BLEPacketSplitter(wrappedData, maxPacketSize);
        };
        BLEPacketSplitter.prototype.getTotalNumberOfPacket = function () {
            return (((this.data.length / this.maxPacketSize) | 0) +
                (this.data.length % this.maxPacketSize === 0 ? 0 : 1));
        };
        BLEPacketSplitter.prototype.getLastPacketSize = function () {
            var result = this.data.length % this.maxPacketSize;
            if (result === 0) {
                result = this.maxPacketSize;
            }
            return result;
        };
        BLEPacketSplitter.prototype.getPackets = function () {
            var packets = [];
            while (this.hasNextPacket()) {
                packets.push(this.getNextPacket());
            }
            return packets;
        };
        BLEPacketSplitter.prototype.getNextPacket = function () {
            var packetSize;
            var offset;
            if (this.currentPacketIndex > 0) {
                offset =
                    this.lastPacketSize +
                        (this.currentPacketIndex - 1) * this.maxPacketSize;
                packetSize = this.maxPacketSize;
            }
            else {
                offset = 0;
                packetSize = this.lastPacketSize;
            }
            var packet = new Uint8Array(packetSize + 1);
            packet[0] = offset | 0;
            for (var i = 0; i < packetSize; i++) {
                packet[i + 1] = this.data[offset + i];
            }
            this.currentPacketIndex--;
            return packet;
        };
        BLEPacketSplitter.prototype.hasNextPacket = function () {
            return this.currentPacketIndex >= 0;
        };
        BLEPacketSplitter.computeChecksum = function (data) {
            return impl.Checksum.compute(data) & 0xff;
        };
        BLEPacketSplitter.prototype.getTotalSize = function () {
            return this.data.length;
        };
        return BLEPacketSplitter;
    }());

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

    exports.BleComError = /** @class */ (function (_super) {
        __extends(BleComError, _super);
        function BleComError(code, msg) {
            return _super.call(this, msg, code) || this;
        }
        BleComError.invalidBleChunkChecksum = function (packetBuilder) {
            return InvalidBleFrameChecksum.create(packetBuilder);
        };
        BleComError.writeSizeAboveMTU = function (data, mtu) {
            return new BleComError(BleComError.Code.BleWriteSizeAboveMTU, "Cannot write " + data.length + " bytes as it's more than the Maximal Transfert Unit (" + mtu + ")");
        };
        BleComError.serviceNotFound = function (uuid) {
            return new BleComError(BleComError.Code.BleGattServiceNotFound, "Bluetooth service with id " + uuid + " not found");
        };
        BleComError.charcacteristicNotFound = function (uuid) {
            return new BleComError(BleComError.Code.BleGattCharacteristicNotFound, "Bluetooth characteristic with id " + uuid + " not found");
        };
        BleComError.bleNotAvailable = function (msg) {
            if (msg === void 0) { msg = 'BLE is not available on your device'; }
            return new BleComError(BleComError.Code.NotAvailable, msg);
        };
        BleComError.gattServerNotAvailable = function () {
            return new BleComError(BleComError.Code.GATTServerNotAvailable, "Generic Attribute Profile (GATT) server is not available");
        };
        BleComError.gattServerConnectionFailed = function () {
            return new BleComError(BleComError.Code.GATTServerConnectionFailed, "Gatt server connection failed");
        };
        return BleComError;
    }(error.CodeError));
    var InvalidBleFrameChecksum = /** @class */ (function (_super) {
        __extends(InvalidBleFrameChecksum, _super);
        function InvalidBleFrameChecksum(packetBuilder) {
            var _this = _super.call(this, exports.BleComError.Code.InvalidChecksum, "Invalid checksum") || this;
            _this.packetBuilder = packetBuilder;
            return _this;
        }
        InvalidBleFrameChecksum.create = function (packetBuilder) {
            return new InvalidBleFrameChecksum(packetBuilder);
        };
        return InvalidBleFrameChecksum;
    }(exports.BleComError));
    (function (BleComError) {
        var Code;
        (function (Code) {
            Code["InvalidChecksum"] = "InvalidChecksum";
            Code["BleGattServiceNotFound"] = "BleGattServiceNotFound";
            Code["BleGattCharacteristicNotFound"] = "BleGattCharacteristicNotFound";
            Code["BleWriteSizeAboveMTU"] = "BleWriteSizeAboveMTU";
            Code["GATTServerNotAvailable"] = "BleComErrorGATTServerNotAvailable";
            Code["GATTServerConnectionFailed"] = "BleComErrorGATTServerConnectionFailed";
            Code["NotAvailable"] = "BleComErrorNotAvailable";
        })(Code = BleComError.Code || (BleComError.Code = {}));
    })(exports.BleComError || (exports.BleComError = {}));

    var __awaiter$1 = (this && this.__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());
        });
    };
    /**
     * @deprecated use UniversalBleProtocolAdpater
     * Abstract class for BLE communication
     *
     * With ble communication, data is split into sub packets.
     * This class handles creation of packet chunks.
     *
     * - You must only implement the function to send one packet chunk writeLwm2mPacketChunk()
     * -
     */
    var AbstractBleProtocol = /** @class */ (function (_super) {
        __extends(AbstractBleProtocol, _super);
        function AbstractBleProtocol(bleOptions) {
            if (bleOptions === void 0) { bleOptions = {
                mtu: BleConfig.maxPacketLengthWithoutOffset + 1,
            }; }
            var _this = _super.call(this) || this;
            _this.bleOptions = bleOptions;
            return _this;
        }
        AbstractBleProtocol.prototype.onNewLwm2mMessage = function (data) {
            if (this._readResolve) {
                debug('onNewLwm2mMessage()', byteConverter.bufferToHexString(data));
                this._readResolve(data);
            }
            else {
                console.warn("No listener for new message... 0x" + byteConverter.bufferToHexString(data));
                this._pendingRead = data;
            }
        };
        AbstractBleProtocol.prototype.onNewLwm2mMessageError = function (err) {
            console.error('Error lwm2m response...', err);
        };
        AbstractBleProtocol.prototype.onLwm2mDataChunkError = function (error) {
            if (this._readReject) {
                this._readReject(error);
            }
            else {
                console.warn('No listener for read message error...', error);
            }
        };
        AbstractBleProtocol.prototype.getLwm2mResponsePromise = function () {
            debug('getLwm2mResponsePromise');
            if (this._pendingRead) {
                var data = this._pendingRead;
                this._pendingRead = undefined;
                return Promise.resolve(data);
            }
            if (!this._readPromise) {
                this.createLwm2mResponsePromise();
            }
            return this._readPromise;
        };
        AbstractBleProtocol.prototype.createLwm2mResponsePromise = function () {
            var _this = this;
            debug('createLwm2mResponsePromise');
            this._readPromise = new Promise(function (resolve, reject) {
                _this._readResolve = resolve;
                _this._readReject = reject;
            });
            return this._readPromise;
        };
        // send(data: Uint8Array): Observable<Uint8Array> {
        //     debug("BLEProtocol:send() " + bufferToHexString(data));
        //     return from(
        //         this
        //             .write(data)
        //             .then(() => this.waitForLwm2mResponse())
        //             .catch((err) => {
        //                 console.error("ERROR SEND " + err);
        //                 throw err;
        //             })
        //     );
        // }
        AbstractBleProtocol.prototype.read = function () {
            var _this = this;
            debug('read()...');
            if (this.packetBuilder) {
                if (this.packetBuilder.hasAllChunks()) {
                    var data = this.packetBuilder.getData();
                    this.packetBuilder = undefined;
                    return Promise.resolve(data);
                }
                // TODO manage error
                console.warn('AbstractBleProtocol::read() There is already a read operation in progress but a new call to ::read has been made.');
            }
            return this.getLwm2mResponsePromise()
                .then(function (response) {
                _this.packetBuilder = undefined;
                return response;
            })
                .catch(function (err) {
                _this.packetBuilder = undefined;
                throw err;
            });
        };
        AbstractBleProtocol.prototype.write = function (data) {
            return __awaiter$1(this, void 0, void 0, function () {
                var packets, promise$1;
                var _this = this;
                return __generator(this, function (_a) {
                    if (this.packetSplitter != null) {
                        console.warn('BLEProtocol', 'write', 'There are already data being sent but a new call to ::write has been made');
                    }
                    this.packetSplitter = BLEPacketSplitter.wrapWithChecksum(data, this.bleOptions.mtu - 1);
                    packets = this.packetSplitter.getPackets();
                    this._pendingRead = undefined;
                    this.packetBuilder = new BLEPacketBuilder(AbstractBleProtocol.RECEIVED_BUFFER_LENGTH);
                    if (packets.length > 0) {
                        // this.createLwm2mResponsePromise()
                        //     .catch((err) => {
                        //         this.onNewLwm2mMessageError(err);
                        //     })
                        promise$1 = promise.promiseSerial(packets, function (packet, index) {
                            return _this.writeLwm2mPacketChunk(packet).then(function (value) {
                                debug("Done sending packet " + (index + 1) + "/" + packets.length);
                                return value;
                            });
                        });
                        // packets.forEach((value: Uint8Array, index: number) => {
                        //     if (!promise){
                        //         promise = this.writeLwm2mPacketChunk(packets[index]);
                        //     }
                        //     else{
                        //         promise = promise.then(() => this.writeLwm2mPacketChunk(packets[index]))
                        //     }
                        //     // promise.then(() => {
                        //         // debug("BLEProtocol", "write", `Done sending packet ${index+1}/${packets.length}`);
                        //     // });
                        // });
                    }
                    else {
                        console.warn('BLEProtocol', 'write', "Nothing to write...");
                        promise$1 = Promise.resolve();
                    }
                    return [2 /*return*/, promise$1
                            .then(function () {
                            _this.packetSplitter = undefined;
                        })
                            .catch(function (err) {
                            _this.packetSplitter = undefined;
                            throw err;
                        })];
                });
            });
        };
        /**
         *
         * @param data data chunk
         */
        AbstractBleProtocol.prototype.onNewLwm2mPacket = function (data) {
            if (!this.packetBuilder) {
                debug("Ignoring lwm2m packet 0x" + byteConverter.bufferToHexString(data) + " as not request has been made yet");
                return;
            }
            this.packetBuilder.append(data);
            if (this.packetBuilder.hasAllChunks()) {
                if (this.packetBuilder.isChecksumValid()) {
                    this.onNewLwm2mMessage(this.packetBuilder.getData());
                }
                else {
                    var error = InvalidBleFrameChecksum.create(this.packetBuilder);
                    this.onLwm2mDataChunkError(error);
                }
            }
        };
        return AbstractBleProtocol;
    }(core.QueueComProtocol));
    // TODO set an option instead ?
    AbstractBleProtocol.RECEIVED_BUFFER_LENGTH = 255;

    function sanitizeUUID(input) {
        return input.replace(/\-/g, '');
    }

    var __awaiter = (this && this.__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 DEFAULT_BLE_OPTIONS = {
        mtu: BleConfig.maxPacketLengthWithoutOffset + 1,
        maximumBufferLength: 255,
        waitForWriteAcknowledge: true,
        preferedComServiceType: 'large-frame',
        sanitizeUUID: false,
    };
    /**
     * BLE communication
     *
     * With ble communication, data is split into sub packets.
     * This class handles creation of packet chunks.
     *
     * - You must only implement the function to send one packet chunk writeLwm2mPacketChunk()
     * -
     */
    var UniversalBleProtocolAdapter = /** @class */ (function (_super) {
        __extends(UniversalBleProtocolAdapter, _super);
        function UniversalBleProtocolAdapter(peripheral, bleOptions) {
            if (bleOptions === void 0) { bleOptions = {}; }
            var _this = _super.call(this) || this;
            _this.peripheral = peripheral;
            _this._useSplitter = true;
            _this.bleOptions = Object.assign(Object.assign({}, DEFAULT_BLE_OPTIONS), bleOptions);
            _this.options.connect.timeout = 8000;
            _this.options.send.timeout = 4000;
            _this.options.disconnect.timeout = 8000;
            peripheral.stateChange.subscribe(function (newState) {
                if (newState !== _this.getConnectionState()) {
                    _this.setConnectionState(newState);
                }
            });
            return _this;
        }
        Object.defineProperty(UniversalBleProtocolAdapter.prototype, "lwm2mCharc", {
            get: function () {
                if (!this._lwm2mCharc) {
                    throw new Error("Illegal state error, lwm2m characteristic is not set");
                }
                return this._lwm2mCharc;
            },
            enumerable: false,
            configurable: true
        });
        UniversalBleProtocolAdapter.prototype.sanitizeUUID = function (uuid) {
            return this.bleOptions.sanitizeUUID ? sanitizeUUID(uuid) : uuid;
        };
        UniversalBleProtocolAdapter.prototype._connect = function () {
            var _this = this;
            return new rxjs.Observable(function (emitter) {
                (function () { return __awaiter(_this, void 0, void 0, function () {
                    var _a, err_1, err_2;
                    return __generator(this, function (_b) {
                        switch (_b.label) {
                            case 0:
                                _b.trys.push([0, 3, , 8]);
                                return [4 /*yield*/, this.peripheral.connect()];
                            case 1:
                                _b.sent();
                                _a = this;
                                return [4 /*yield*/, this.setupLwm2mCharacteristic()];
                            case 2:
                                _a._lwm2mCharc = _b.sent();
                                emitter.next(null);
                                emitter.complete();
                                return [3 /*break*/, 8];
                            case 3:
                                err_1 = _b.sent();
                                _b.label = 4;
                            case 4:
                                _b.trys.push([4, 6, , 7]);
                                return [4 /*yield*/, this.peripheral.disconnect()];
                            case 5:
                                _b.sent();
                                return [3 /*break*/, 7];
                            case 6:
                                err_2 = _b.sent();
                                debug('Failed to propertly disconnect after connection failed', err_2.message);
                                return [3 /*break*/, 7];
                            case 7:
                                emitter.error(err_1);
                                return [3 /*break*/, 8];
                            case 8: return [2 /*return*/];
                        }
                    });
                }); })();
            }).pipe(operators.share());
        };
        UniversalBleProtocolAdapter.prototype._disconnect = function () {
            this._lwm2mCharc = undefined;
            if (this.peripheral) {
                return rxjs.from(this.peripheral.disconnect());
            }
            return rxjs.from(Promise.resolve());
        };
        UniversalBleProtocolAdapter.prototype.setupLwm2mCharacteristic = function () {
            return __awaiter(this, void 0, void 0, function () {
                var lwm2mServiceUUIDs, serviceMap, charac;
                return __generator(this, function (_a) {
                    switch (_a.label) {
                        case 0:
                            lwm2mServiceUUIDs = [
                                this.sanitizeUUID(BleConfig.services.lwm2m.service),
                            ];
                            if (this.bleOptions.preferedComServiceType === 'large-frame') {
                                lwm2mServiceUUIDs.push(this.sanitizeUUID(BleConfig.services.fastLwm2m.service));
                            }
                            return [4 /*yield*/, this.peripheral.discoverServices(lwm2mServiceUUIDs)];
                        case 1:
                            serviceMap = _a.sent();
                            debug('Found services ', Object.keys(serviceMap).join(', '), 'asked for services: ', lwm2mServiceUUIDs.join(', '));
                            return [4 /*yield*/, this._selectLwm2mCharacteristic(serviceMap)];
                        case 2:
                            charac = _a.sent();
                            return [4 /*yield*/, charac.enableNotifications(true)];
                        case 3:
                            _a.sent();
                            return [2 /*return*/, charac];
                    }
                });
            });
        };
        UniversalBleProtocolAdapter.prototype._selectLwm2mCharacteristic = function (serviceMap) {
            return __awaiter(this, void 0, void 0, function () {
                var largeFrameServiceUUID, legacyLwm2mServiceUUID;
                return __generator(this, function (_a) {
                    largeFrameServiceUUID = this.sanitizeUUID(BleConfig.services.fastLwm2m.service);
                    legacyLwm2mServiceUUID = this.sanitizeUUID(BleConfig.services.lwm2m.service);
                    if (this.bleOptions.preferedComServiceType === 'legacy' &&
                        serviceMap[legacyLwm2mServiceUUID]) {
                        debug('Force usage of legacy lwm2m characteristic UUID: ' +
                            legacyLwm2mServiceUUID);
                        return [2 /*return*/, this._getLegacyLwm2mCharacteristic(serviceMap[legacyLwm2mServiceUUID])];
                    }
                    else if (serviceMap[largeFrameServiceUUID]) {
                        debug('Found fast lwm2m characteristic UUID: ' + largeFrameServiceUUID);
                        return [2 /*return*/, this._getLargeFrameLwm2mCharacteristic(serviceMap[largeFrameServiceUUID])];
                    }
                    else if (serviceMap[legacyLwm2mServiceUUID]) {
                        debug('Found legacy lwm2m characteristic UUID: ' + legacyLwm2mServiceUUID);
                        return [2 /*return*/, this._getLegacyLwm2mCharacteristic(serviceMap[legacyLwm2mServiceUUID])];
                    }
                    else {
                        throw exports.BleComError.serviceNotFound(legacyLwm2mServiceUUID);
                    }
                    return [2 /*return*/];
                });
            });
        };
        UniversalBleProtocolAdapter.prototype._getLargeFrameLwm2mCharacteristic = function (service) {
            return __awaiter(this, void 0, void 0, function () {
                return __generator(this, function (_a) {
                    switch (_a.label) {
                        case 0:
                            this.bleOptions.mtu = 255; // TODO read from device
                            this._useSplitter = false;
                            return [4 /*yield*/, service.getCharacteristic(this.sanitizeUUID(BleConfig.services.fastLwm2m.charac))];
                        case 1: return [2 /*return*/, _a.sent()];
                    }
                });
            });
        };
        UniversalBleProtocolAdapter.prototype._getLegacyLwm2mCharacteristic = function (service) {
            return __awaiter(this, void 0, void 0, function () {
                return __generator(this, function (_a) {
                    switch (_a.label) {
                        case 0:
                            this.bleOptions.mtu = BleConfig.maxPacketLengthWithoutOffset + 1;
                            this._useSplitter = true;
                            return [4 /*yield*/, service.getCharacteristic(this.sanitizeUUID(BleConfig.services.lwm2m.charac))];
                        case 1: return [2 /*return*/, _a.sent()];
                    }
                });
            });
        };
        UniversalBleProtocolAdapter.prototype.read = function () {
            return __awaiter(this, void 0, void 0, function () {
                return __generator(this, function (_a) {
                    // debug('read()...');
                    if (!this._readPromise) {
                        this._readPromise = this._createReadPromise();
                    }
                    return [2 /*return*/, this._readPromise];
                });
            });
        };
        UniversalBleProtocolAdapter.prototype.readUnit = function () {
            return __awaiter(this, void 0, void 0, function () {
                return __generator(this, function (_a) {
                    return [2 /*return*/, this.lwm2mCharc.data
                            .pipe(operators.first(), operators.map(function (info) { return info.data; }))
                            .toPromise()];
                });
            });
        };
        UniversalBleProtocolAdapter.prototype.write = function (data) {
            return __awaiter(this, void 0, void 0, function () {
                var chunks, chunks_1, chunks_1_1, chunk, e_1_1;
                var e_1, _a;
                return __generator(this, function (_b) {
                    switch (_b.label) {
                        case 0:
                            // debug('write()...');
                            this._readPromise = this._createReadPromise();
                            if (!this.useSplitter) return [3 /*break*/, 9];
                            chunks = BLEPacketSplitter.wrapWithChecksum(data, this.chunkSize).getPackets();
                            _b.label = 1;
                        case 1:
                            _b.trys.push([1, 6, 7, 8]);
                            chunks_1 = __values(chunks), chunks_1_1 = chunks_1.next();
                            _b.label = 2;
                        case 2:
                            if (!!chunks_1_1.done) return [3 /*break*/, 5];
                            chunk = chunks_1_1.value;
                            return [4 /*yield*/, this.writeUnit(chunk)];
                        case 3:
                            _b.sent();
                            _b.label = 4;
                        case 4:
                            chunks_1_1 = chunks_1.next();
                            return [3 /*break*/, 2];
                        case 5: return [3 /*break*/, 8];
                        case 6:
                            e_1_1 = _b.sent();
                            e_1 = { error: e_1_1 };
                            return [3 /*break*/, 8];
                        case 7:
                            try {
                                if (chunks_1_1 && !chunks_1_1.done && (_a = chunks_1.return)) _a.call(chunks_1);
                            }
                            finally { if (e_1) throw e_1.error; }
                            return [7 /*endfinally*/];
                        case 8: return [3 /*break*/, 10];
                        case 9: return [2 /*return*/, this.writeUnit(data)];
                        case 10: return [2 /*return*/];
                    }
                });
            });
        };
        Object.defineProperty(UniversalBleProtocolAdapter.prototype, "useSplitter", {
            get: function () {
                return this._useSplitter;
            },
            enumerable: false,
            configurable: true
        });
        Object.defineProperty(UniversalBleProtocolAdapter.prototype, "chunkSize", {
            get: function () {
                return this.bleOptions.mtu - 1;
            },
            enumerable: false,
            configurable: true
        });
        UniversalBleProtocolAdapter.prototype.writeUnit = function (data) {
            return __awaiter(this, void 0, void 0, function () {
                return __generator(this, function (_a) {
                    switch (_a.label) {
                        case 0:
                            if (data.length > this.bleOptions.mtu) {
                                throw exports.BleComError.writeSizeAboveMTU(data, this.bleOptions.mtu);
                            }
                            if (!this.bleOptions.waitForWriteAcknowledge) return [3 /*break*/, 2];
                            return [4 /*yield*/, this.lwm2mCharc.write(data, true)];
                        case 1:
                            _a.sent();
                            return [3 /*break*/, 3];
                        case 2:
                            this.lwm2mCharc.write(data, true).catch(function (err) {
                                console.warn("Write error ignored", err);
                            });
                            _a.label = 3;
                        case 3: return [2 /*return*/];
                    }
                });
            });
        };
        UniversalBleProtocolAdapter.prototype._createReadPromise = function () {
            return __awaiter(this, void 0, void 0, function () {
                var packetBuilder, chunk;
                return __generator(this, function (_a) {
                    switch (_a.label) {
                        case 0:
                            if (!this.useSplitter) return [3 /*break*/, 4];
                            packetBuilder = new BLEPacketBuilder(this.bleOptions.maximumBufferLength);
                            _a.label = 1;
                        case 1:
                            if (!!packetBuilder.hasAllChunks()) return [3 /*break*/, 3];
                            return [4 /*yield*/, this.readUnit()];
                        case 2:
                            chunk = _a.sent();
                            packetBuilder.append(chunk);
                            return [3 /*break*/, 1];
                        case 3:
                            if (packetBuilder.isChecksumValid()) {
                                return [2 /*return*/, packetBuilder.getData()];
                            }
                            else {
                                throw exports.BleComError.invalidBleChunkChecksum(packetBuilder);
                            }
                            return [3 /*break*/, 5];
                        case 4: return [2 /*return*/, this.readUnit()];
                        case 5: return [2 /*return*/];
                    }
                });
            });
        };
        return UniversalBleProtocolAdapter;
    }(core.QueueComProtocol));

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

    exports.AbstractBleProtocol = AbstractBleProtocol;
    exports.BLEPacketBuilder = BLEPacketBuilder;
    exports.BLEPacketSplitter = BLEPacketSplitter;
    exports.BleConfig = BleConfig;
    exports.DEFAULT_BLE_OPTIONS = DEFAULT_BLE_OPTIONS;
    exports.InvalidBleFrameChecksum = InvalidBleFrameChecksum;
    exports.UniversalBleProtocolAdapter = UniversalBleProtocolAdapter;

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

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