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());
    });
};
import { bufferToHexString } from '@iotize/common/byte-converter';
import { CodeError } from '@iotize/common/error';
import { debug } from './debug';
const TAG = 'Relay';
export class ComRelayError extends CodeError {
    static illegalArgument(message) {
        return new ComRelayError(ComRelayError.Code.IllegalArgmentError, message);
    }
    constructor(code, msg) {
        super(msg, code);
    }
}
(function (ComRelayError) {
    let Code;
    (function (Code) {
        Code["IllegalArgmentError"] = "IllegalArgmentError";
    })(Code = ComRelayError.Code || (ComRelayError.Code = {}));
})(ComRelayError || (ComRelayError = {}));
/**
 * This is an experimental feature.
 *
 * @experimental
 */
export class ComRelay {
    // 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
     */
    constructor(sourceProtocol, targetProtocol) {
        if (sourceProtocol) {
            this.setSourceProtocol(sourceProtocol);
        }
        if (targetProtocol) {
            this.setTargetProtocol(targetProtocol);
        }
    }
    get sourceProtocol() {
        if (!this.mSourceProtocol) {
            throw ComRelayError.illegalArgument('Missing source protocol');
        }
        return this.mSourceProtocol;
    }
    get targetProtocol() {
        if (!this.mTargetProtocol) {
            throw ComRelayError.illegalArgument('Missing target protocol');
        }
        return this.mTargetProtocol;
    }
    setSourceProtocol(protocol) {
        if (this.mSourceProtocol) {
            this.removeSourceProtocol();
        }
        this.mSourceProtocol = protocol;
        return this;
    }
    removeSourceProtocol() {
        if (this._sourceProtocolSubscription) {
            this._sourceProtocolSubscription.unsubscribe();
            this._sourceProtocolSubscription = undefined;
        }
        this.mSourceProtocol = undefined;
        return this;
    }
    setTargetProtocol(protocol) {
        this.mTargetProtocol = protocol;
        return this;
    }
    start(options) {
        return __awaiter(this, void 0, void 0, function* () {
            return promiseTimout(options ? options.timeout || -1 : -1, (resolve, reject) => __awaiter(this, void 0, void 0, function* () {
                try {
                    if (!this.sourceProtocol.isConnected()) {
                        debug(TAG, 'Connecting to source protocol...');
                        yield this.sourceProtocol.connect().toPromise();
                    }
                    if (!this.targetProtocol.isConnected()) {
                        debug(TAG, 'Connecting to target protocol...');
                        yield this.targetProtocol.connect().toPromise();
                    }
                    debug(TAG, 'Source/Target are now connected. Listening to messages...');
                    // We need to reconnect
                    this.listenToDataStream();
                    resolve();
                }
                catch (ex) {
                    reject(ex);
                }
            }));
        });
    }
    /**
     * Stop relay and disconnect target and source protocol according to given options
     * @param options
     */
    stop(options) {
        var _a, _b, _c, _d;
        if (!(options === null || options === void 0 ? void 0 : options.keepTargetProtocolConnected) &&
            ((_a = this.mTargetProtocol) === null || _a === void 0 ? void 0 : _a.isConnected())) {
            (_b = this.mTargetProtocol) === null || _b === void 0 ? void 0 : _b.disconnect();
        }
        if (!(options === null || options === void 0 ? void 0 : options.keepSourceProtocolConnected) &&
            ((_c = this.mSourceProtocol) === null || _c === void 0 ? void 0 : _c.isConnected())) {
            (_d = this.mSourceProtocol) === null || _d === void 0 ? void 0 : _d.disconnect();
        }
    }
    listenToDataStream() {
        // if (!this.mSourceProtocol!.isConnected()){
        //     await this.mSourceProtocol!.connect();
        // }
        return new Promise((resolve, reject) => {
            if (!this.mSourceProtocol) {
                return reject(ComRelayError.illegalArgument(`Missing source protocol`));
            }
            this._sourceProtocolSubscription = this.mSourceProtocol
                .receiveStream()
                .subscribe({
                next: (data) => {
                    if (!data) {
                        debug(TAG, 'Received null event...');
                        return;
                    }
                    if (!this.mTargetProtocol) {
                        debug(TAG, 'Target protocol is null. Cannot redirect message to it');
                        return;
                    }
                    try {
                        debug(TAG, `Received new message`, bufferToHexString(data));
                        this.mTargetProtocol
                            .send(data)
                            .toPromise()
                            .then((response) => {
                            debug(TAG, `Target protocol responded with: `, bufferToHexString(response));
                            return this.sourceProtocol.write(response);
                        })
                            .then(() => {
                            debug(TAG, `Successfully sent response!`);
                        })
                            .catch((err) => {
                            debug(TAG, `Cannot transfer response to source protocol: ${err} JSON=${JSON.stringify(err)}`, err);
                        });
                    }
                    catch (err) {
                        debug(TAG, `Source cannot transfer data to target due to error: ${err}`, err);
                    }
                },
                error: (err) => {
                    debug(TAG, `Error occured ${err}`);
                },
                complete: () => {
                    resolve();
                },
            });
        });
    }
    onStop() {
        // TODO cleanup
        return this;
    }
}
function promiseTimout(ms, callback) {
    return new Promise(function (resolve, reject) {
        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);
        }
    });
}
//# sourceMappingURL=data:application/json;base64, 