var __assign = (this && this.__assign) || function () {
    __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;
    };
    return __assign.apply(this, arguments);
};
import '../generated/converter-stream-extension';
import { bufferToHexString } from '@iotize/common/byte-converter';
import { AesEcb128Converter } from '@iotize/common/crypto';
import { ScramAuth } from '@iotize/tap/auth';
import { TapStreamReader, TapStreamWriter } from '@iotize/tap/client/impl';
import { DefaultSinglePacketKeyGenerator } from './single-packet-key-generator';
var TAG = 'SinglePacket';
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 SinglePacketEncryptionOptionsBuilder = /** @class */ (function () {
    function SinglePacketEncryptionOptionsBuilder() {
    }
    SinglePacketEncryptionOptionsBuilder.fromBlankKey = function (username) {
        return {
            hashKey: new Uint8Array(32),
            serialNumber: 'IoTize_FWUpdate_SP',
            username: username
        };
    };
    SinglePacketEncryptionOptionsBuilder.fromHashKey = function (hashKey, username) {
        return {
            hashKey: hashKey,
            serialNumber: 'IoTize_FWUpdate_SP',
            username: username
        };
    };
    SinglePacketEncryptionOptionsBuilder.fromUserCredentials = function (credentials, options) {
        var keys = ScramAuth.computeBaseKeys(credentials.password, options);
        return SinglePacketEncryptionOptionsBuilder.fromCurrentSessionData({
            username: credentials.username,
            serverKey: keys.serverKey,
            storedKey: keys.storedKey
        });
    };
    SinglePacketEncryptionOptionsBuilder.fromCurrentSessionData = function (sessionData) {
        var hashKey = TapStreamWriter.create(32).writeBytes(sessionData.storedKey).writeBytes(sessionData.serverKey).toBytes;
        if (hashKey.length !== 32) { // TODO constant
            throw new Error("Single packet encryption not available");
        }
        return {
            hashKey: hashKey,
            serialNumber: 'IoTize_FWUpdate_SP',
            username: sessionData.username
        };
    };
    return SinglePacketEncryptionOptionsBuilder;
}());
export { SinglePacketEncryptionOptionsBuilder };
var EncryptedSinglePacketConverter = /** @class */ (function () {
    function EncryptedSinglePacketConverter(options, encryptionAlgo, keyGenerator) {
        if (encryptionAlgo === void 0) { encryptionAlgo = new AesEcb128Converter(); }
        if (keyGenerator === void 0) { keyGenerator = new DefaultSinglePacketKeyGenerator(); }
        this.options = options;
        this.encryptionAlgo = encryptionAlgo;
        this.keyGenerator = keyGenerator;
    }
    EncryptedSinglePacketConverter.createWithBlankKey = function () {
        return new EncryptedSinglePacketConverter(SinglePacketEncryptionOptionsBuilder.fromBlankKey("IoTize"));
    };
    EncryptedSinglePacketConverter.prototype.setEncryptionOptions = function (options) {
        this.options = __assign(__assign({}, this.options), options);
    };
    EncryptedSinglePacketConverter.prototype.encode = function (packet) {
        var stream = new TapStreamWriter();
        stream.writeU4(packet.sendTime);
        stream.writeSinglePacketHeader(packet.header);
        var payloadBytesPosition = stream.pos;
        stream.writeSinglePacketPayload(packet.payload);
        if (packet.header.encryption) {
            var bytes = stream.toBytes;
            // debug(`SinglePacket before encryption: ${toHexString(bytes)}`);
            var encryptionAlgo = this._getEncryptionAlgo(packet.header);
            var encryptedPayloadBytes = bytes.slice(payloadBytesPosition);
            encryptedPayloadBytes = encryptionAlgo.encode(encryptedPayloadBytes);
            stream.pos = payloadBytesPosition;
            stream.writeBytes(encryptedPayloadBytes);
        }
        return stream.toBytes;
    };
    EncryptedSinglePacketConverter.prototype.decode = function (packetData) {
        var stream = TapStreamReader.fromArray(packetData);
        var sendTime = stream.readU4();
        var header = stream.readSinglePacketHeader();
        var payload;
        if (header.encryption) {
            var encryptedData = stream.readBytesFull();
            var encryptionAlgo = this._getEncryptionAlgo(header);
            var decryptedData = encryptionAlgo.decode(encryptedData);
            var payloadStream = TapStreamReader.fromArray(decryptedData);
            // if (header.messageType == SinglePacketModel.PacketType.CODE_EXEC) {
            payload = payloadStream.readSinglePacketPayloadFullData();
            // } else {
            //     payload = payloadStream.readSinglePacketPayload();
            // }
        }
        else {
            payload = stream.readSinglePacketPayload();
        }
        var packet = {
            sendTime: sendTime,
            header: header,
            payload: payload
        };
        // TODO check CRC
        // let computedCrc = 0;
        // if (computedCrc !== packet.payload.crc){
        //     CRC.fromBytes()
        // }
        return packet;
    };
    EncryptedSinglePacketConverter.prototype._getEncryptionAlgo = function (header) {
        var encryptionKey = this.computeKey(header);
        this.encryptionAlgo.setOptions({
            iv: '00000000000000000000000000000000',
            key: bufferToHexString(encryptionKey)
        });
        return this.encryptionAlgo;
    };
    EncryptedSinglePacketConverter.prototype.computeKey = function (header) {
        if (!(header instanceof Uint8Array)) {
            var stream = new TapStreamWriter(12);
            header = stream.writeSinglePacketHeader(header).toBytes;
        }
        var generateOptions = __assign(__assign({}, this.options), {
            headerBytes: header
        });
        var key = this.keyGenerator.generate(generateOptions);
        return key;
    };
    return EncryptedSinglePacketConverter;
}());
export { EncryptedSinglePacketConverter };
//# sourceMappingURL=data:application/json;base64, 