"use strict";
var __extends = (this && this.__extends) || (function () {
    var 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 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 api_1 = require("../../protocol/api");
var rxjs_1 = require("rxjs");
var abstract_com_protocol_1 = require("./abstract-com-protocol");
var observable_helper_1 = require("../../util/observable-helper");
var logger_1 = require("../../logger");
var promise_timeout_1 = require("../../util/promise-timeout");
var task_1 = require("../../util/task");
var logger = logger_1.default('ComProtocol');
/**
 *
 */
var QueueComProtocol = /** @class */ (function (_super) {
    __extends(QueueComProtocol, _super);
    function QueueComProtocol() {
        var _this = _super.call(this) || this;
        _this._jobQueue = new task_1.TaskQueue();
        return _this;
    }
    QueueComProtocol.prototype._send = function (data, options) {
        var _this = this;
        try {
            if (!this.isConnected()) {
                var error = api_1.ComProtocol.Errors.notConnected({
                    protocol: this
                });
                return rxjs_1.throwError(error);
            }
            var startTime_1 = new Date();
            return rxjs_1.from(promise_timeout_1.promiseTimeout(options.timeout, this.write(data), function (info) {
                return api_1.ComProtocol.Errors.timeoutError({
                    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_timeout_1.promiseTimeout(newTimeout, _this.read());
            }));
        }
        catch (error) {
            return rxjs_1.throwError(error);
            // return Promise.reject(error);        
        }
    };
    /**
     * Cancel pending requests
     */
    QueueComProtocol.prototype.cancel = function () {
        logger.debug("Canceling operations");
        this._jobQueue.cancelAll(api_1.ComProtocol.Errors.operationCanceled());
    };
    /**
     *
     * @param data
     * @param options
     */
    QueueComProtocol.prototype.send = function (data, options) {
        // logger.debug(`QueueComProtocol`, `::send() ENQUEUE request (${data.length} bytes) ${FormatHelper.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_1.ConnectionState.CONNECTED) {
            logger.debug("QueueComProtocol is already connected");
            return rxjs_1.of(null);
        }
        if (this.connectionState == api_1.ConnectionState.CONNECTING) {
            return this._connect$;
        }
        var connectTimeout = options ? options.timeout : this._options.connect.timeout;
        var startTime = new Date();
        this._connect$ = observable_helper_1.ObservableHelper.timeout(this._connect(options), connectTimeout, function (err) {
            return api_1.ComProtocol.Errors.timeoutError({
                msg: 'Protocol connection timeout',
                protocol: _this,
                timeout: connectTimeout,
                startTime: startTime
            });
        });
        this.setConnectionState(api_1.ConnectionState.CONNECTING);
        this._connectionSubscription = this._connect$.subscribe(function () {
            _this.setConnectionState(api_1.ConnectionState.CONNECTED);
        }, function (error) {
            _this.setConnectionState(api_1.ConnectionState.DISCONNECTED);
        });
        return this._connect$;
    };
    QueueComProtocol.prototype.disconnect = function (options) {
        var _this = this;
        if (this.connectionState === api_1.ConnectionState.DISCONNECTED) {
            return rxjs_1.of(null);
        }
        if (this.connectionState == api_1.ConnectionState.DISCONNECTING) {
            return this._disconnect$;
        }
        this._connect$ = undefined;
        var disconnectTimeout = options ? options.timeout : this._options.disconnect.timeout;
        var startTime = new Date();
        this._disconnect$ = observable_helper_1.ObservableHelper.timeout(this._disconnect(options), disconnectTimeout, function (err) {
            return api_1.ComProtocol.Errors.timeoutError({
                msg: 'Protocol disconnection timeout',
                protocol: _this,
                timeout: disconnectTimeout,
                startTime: startTime
            });
        });
        this.setConnectionState(api_1.ConnectionState.DISCONNECTING);
        this._disconnect$.subscribe(function () {
            _this.setConnectionState(api_1.ConnectionState.DISCONNECTED);
        }, function (error) {
            _this.setConnectionState(api_1.ConnectionState.DISCONNECTED);
        });
        return this._disconnect$;
    };
    return QueueComProtocol;
}(abstract_com_protocol_1.AbstractComProtocol));
exports.QueueComProtocol = QueueComProtocol;
