"use strict";
var __assign = (this && this.__assign) || Object.assign || function(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;
};
Object.defineProperty(exports, "__esModule", { value: true });
var crypto_helper_1 = require("../config/crypto-helper");
var crypto_js_1 = require("crypto-js");
var format_helper_1 = require("../../core/format/format-helper");
var model_1 = require("../model");
var tap_stream_1 = require("../../client/impl/frame/tap-stream");
var aes_ecb_128_converter_1 = require("../../core/crypto/aes-ecb-128-converter");
var logger_1 = require("../../logger");
var logger = logger_1.default('SinglePacket');
tap_stream_1.TapStreamReader.prototype.readSinglePacketPayloadFullData = function () {
    var model = {};
    model.logTime = this.readU4();
    model.dataSize = this.readU2();
    model.data = this.readBytes(-4);
    model.crc = this.readU4();
    return model;
};
var DefaultSinglePacketKeyGenerator = /** @class */ (function () {
    function DefaultSinglePacketKeyGenerator() {
    }
    /**
     * HMsg = UserName | IoTizeSN | PacketHeader1
     */
    DefaultSinglePacketKeyGenerator.prototype.generate = function (options) {
        var stream = new tap_stream_1.TapStreamWriter();
        if (options.username.length > 16) {
            throw new Error("Invalid username too long: " + options.username);
        }
        stream.writeStr(options.username);
        if (options.serialNumber.length != 18) { // TODO constant
            throw new Error("Invalid sn: " + options.serialNumber + ". Length must be 18");
        }
        stream.writeStr(options.serialNumber, 18);
        if (options.headerBytes.length != 12) {
            throw new Error("Invalid header length " + options.headerBytes.length + ". Expected 12");
        }
        stream.writeBytes(options.headerBytes);
        var data = stream.toBytes;
        var wordArray = crypto_helper_1.CryptoHelper.sanitizeInput(data);
        var keyArray = crypto_helper_1.CryptoHelper.sanitizeInput(options.hashKey);
        var result = crypto_js_1.HmacSHA256(wordArray, keyArray);
        var key = crypto_helper_1.CryptoHelper.wordArrayToByteArray(result).slice(0, 16);
        return key;
    };
    return DefaultSinglePacketKeyGenerator;
}());
exports.DefaultSinglePacketKeyGenerator = DefaultSinglePacketKeyGenerator;
var EncryptedSinglePacketConverter = /** @class */ (function () {
    function EncryptedSinglePacketConverter(options, encryptionAlgo, keyGenerator) {
        if (encryptionAlgo === void 0) { encryptionAlgo = new aes_ecb_128_converter_1.AesEcb128Converter(); }
        if (keyGenerator === void 0) { keyGenerator = new DefaultSinglePacketKeyGenerator(); }
        this.options = options;
        this.encryptionAlgo = encryptionAlgo;
        this.keyGenerator = keyGenerator;
    }
    // encode(packet: SinglePacketModel): Uint8Array {
    //     let stream = new TapStreamWriter(packet.outerHeader.packetLength + 4);
    //     let payload: Uint8Array = new TapStreamWriter().writeSinglePacketPayload(packet.payload).toBytes;
    //     if (packet.outerHeader.encryption) {
    //         let encryptionAlgo = this._getEncryptionAlgo(packet.outerHeader);
    //         payload = encryptionAlgo.encode(payload);
    //     }
    //     stream.writeU4(packet.sendTime);
    //     if (packet.outerHeader.encryption) {
    //         packet.outerHeader.packetLength /= 16
    //     }
    //     stream.writeSinglePacketOuterHeader(packet.outerHeader);
    //     stream.writeBytes(payload);
    //     return stream.toBytes;
    // }
    EncryptedSinglePacketConverter.prototype.encode = function (packet) {
        var stream = new tap_stream_1.TapStreamWriter();
        stream.writeU4(packet.sendTime);
        stream.writeSinglePacketOuterHeader(packet.outerHeader);
        var payloadBytesPosition = stream.pos;
        stream.writeSinglePacketPayload(packet.payload);
        // let payloadBytes: Uint8Array = // new TapStreamWriter().writeSinglePacketPayload(packet.payload).toBytes;
        if (packet.outerHeader.encryption) {
            var encryptionAlgo = this._getEncryptionAlgo(packet.outerHeader);
            var encryptedPayloadBytes = stream.toBytes.slice(payloadBytesPosition);
            encryptedPayloadBytes = encryptionAlgo.encode(encryptedPayloadBytes);
            stream.pos = payloadBytesPosition;
            stream.writeBytes(encryptedPayloadBytes);
        }
        // if (packet.outerHeader.encryption) {
        //     // If encryption packet length unit is 16bytes
        //     packet.outerHeader.packetLength = Math.floor((packet.payload.data.length + 6 + 4) / 16) 
        // }
        return stream.toBytes;
    };
    EncryptedSinglePacketConverter.prototype.decode = function (packetData) {
        var stream = tap_stream_1.TapStreamReader.fromArray(packetData);
        var sendTime = stream.readU4();
        var header = stream.readSinglePacketOuterHeader();
        var payload;
        if (header.encryption) {
            var encryptedData = stream.readBytesFull();
            var encryptionAlgo = this._getEncryptionAlgo(header);
            var decryptedData = encryptionAlgo.decode(encryptedData);
            var payloadStream = tap_stream_1.TapStreamReader.fromArray(decryptedData);
            if (header.messageType == model_1.SinglePacket.PacketType.CODE_EXEC) {
                payload = payloadStream.readSinglePacketPayloadFullData();
            }
            else {
                payload = payloadStream.readSinglePacketPayload();
            }
        }
        else {
            payload = stream.readSinglePacketPayload();
        }
        var packet = {
            sendTime: sendTime,
            outerHeader: header,
            payload: payload
        };
        // if (packet.outerHeader.messageType == SinglePacketModel.PacketType.CODE_EXEC) {
        //     packet.outerHeader.packetLength *= 16;
        // }
        // TODO check CRC
        // if (packet.payload.crc){
        //     CRC.fromBytes()
        // }
        return packet;
    };
    EncryptedSinglePacketConverter.prototype._getEncryptionAlgo = function (header) {
        var encryptionKey = this.computeKey(header);
        this.encryptionAlgo.setOptions({
            iv: "00000000000000000000000000000000",
            key: format_helper_1.FormatHelper.toHexString(encryptionKey)
        });
        return this.encryptionAlgo;
    };
    EncryptedSinglePacketConverter.prototype.computeKey = function (header) {
        if (!(header instanceof Uint8Array)) {
            var stream = new tap_stream_1.TapStreamWriter(12);
            header = stream.writeSinglePacketOuterHeader(header).toBytes;
        }
        var generateOptions = __assign({}, this.options, {
            headerBytes: header
        });
        var key = this.keyGenerator.generate(generateOptions);
        return key;
    };
    return EncryptedSinglePacketConverter;
}());
exports.EncryptedSinglePacketConverter = EncryptedSinglePacketConverter;
