"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.parseInstallCode = parseInstallCode;
exports.checkInstallCode = checkInstallCode;
const consts_1 = require("../../zspec/consts");
const utils_1 = require("../../zspec/utils");
/**
 * Parse the given code using known formats:
 * - 95 or 91-length
 * - Widely adopted (Ubisys, Danfoss, Inovelli, Ledvance): ...Z:<ieee>$I:<key>...
 * - Pipe-separated (Muller-Licht, Innr): <ieee>|<key>
 * - Pipe-enclosed (frient): |<ieee>|<key>|
 * - Aqara: G$M:...$A:<ieee>$I:<key>
 * - Hue: HUE:Z:<key> M:<ieee>...
 * @param installCode
 * @returns
 *   - the IEEE address
 *   - the raw key
 */
function parseInstallCode(installCode) {
    const widelyAdoptedMatch = installCode.match(/Z:([a-zA-Z0-9]{16})\$I:([a-zA-Z0-9]+)/);
    if (widelyAdoptedMatch) {
        return [`0x${widelyAdoptedMatch[1].toLowerCase()}`, widelyAdoptedMatch[2]];
    }
    const pipeMatch = installCode.match(/^\|?([a-zA-Z0-9]{16})\|([a-zA-Z0-9]+)\|?$/);
    if (pipeMatch) {
        return [`0x${pipeMatch[1].toLowerCase()}`, pipeMatch[2]];
    }
    const aqaraMatch = installCode.match(/^G\$M:.+\$A:([a-zA-Z0-9]{16})\$I:([a-zA-Z0-9]+)$/);
    if (aqaraMatch) {
        return [`0x${aqaraMatch[1].toLowerCase()}`, aqaraMatch[2]];
    }
    const hueMatch = installCode.match(/^HUE:Z:([a-zA-Z0-9]+) M:([a-zA-Z0-9]{16})/);
    if (hueMatch) {
        return [`0x${hueMatch[2].toLowerCase()}`, hueMatch[1]];
    }
    if (installCode.length === 95 || installCode.length === 91) {
        const keyStart = installCode.length - (installCode.length === 95 ? 36 : 32);
        return [`0x${installCode.substring(keyStart - 19, keyStart - 3).toLowerCase()}`, installCode.substring(keyStart, installCode.length)];
    }
    throw new Error(`Unsupported install code, got ${installCode.length} chars, expected 95 or 91 chars, or known format`);
}
/**
 * Check if install code (little-endian) is valid, and if not, and requested, fix it.
 *
 * WARNING: Due to conflicting sizes between 8-length code with invalid CRC, and 10-length code missing CRC, given 8-length codes are always assumed to be 8-length code with invalid CRC (most probable scenario).
 *
 * @param code The code to check. Reference is not modified by this procedure but is returned when code was valid, as `outCode`.
 * @param adjust If false, throws if the install code is invalid, otherwise try to fix it (CRC)
 * @returns
 *   - The adjusted code, or `code` if not adjusted.
 *   - If adjust is false, undefined, otherwise, the reason why the code needed adjusting or undefined if not.
 *   - Throws when adjust=false and invalid, or cannot fix.
 */
function checkInstallCode(code, adjust = true) {
    const crcLowByteIndex = code.length - consts_1.INSTALL_CODE_CRC_SIZE;
    const crcHighByteIndex = code.length - consts_1.INSTALL_CODE_CRC_SIZE + 1;
    for (const codeSize of consts_1.INSTALL_CODE_SIZES) {
        if (code.length === codeSize) {
            // install code has CRC, check if valid, if not, replace it
            const crc = (0, utils_1.crc16X25)(code.subarray(0, -2));
            const crcHighByte = (crc >> 8) & 0xff;
            const crcLowByte = crc & 0xff;
            if (code[crcLowByteIndex] !== crcLowByte || code[crcHighByteIndex] !== crcHighByte) {
                // see WARNING above, 8 is smallest valid length, so always ends up here
                if (adjust) {
                    const outCode = Buffer.from(code);
                    outCode[crcLowByteIndex] = crcLowByte;
                    outCode[crcHighByteIndex] = crcHighByte;
                    return [outCode, "invalid CRC"];
                }
                throw new Error(`Install code ${code.toString("hex")} failed CRC validation`);
            }
            return [code, undefined];
        }
        if (code.length === codeSize - consts_1.INSTALL_CODE_CRC_SIZE) {
            if (adjust) {
                // install code is missing CRC
                const crc = (0, utils_1.crc16X25)(code);
                const outCode = Buffer.alloc(code.length + consts_1.INSTALL_CODE_CRC_SIZE);
                code.copy(outCode, 0);
                outCode.writeUInt16LE(crc, code.length);
                return [outCode, "missing CRC"];
            }
            throw new Error(`Install code ${code.toString("hex")} failed CRC validation`);
        }
    }
    // never returned from within the above loop
    throw new Error(`Install code ${code.toString("hex")} has invalid size`);
}
//# sourceMappingURL=installCodes.js.map