"use strict";
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 __generator = (this && this.__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 });
exports.SocketClient = exports.encode = exports.decode = exports.send_escape = exports.receive_escape = void 0;
var net = require("net");
var events_1 = require("events");
/**
 * 接收数据转义
 * @param data
 * @returns
 */
function receive_escape(data) {
    var arr = [];
    for (var i = 0; i < data.length; i++) {
        //转义 0xff 0xbb => 0xfb 0xff 0xfc => 0xff 0xff 0xee => 0xfe
        if (data[i] === 0xff) {
            if (data[i + 1] === 0xbb) {
                arr.push(0xfb);
                i++;
            }
            else if (data[i + 1] === 0xfc) {
                arr.push(0xff);
                i++;
            }
            else if (data[i + 1] === 0xee) {
                arr.push(0xfe);
                i++;
            }
        }
        else {
            arr.push(data[i]);
        }
    }
    return arr;
}
exports.receive_escape = receive_escape;
/**
 * 发送数据转义
 * @param data
 * @returns
 */
function send_escape(data) {
    var arr = [];
    for (var i = 0; i < data.length; i++) {
        //转义 0xfb => 0xff 0xbb  0xff => 0xff 0xfc  0xfe => 0xff 0xee
        if (data[i] === 0xfb) {
            arr.push(0xff, 0xbb);
        }
        else if (data[i] === 0xff) {
            arr.push(0xff, 0xfc);
        }
        else if (data[i] === 0xfe) {
            arr.push(0xff, 0xee);
        }
        else {
            arr.push(data[i]);
        }
    }
    return arr;
}
exports.send_escape = send_escape;
/**
 * 解码（反序列化）
 * @param datas
 * @returns
 */
function decode(datas) {
    var packs = [];
    var _i = 0;
    var _j = 0;
    var unescape = null;
    //[][][][][][][][][][][][]
    for (var i = 0; i < datas.length; i++) {
        for (var j = 0; j < datas[i].length; j++) {
            if (datas[i][j] === 0xfb) {
                unescape = [0xfb];
            }
            else {
                if (unescape) {
                    unescape.push(datas[i][j]);
                    if (datas[i][j] === 0xfe) {
                        _i = i;
                        _j = j;
                        var escape_1 = receive_escape(unescape);
                        var data_length = Buffer.from(escape_1.slice(10, 12)).readUInt16BE();
                        var checksum = Buffer.from(escape_1.slice(12 + data_length, 12 + data_length + 2)).readUInt16BE();
                        var new_checksum = 0;
                        for (var i_1 = 1; i_1 < escape_1.length - 3; i_1++) {
                            new_checksum += escape_1[i_1];
                            //防溢出
                            new_checksum = new_checksum & 0xFFFF;
                        }
                        if (new_checksum === checksum) {
                            /**
                             解码
                                0,1 协议头	1byte	0xfb
                                1,5 时间戳	4byte	唯一标识，重复发送的包，时间戳需一样。
                                5,6 命令码	1byte	规定发送命令标示（回复包同发送包命令码一样)
                                6,8 总包数	2byte
                                8,10 包序号	2byte	从0开始（如仅仅1个包，则总包为1，包序号为0
                                10,12 数据长度	2byte	最大1024。
                                数据内容	N byte	依数据长度而定
                                校验码	2byte	从时间戳开始到数据内容最后一个字节在内的所有数据累加和
                                协议尾	1byte	0xfe
                            */
                            var pack = {
                                ts: Buffer.from(escape_1.slice(1, 5)).readUInt32BE(),
                                cmd: String.fromCharCode.apply(String, escape_1.slice(5, 6)),
                                packageTotal: Buffer.from(escape_1.slice(6, 8)).readUInt16BE(),
                                packageNo: Buffer.from(escape_1.slice(8, 10)).readUInt16BE(),
                                dataLength: data_length,
                                data: Buffer.from(escape_1.slice(12, 12 + data_length))
                            };
                            packs.push(pack);
                        }
                        unescape = null;
                    }
                }
            }
        }
    }
    var left = [];
    for (var i = _i; i < datas.length; i++) {
        if (i === _i) {
            if (_j === datas[i].length - 1)
                continue;
            left.push(datas[i].subarray(_j + 1));
        }
        else {
            left.push(datas[i]);
        }
    }
    return { packs: packs, left: left.length > 0 ? left : null };
}
exports.decode = decode;
/**
 * 编码（序列化）
 * @param cmd
 * @param data
 * @returns
 */
function encode(cmd, data) {
    var ts_buf = Buffer.alloc(4);
    ts_buf.writeInt32BE(Math.floor(Date.now() / 1000));
    var cmd_buf = Buffer.alloc(1);
    cmd_buf.writeUInt8(cmd.charCodeAt(0));
    var pack_total = Math.ceil(data.length / 1024) || 1;
    var pack_total_buf = Buffer.alloc(2);
    pack_total_buf.writeInt16BE(pack_total);
    var pack_no_buf = Buffer.alloc(2);
    var data_length_buf = Buffer.alloc(2);
    var checksum_buf = Buffer.alloc(2);
    var bufs = [];
    for (var i = 0; i < pack_total; i++) {
        var data_buf = data.subarray(i * 1024, (i + 1) * 1024);
        pack_no_buf.writeInt16BE(i);
        data_length_buf.writeUInt16BE(data_buf.length);
        var unescape_1 = Buffer.concat([
            ts_buf,
            cmd_buf,
            pack_total_buf,
            pack_no_buf,
            data_length_buf,
            data_buf,
            checksum_buf
        ]);
        var checksum = 0;
        for (var i_2 = 0; i_2 < unescape_1.length - 2; i_2++) {
            checksum += unescape_1[i_2];
        }
        unescape_1.writeUInt16BE(checksum, unescape_1.length - 2);
        var escape_2 = send_escape(unescape_1);
        var buf = Buffer.concat([Uint8Array.from([0xfb]), Buffer.from(escape_2), Uint8Array.from([0xfe])]);
        bufs.push(buf);
    }
    return bufs;
}
exports.encode = encode;
/**
 * 视频服务程序SOCKET客户端
 */
var SocketClient = /** @class */ (function () {
    function SocketClient(opts) {
        this._socket = null;
        this.opts = null;
        this.decode_cache = null;
        this.disposed = null;
        if (!net.isIP(opts.serverIp))
            throw new Error('Invalid ip.');
        if (opts.serverPort) {
            if (!Number.isInteger(opts.serverPort) ||
                opts.serverPort < 0 ||
                opts.serverPort > 65535)
                throw new Error('Invalid port.');
        }
        this.opts = Object.assign({}, opts);
        this.emitter = new events_1.EventEmitter();
        this._connect();
    }
    Object.defineProperty(SocketClient.prototype, "socketInstance", {
        get: function () {
            return this._socket;
        },
        enumerable: false,
        configurable: true
    });
    SocketClient.prototype.on = function (event, listener) {
        this.emitter.on(event, listener);
        return this;
    };
    /**
     * @param cmd 'A' | 'C' | 'D' | 'F' | 'n' | 'Z' | 'P' | 'V' 等
     * @param data
     */
    SocketClient.prototype.send = function (cmd, data) {
        this._send(cmd, Buffer.isBuffer(data) ? data : Buffer.from(JSON.stringify(data)));
    };
    SocketClient.prototype.destroy = function () {
        this._dispose();
    };
    SocketClient.prototype._connect = function () {
        var _this = this;
        var socket = net.connect({
            host: this.opts.serverIp,
            port: this.opts.serverPort,
        });
        socket.on('connect', function () {
            _this.emitter.emit('connect');
            _this._heartbeat();
        });
        socket.on('data', function (data) {
            _this.emitter.emit('data', data);
            _this._decode(data);
        });
        socket.on('error', function (err) {
            _this.emitter.emit('error', err);
        });
        socket.on('close', function (hadError) { return __awaiter(_this, void 0, void 0, function () {
            return __generator(this, function (_a) {
                this.emitter.emit('close', hadError);
                clearInterval(this.heartbeatTimer);
                this.decode_cache = null;
                this._reconnect();
                return [2 /*return*/];
            });
        }); });
        this._socket = socket;
    };
    SocketClient.prototype._reconnect = function () {
        return __awaiter(this, void 0, void 0, function () {
            var _this = this;
            return __generator(this, function (_a) {
                switch (_a.label) {
                    case 0:
                        if (!!this.disposed) return [3 /*break*/, 2];
                        return [4 /*yield*/, new Promise(function (resolve) {
                                setTimeout(resolve, _this.opts.reconnectDelay || 1000 * 10);
                            })];
                    case 1:
                        _a.sent();
                        this._connect();
                        _a.label = 2;
                    case 2: return [2 /*return*/];
                }
            });
        });
    };
    SocketClient.prototype._heartbeat = function () {
        var _this = this;
        if (!this.disposed) {
            clearInterval(this.heartbeatTimer);
            this.heartbeatTimer = setInterval(function () {
                _this.send('F', Buffer.alloc(0));
            }, 1000 * 5);
        }
    };
    SocketClient.prototype._decode = function (data) {
        var _this = this;
        if (!data || data.length === 0)
            return;
        try {
            if (data[0] === 0xfb) {
                this.decode_cache = [data];
            }
            else {
                if (this.decode_cache) {
                    if (this.decode_cache.length > 20) {
                        this.decode_cache.shift();
                    }
                    this.decode_cache.push(data);
                }
                else {
                    this.decode_cache = [data];
                }
            }
            if (!data.includes(0xfe))
                return;
            var dec = decode(this.decode_cache);
            dec.packs.forEach(function (pack) {
                _this.emitter.emit('package', pack);
                if (pack.cmd === 'n') {
                    try {
                        var data_1 = JSON.parse(Buffer.from(pack.data).toString());
                        if (data_1.cmd) {
                            _this.emitter.emit('cmd', data_1);
                        }
                    }
                    catch (error) { }
                }
            });
            this.decode_cache = dec.left;
        }
        catch (error) {
            this.emitter.emit('error', error);
        }
    };
    SocketClient.prototype._send = function (cmd, data) {
        if (this._socket.destroyed)
            return;
        var bufs = encode(cmd, data);
        for (var _a = 0, bufs_1 = bufs; _a < bufs_1.length; _a++) {
            var buf = bufs_1[_a];
            this._socket.write(buf);
            this.emitter.emit('send', buf, cmd);
        }
    };
    SocketClient.prototype._dispose = function () {
        this.disposed = true;
        clearInterval(this.heartbeatTimer);
        this._socket.destroy();
        this.decode_cache = null;
    };
    return SocketClient;
}());
exports.SocketClient = SocketClient;
//# sourceMappingURL=SocketClient.js.map