"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.getInformationFromSmmFirmwareVersion = exports.smmFirmwareUnencryptedVersions = exports.smmFirmwareSpecialVersions = exports.checkSmmFirmwareInstallationAllowed = exports.bitwiseDecodeNumberVersion = exports.bitwiseEncodeSemverString = exports.parseSmmBinaryHeaders = exports.convertGroupedSmmParametersToTransmitData = exports.getSafetyStatuswordBitNames = exports.getSafetyStatuswordBits = exports.safetyStatuswordBits = exports.safetyStatuswordBitsV2 = exports.safetyStatuswordBitsV1 = exports.smmOdV1DefaultValues = exports.getSmmParametersGroupNames = exports.packSmmSoftwareUpdateCrc = exports.packSmmValidationReport = exports.packSmmDate = exports.packSmmParameterValuesForVerification = exports.unpackSmmParameterValues = exports.packSmmParameterValues = exports.packSmmChangePassword = exports.packSmmCredentials = exports.calcSmmCRC32 = exports.buildSmmParametersPackFormat = exports.buildSmmParametersGroupPackFormats = exports.buildSmmParametersGroupBitmask = exports.resolveSmmOd = void 0;
const tslib_1 = require("tslib");
const buffer_1 = require("buffer");
const python_struct_1 = tslib_1.__importDefault(require("python-struct"));
const semver_1 = require("semver");
const util_1 = require("./util");
// https://docs.google.com/spreadsheets/d/1XYZro2vkAKeuZ2briHCR28taoAz2A7hq/edit?usp=sharing&ouid=110065739563604250219&rtpof=true&sd=true
const smm_od_v1_json_1 = tslib_1.__importDefault(require("./smm-od-v1.json"));
const product_1 = require("./product");
const smm_od_v2_json_1 = tslib_1.__importDefault(require("./smm-od-v2.json"));
/**
 * Resolves the appropriate SMM OD (Object Dictionary) version based on the provided parameter structure version.
 *
 * @param {number} [parameterStructureVersion=0x0041] - The version of the parameter structure to determine which SMM OD to return.
 * @returns {SmmOd} - Returns `smmOdV1` if the version is less than `0x0203`, otherwise returns `smmOdV2`.
 */
function resolveSmmOd(parameterStructureVersion = 0x0041) {
    return (parameterStructureVersion < 0x0203 ? smm_od_v1_json_1.default : smm_od_v2_json_1.default);
}
exports.resolveSmmOd = resolveSmmOd;
/**
 * Builds a bitmask for the safety parameters of a given group in the SMM parameter set.
 *
 * The bitmask is an array of bytes where each bit represents whether a specific safety
 * parameter belongs to the specified group. The group index determines which group's
 * parameters are marked as `1` in the bitmask, and others are marked as `0`.
 *
 * @param groupIndex - The index of the group for which to build the bitmask. The index corresponds
 *                     to the position of the group in the safety parameters.
 * @param parameterStructureVersion - The version of the parameter struct to use. Defaults to `0x0041`.
 *
 * @returns A `Uint8Array` representing the bitmask for the specified group. Each byte contains 8 bits,
 *          where each bit corresponds to a safety parameter.
 *
 * @throws If the `groupIndex` is out of range or if the parameter structure is not valid for the given version.
 */
function buildSmmParametersGroupBitmask(groupIndex, parameterStructureVersion = 0x0041) {
    const smmOd = resolveSmmOd(parameterStructureVersion);
    const groupMap = (0, util_1.mapByProperty)(smmOd.parameters.safety, 'group');
    const groupNames = Array.from(groupMap.keys());
    const bits = [0];
    smmOd.parameters.safety.forEach((parameter) => {
        bits.push(parameter.group === groupNames[groupIndex] ? 1 : 0);
    });
    const uint8Array = new Uint8Array(Math.ceil(bits.length / 8));
    for (let i = 0; i < bits.length; i++) {
        let byte = 0;
        for (let j = 0; j < 8; j++) {
            byte = (byte << 1) | (bits[i + j] || 0);
        }
        uint8Array[i / 8] = byte;
    }
    return uint8Array;
}
exports.buildSmmParametersGroupBitmask = buildSmmParametersGroupBitmask;
/**
 * Builds the structure format strings for each group of parameters based on the given struct version.
 *
 * This function maps parameters in the `smmOd` object to their respective safety groups, processes
 * them, and converts their types to structure format characters (e.g., `<` for little-endian).
 * The function supports different versions of the parameter struct format, defaulting to version `0x0041`.
 *
 * @param parameterStructureVersion - The version of the parameter struct to use. Defaults to `0x0041`.
 *
 * @returns An array of structure format strings for each group of parameters.
 */
function buildSmmParametersGroupPackFormats(parameterStructureVersion = 0x0041) {
    const smmOd = resolveSmmOd(parameterStructureVersion);
    const groupMap = (0, util_1.mapByProperty)(smmOd.parameters.safety, 'group'); // mapByProperty keeps the order of the original array
    const groups = Array.from(groupMap.values());
    let packFormats = groups.map((parameters) => {
        return parameters.reduce((packFormat, parameter) => {
            if (parameter.type === 'enum' && parameter.typeRef) {
                const enumType = smmOd.types[parameter.typeRef];
                packFormat += (0, util_1.convertCTypeToStructFormatCharacter)(enumType.type);
            }
            else {
                packFormat += (0, util_1.convertCTypeToStructFormatCharacter)(parameter.type);
            }
            return packFormat;
        }, '<');
    });
    return packFormats;
}
exports.buildSmmParametersGroupPackFormats = buildSmmParametersGroupPackFormats;
/**
 * Builds the structure format string for all parameters based on the given struct version.
 *
 * This function processes the parameters in the `smmOd` object, converting each parameter's type
 * to its corresponding structure format character (e.g., `<` for little-endian). It supports different
 * versions of the parameter struct format, defaulting to version `0x0041`.
 *
 * @param parameterStructureVersion - The version of the parameter struct to use. Defaults to `0x0041`.
 *
 * @returns A structure format string representing all the parameters, starting with a `<` (little-endian).
 */
function buildSmmParametersPackFormat(parameterStructureVersion = 0x0041) {
    const smmOd = resolveSmmOd(parameterStructureVersion);
    const parameters = smmOd.parameters.safety;
    const packFormat = parameters.reduce((packFormat, parameter) => {
        if (parameter.type === 'enum' && parameter.typeRef) {
            const enumType = smmOd.types[parameter.typeRef];
            packFormat += (0, util_1.convertCTypeToStructFormatCharacter)(enumType.type);
        }
        else {
            packFormat += (0, util_1.convertCTypeToStructFormatCharacter)(parameter.type);
        }
        return packFormat;
    }, '<');
    return packFormat;
}
exports.buildSmmParametersPackFormat = buildSmmParametersPackFormat;
/**
 * Calculates a CRC-32 checksum using a custom SMM-specific polynomial lookup table.
 *
 * This CRC implementation is tailored for Safety Motion Module (SMM) data validation,
 * ensuring data integrity across packed buffers. It supports an initial CRC seed,
 * allowing for chained calculations or non-standard CRC resets.
 *
 * @param buffer - The input data buffer (`Uint8Array`) for which to calculate the CRC.
 * @param initial - The initial CRC value, typically `0xFFFFFFFF` for standard calculations.
 *
 * @returns The computed CRC-32 checksum as a 32-bit unsigned integer.
 */
function calcSmmCRC32(buffer, initial) {
    const crcTable = [
        0x00000000, 0xf4acfb13, 0x1df50d35, 0xe959f626, 0x3bea1a6a, 0xcf46e179, 0x261f175f, 0xd2b3ec4c, 0x77d434d4,
        0x8378cfc7, 0x6a2139e1, 0x9e8dc2f2, 0x4c3e2ebe, 0xb892d5ad, 0x51cb238b, 0xa567d898, 0xefa869a8, 0x1b0492bb,
        0xf25d649d, 0x06f19f8e, 0xd44273c2, 0x20ee88d1, 0xc9b77ef7, 0x3d1b85e4, 0x987c5d7c, 0x6cd0a66f, 0x85895049,
        0x7125ab5a, 0xa3964716, 0x573abc05, 0xbe634a23, 0x4acfb130, 0x2bfc2843, 0xdf50d350, 0x36092576, 0xc2a5de65,
        0x10163229, 0xe4bac93a, 0x0de33f1c, 0xf94fc40f, 0x5c281c97, 0xa884e784, 0x41dd11a2, 0xb571eab1, 0x67c206fd,
        0x936efdee, 0x7a370bc8, 0x8e9bf0db, 0xc45441eb, 0x30f8baf8, 0xd9a14cde, 0x2d0db7cd, 0xffbe5b81, 0x0b12a092,
        0xe24b56b4, 0x16e7ada7, 0xb380753f, 0x472c8e2c, 0xae75780a, 0x5ad98319, 0x886a6f55, 0x7cc69446, 0x959f6260,
        0x61339973, 0x57f85086, 0xa354ab95, 0x4a0d5db3, 0xbea1a6a0, 0x6c124aec, 0x98beb1ff, 0x71e747d9, 0x854bbcca,
        0x202c6452, 0xd4809f41, 0x3dd96967, 0xc9759274, 0x1bc67e38, 0xef6a852b, 0x0633730d, 0xf29f881e, 0xb850392e,
        0x4cfcc23d, 0xa5a5341b, 0x5109cf08, 0x83ba2344, 0x7716d857, 0x9e4f2e71, 0x6ae3d562, 0xcf840dfa, 0x3b28f6e9,
        0xd27100cf, 0x26ddfbdc, 0xf46e1790, 0x00c2ec83, 0xe99b1aa5, 0x1d37e1b6, 0x7c0478c5, 0x88a883d6, 0x61f175f0,
        0x955d8ee3, 0x47ee62af, 0xb34299bc, 0x5a1b6f9a, 0xaeb79489, 0x0bd04c11, 0xff7cb702, 0x16254124, 0xe289ba37,
        0x303a567b, 0xc496ad68, 0x2dcf5b4e, 0xd963a05d, 0x93ac116d, 0x6700ea7e, 0x8e591c58, 0x7af5e74b, 0xa8460b07,
        0x5ceaf014, 0xb5b30632, 0x411ffd21, 0xe47825b9, 0x10d4deaa, 0xf98d288c, 0x0d21d39f, 0xdf923fd3, 0x2b3ec4c0,
        0xc26732e6, 0x36cbc9f5, 0xaff0a10c, 0x5b5c5a1f, 0xb205ac39, 0x46a9572a, 0x941abb66, 0x60b64075, 0x89efb653,
        0x7d434d40, 0xd82495d8, 0x2c886ecb, 0xc5d198ed, 0x317d63fe, 0xe3ce8fb2, 0x176274a1, 0xfe3b8287, 0x0a977994,
        0x4058c8a4, 0xb4f433b7, 0x5dadc591, 0xa9013e82, 0x7bb2d2ce, 0x8f1e29dd, 0x6647dffb, 0x92eb24e8, 0x378cfc70,
        0xc3200763, 0x2a79f145, 0xded50a56, 0x0c66e61a, 0xf8ca1d09, 0x1193eb2f, 0xe53f103c, 0x840c894f, 0x70a0725c,
        0x99f9847a, 0x6d557f69, 0xbfe69325, 0x4b4a6836, 0xa2139e10, 0x56bf6503, 0xf3d8bd9b, 0x07744688, 0xee2db0ae,
        0x1a814bbd, 0xc832a7f1, 0x3c9e5ce2, 0xd5c7aac4, 0x216b51d7, 0x6ba4e0e7, 0x9f081bf4, 0x7651edd2, 0x82fd16c1,
        0x504efa8d, 0xa4e2019e, 0x4dbbf7b8, 0xb9170cab, 0x1c70d433, 0xe8dc2f20, 0x0185d906, 0xf5292215, 0x279ace59,
        0xd336354a, 0x3a6fc36c, 0xcec3387f, 0xf808f18a, 0x0ca40a99, 0xe5fdfcbf, 0x115107ac, 0xc3e2ebe0, 0x374e10f3,
        0xde17e6d5, 0x2abb1dc6, 0x8fdcc55e, 0x7b703e4d, 0x9229c86b, 0x66853378, 0xb436df34, 0x409a2427, 0xa9c3d201,
        0x5d6f2912, 0x17a09822, 0xe30c6331, 0x0a559517, 0xfef96e04, 0x2c4a8248, 0xd8e6795b, 0x31bf8f7d, 0xc513746e,
        0x6074acf6, 0x94d857e5, 0x7d81a1c3, 0x892d5ad0, 0x5b9eb69c, 0xaf324d8f, 0x466bbba9, 0xb2c740ba, 0xd3f4d9c9,
        0x275822da, 0xce01d4fc, 0x3aad2fef, 0xe81ec3a3, 0x1cb238b0, 0xf5ebce96, 0x01473585, 0xa420ed1d, 0x508c160e,
        0xb9d5e028, 0x4d791b3b, 0x9fcaf777, 0x6b660c64, 0x823ffa42, 0x76930151, 0x3c5cb061, 0xc8f04b72, 0x21a9bd54,
        0xd5054647, 0x07b6aa0b, 0xf31a5118, 0x1a43a73e, 0xeeef5c2d, 0x4b8884b5, 0xbf247fa6, 0x567d8980, 0xa2d17293,
        0x70629edf, 0x84ce65cc, 0x6d9793ea, 0x993b68f9,
    ];
    let crc = initial;
    let tblIdx;
    for (let i = 0; i < buffer.length; i++) {
        tblIdx = ((crc >> 24) ^ buffer[i]) & 0xff;
        crc = (crcTable[tblIdx] ^ (crc << 8)) & 0xffffffff;
    }
    return new Uint32Array([crc])[0];
}
exports.calcSmmCRC32 = calcSmmCRC32;
/**
 * Packs SMM (Safety Motion Module) credentials (username and password) into a fixed-size binary format.
 * Each field is padded or truncated to 24 bytes.
 *
 * @param username - The username to be packed. It will be padded or truncated to 24 bytes.
 * @param password - The password to be packed. It will also be padded or truncated to 24 bytes.
 *
 * @returns A `Buffer` containing the packed username and password.
 *
 * @throws Error if the username or password contains invalid characters or types.
 */
function packSmmCredentials(username, password) {
    return python_struct_1.default.pack('<24s24s', username, password);
}
exports.packSmmCredentials = packSmmCredentials;
/**
 * Packs the new and old passwords into a fixed-size binary format for SMM (Safety Motion Module) password change operations.
 * Each password is padded or truncated to 24 bytes.
 *
 * @param newPassword - The new password to be packed. It will be padded or truncated to 24 bytes.
 * @param oldPassword - The current/old password to be packed. It will be padded or truncated to 24 bytes.
 *
 * @returns A `Buffer` containing the packed new and old passwords.
 *
 * @throws Error if either password contains invalid characters or types.
 */
function packSmmChangePassword(newPassword, oldPassword) {
    return python_struct_1.default.pack('<24s24s', newPassword, oldPassword);
}
exports.packSmmChangePassword = packSmmChangePassword;
/**
 * Packs SMM (Safety Motion Module) parameter values into a binary buffer,
 * following the specified SMM parameter structure version.
 * It computes and appends a CRC32 checksum to ensure data integrity.
 *
 * @param values - An array of `DataType` values to be packed.
 * @param parameterStructureVersion - The version of the parameter struct to use. Defaults to `0x0041`.
 *
 * @returns An object containing:
 * - `buffer`: A `Buffer` with the packed values followed by the CRC32 checksum.
 * - `crc`: The computed CRC32 checksum for the packed data.
 * - `crcBuffer`: A `Buffer` representing the packed CRC32 checksum.
 * - `values`: The original `DataType[]` values.
 * - `valuesBuffer`: A `Buffer` containing the packed values (without the CRC).
 *
 * @throws Error if packing fails due to an invalid structure or value mismatch.
 */
function packSmmParameterValues(values, parameterStructureVersion = 0x0041) {
    const packFormat = buildSmmParametersPackFormat(parameterStructureVersion);
    const valuesBuffer = python_struct_1.default.pack(packFormat, values);
    // @ts-ignore
    const crc = calcSmmCRC32(valuesBuffer, 0xffffffff);
    const crcBuffer = python_struct_1.default.pack('<I', crc);
    // @ts-ignore
    const buffer = buffer_1.Buffer.concat([valuesBuffer, crcBuffer]);
    return { buffer, crc, crcBuffer, values, valuesBuffer };
}
exports.packSmmParameterValues = packSmmParameterValues;
/**
 * Unpacks SMM (Safety Motion Module) parameter values from a `Uint8Array` buffer
 * according to the specified parameter structure version.
 *
 * This function decodes the packed buffer back into its original data values,
 * using the structure format defined for the given SMM parameter structure version.
 *
 * @param values - A `Uint8Array` representing the packed parameter values.
 * @param parameterStructureVersion - The version of the parameter struct to use. Defaults to `0x0041`.
 *
 * @returns An array of unpacked values, decoded according to the structure format.
 *
 * @throws Error if the unpacking fails or the data format is incorrect.
 */
function unpackSmmParameterValues(values, parameterStructureVersion = 0x0041) {
    const packFormat = buildSmmParametersPackFormat(parameterStructureVersion);
    const buffer = buffer_1.Buffer.from(values);
    return python_struct_1.default.unpack(packFormat, buffer);
}
exports.unpackSmmParameterValues = unpackSmmParameterValues;
/**
 * Packs SMM (Safety Motion Module) parameter values into a buffer for verification,
 * including a bitmask and CRC32 checksum to ensure data integrity.
 *
 * This function prepares data specifically for verification purposes, combining
 * parameter values, a group-specific bitmask, and a CRC32 checksum.
 *
 * @param values - An array of `DataType` representing the parameter values to be packed.
 * @param groupIndex - The index of the parameter group, determining the bitmask and packing format.
 * @param parameterStructureVersion - The version of the parameter struct to use. Defaults to `0x0041`.
 *
 * @returns An object containing:
 * - `buffer`: A `Uint8Array` combining the bitmask and CRC32 checksum for verification.
 * - `crc`: The CRC32 checksum calculated from the packed data and bitmask.
 *
 * @throws Error if the groupIndex is invalid or packing fails.
 */
function packSmmParameterValuesForVerification(values, groupIndex, parameterStructureVersion = 0x0041) {
    const packFormats = buildSmmParametersGroupPackFormats(parameterStructureVersion);
    const valuesPackFormat = packFormats[groupIndex];
    const valuesBuffer = python_struct_1.default.pack(valuesPackFormat, values);
    const bitmaskBuffer = buildSmmParametersGroupBitmask(groupIndex, parameterStructureVersion);
    // @ts-ignore
    const crcInputBuffer = (0, util_1.mergeUint8Arrays)(valuesBuffer, bitmaskBuffer);
    const crc = calcSmmCRC32(crcInputBuffer, 0xffffffff);
    const crcBuffer = python_struct_1.default.pack('<I', crc);
    // @ts-ignore
    const buffer = (0, util_1.mergeUint8Arrays)(bitmaskBuffer, crcBuffer);
    return { buffer, crc };
}
exports.packSmmParameterValuesForVerification = packSmmParameterValuesForVerification;
/**
 * Packs a `Date` object into a binary buffer in a specific format for the SMM (Safety Motion Module).
 * The date is serialized into the following fields: year (2 bytes), month (1 byte), day (1 byte),
 * hours (1 byte), minutes (1 byte), seconds (1 byte), and a trailing byte (set to 0).
 * The packed data can be used for storing or transmitting the date in a structured format compatible with SMM.
 *
 * @param date - The `Date` object to be packed. It is expected to be in UTC time.
 *
 * @returns A `Buffer` containing the packed date.
 */
function packSmmDate(date) {
    const year = date.getUTCFullYear();
    const month = date.getUTCMonth() + 1;
    const day = date.getUTCDate();
    const hours = date.getUTCHours();
    const minutes = date.getUTCMinutes();
    const seconds = date.getUTCSeconds();
    const buffer = python_struct_1.default.pack('<HBBBBBB', year, month, day, hours, minutes, seconds, 0);
    return buffer;
}
exports.packSmmDate = packSmmDate;
/**
 * Packs a validation report for the SMM (Safety Motion Module), including a CRC, date, and credentials.
 * The function takes a content `Uint8Array`, a `Date`, a `username`, and a `password`, and returns an object
 * containing the packed report, the CRC, and the credentials used for packing.
 *
 * The packed report includes the following components:
 * - CRC32 checksum of the content.
 * - The date packed into a specific format.
 * - The credentials (username and password) packed into a specific format.
 *
 * @param content - The content of the validation report, as a `Uint8Array`.
 * @param date - The date of the report to be packed into the format used by SMM.
 * @param username - The username associated with the report.
 * @param password - The password associated with the report.
 *
 * @returns An object containing:
 * - `buffer`: A `Uint8Array` that includes the packed CRC, date, and credentials.
 * - `crc`: The CRC32 checksum of the `content`.
 * - `credentials`: The packed username and password.
 */
function packSmmValidationReport(content, date, username, password) {
    const crc = calcSmmCRC32(content, 0xffffffff);
    const crcBuffer = python_struct_1.default.pack('<I', crc);
    const reportDate = packSmmDate(date);
    const credentials = packSmmCredentials(username, password);
    // @ts-ignore
    const buffer = (0, util_1.mergeUint8Arrays)(crcBuffer, reportDate, credentials);
    return { buffer, crc, credentials };
}
exports.packSmmValidationReport = packSmmValidationReport;
/**
 * Packs a CRC (Cyclic Redundancy Check) value into a 4-byte little-endian binary format.
 * This is typically used for verifying software updates in the SMM (Safety Motion Module).
 *
 * @param crc - The CRC value to be packed, represented as a 32-bit unsigned integer.
 *
 * @returns A `Buffer` containing the packed CRC value in little-endian format.
 *
 * @throws Error if the provided CRC is not a valid number.
 */
function packSmmSoftwareUpdateCrc(crc) {
    return python_struct_1.default.pack('<I', crc);
}
exports.packSmmSoftwareUpdateCrc = packSmmSoftwareUpdateCrc;
/**
 * Retrieves the names of all safety parameter groups from the specified parameter structure version.
 *
 * The function returns a list of unique group names based on the version of the parameter structure.
 * It will select `smmOdV1` if the version is lower than `0x0203`, and `smmOdV2` otherwise.
 *
 * @param parameterStructureVersion - The version of the parameter struct to use. Defaults to `0x0041`.
 *
 * @returns An array of strings representing the group names in the safety parameters.
 */
function getSmmParametersGroupNames(parameterStructureVersion = 0x0041) {
    const smmOd = (parameterStructureVersion < 0x0203 ? smm_od_v1_json_1.default : smm_od_v2_json_1.default);
    const groupMap = (0, util_1.mapByProperty)(smmOd.parameters.safety, 'group');
    return Array.from(groupMap.keys());
}
exports.getSmmParametersGroupNames = getSmmParametersGroupNames;
exports.smmOdV1DefaultValues = smm_od_v1_json_1.default['parameters']['safety'].map((parameter) => parameter['defaultValue']);
/**
 * Represents an array of safety statusword bits, where each element corresponds
 * to a specific bit within a 4-byte safety statusword. Each entry includes:
 * - the name of the bit,
 * - an optional abbreviation, and
 * - a brief description of its function or purpose.
 *
 * @note This structure is specifically used for SMM LW1.
 */
exports.safetyStatuswordBitsV1 = [
    // Byte 1
    { name: 'STO state', abbrv: 'STO', description: '' },
    { name: '', abbrv: '', description: 'r = reserved' },
    { name: '', abbrv: '', description: 'r = reserved' },
    { name: 'SOS state', abbrv: 'SOS', description: '' },
    { name: '', abbrv: '', description: 'r = reserved' },
    { name: '', abbrv: '', description: 'r = reserved' },
    { name: '', abbrv: '', description: 'r = reserved' },
    { name: 'Error active', description: '' },
    // Byte 2
    { name: 'SS1 state', abbrv: 'SS1', description: '' },
    { name: 'SS2 state', abbrv: 'SS2', description: '' },
    { name: '', abbrv: '', description: 'u = unused' },
    { name: '', abbrv: '', description: 'u = unused' },
    { name: 'SLS Instance 1 state', abbrv: 'SLS1', description: '' },
    { name: 'SLS Instance 2 state', abbrv: 'SLS2', description: '' },
    { name: 'SLS Instance 3 state', abbrv: 'SLS3', description: '' },
    { name: 'SLS Instance 4 state', abbrv: 'SLS4', description: '' },
    // Byte 3
    { name: 'Restart acknowledge required', abbrv: '', description: '' },
    { name: 'SBC command (state)', abbrv: 'SBC', description: '' },
    { name: 'Temperature warning', abbrv: '', description: '' },
    { name: 'Safe position valid', abbrv: '', description: '' },
    { name: 'Safe speed valid', abbrv: '', description: '' },
    { name: '', abbrv: '', description: 'u = unused' },
    { name: '', abbrv: '', description: 'u = unused' },
    { name: '', abbrv: '', description: 'u = unused' },
    // Byte 4
    { name: 'safe input 1', abbrv: '', description: '' },
    { name: 'safe input 2', abbrv: '', description: '' },
    { name: 'safe input 3', abbrv: '', description: '' },
    { name: 'safe input 4', abbrv: '', description: '' },
    { name: 'safe output 1', abbrv: '', description: '' },
    { name: 'safe output 2', abbrv: '', description: '' },
    { name: 'analog input diagnostic', abbrv: '', description: '' },
    { name: 'analog input valid', abbrv: '', description: '' }, // 3.7
];
/**
 * Represents an array of safety statusword bits, where each element corresponds
 * to a specific bit within a 4-byte safety statusword. Each entry provides:
 * - the bit's name,
 * - an optional abbreviation, and
 * - a brief description of its function or purpose.
 *
 * @note This structure is used specifically for SMM LW2.
 */
exports.safetyStatuswordBitsV2 = [
    // Byte 1
    { name: 'STO state', abbrv: 'STO', description: '' },
    { name: '', abbrv: '', description: 'r = reserved' },
    { name: '', abbrv: '', description: 'r = reserved' },
    { name: 'SOS state', abbrv: 'SOS', description: '' },
    { name: '', abbrv: '', description: 'r = reserved' },
    { name: '', abbrv: '', description: 'r = reserved' },
    { name: '', abbrv: '', description: 'r = reserved' },
    { name: 'Error active', description: '' },
    // Byte 2
    { name: 'SS1 state', abbrv: 'SS1', description: '' },
    { name: 'SS2 state', abbrv: 'SS2', description: '' },
    { name: '', abbrv: '', description: 'u = unused' },
    { name: '', abbrv: '', description: 'u = unused' },
    { name: 'SLS Instance 1 state', abbrv: 'SLS1', description: '' },
    { name: 'SLS Instance 2 state', abbrv: 'SLS2', description: '' },
    { name: 'SLS Instance 3 state', abbrv: 'SLS3', description: '' },
    { name: 'SLS Instance 4 state', abbrv: 'SLS4', description: '' },
    // Byte 3
    { name: 'Restart acknowledge required', abbrv: '', description: '' },
    { name: 'SBC command (state)', abbrv: 'SBC', description: '' },
    { name: 'Temperature warning', abbrv: '', description: '' },
    { name: 'Safe position valid', abbrv: '', description: '' },
    { name: 'Safe speed valid', abbrv: '', description: '' },
    { name: 'Safe torque valid', abbrv: '', description: '' },
    { name: 'Safe torque sign valid', abbrv: '', description: '' },
    { name: 'SLT state', abbrv: '', description: '' },
    // Byte 4
    { name: 'safe input 1', abbrv: '', description: '' },
    { name: 'safe input 2', abbrv: '', description: '' },
    { name: 'safe input 3', abbrv: '', description: '' },
    { name: 'safe input 4', abbrv: '', description: '' },
    { name: 'safe output 1', abbrv: '', description: '' },
    { name: 'safe output 2', abbrv: '', description: '' },
    { name: 'analog input diagnostic', abbrv: '', description: '' },
    { name: 'analog input valid', abbrv: '', description: '' }, // 3.7
];
/**
 * Represents an array of safety statusword bits, where each element corresponds
 * to a specific bit within a 4-byte safety statusword. Each entry includes:
 * - the bit's name,
 * - an optional abbreviation, and
 * - a brief description of its function or purpose.
 *
 * @note This structure is intended for devices without SMM.
 */
exports.safetyStatuswordBits = [
    // Byte 1
    { name: 'STO state', abbrv: 'STO', description: '' },
    { name: '', abbrv: '', description: 'u = unused' },
    { name: '', abbrv: '', description: 'u = unused' },
    { name: '', abbrv: '', description: 'u = unused' },
    { name: '', abbrv: '', description: 'u = unused' },
    { name: '', abbrv: '', description: 'u = unused' },
    { name: '', abbrv: '', description: 'u = unused' },
    { name: 'Error active', description: '' },
    // Byte 2
    { name: 'SBC command (state)', abbrv: 'SBC', description: '' },
    { name: '', abbrv: '', description: 'u = unused' },
    { name: '', abbrv: '', description: 'u = unused' },
    { name: '', abbrv: '', description: 'u = unused' },
    { name: '', abbrv: '', description: 'u = unused' },
    { name: '', abbrv: '', description: 'u = unused' },
    { name: '', abbrv: '', description: 'u = unused' },
    { name: '', abbrv: '', description: 'u = unused' }, // 1.7
];
/**
 * Retrieves the safety statusword bits corresponding to the specified parameter structure version.
 *
 * Behavior based on `parameterStructureVersion`:
 * - If `undefined` or not provided, returns the non-SMM bits (`safetyStatuswordBits`).
 * - If less than `0x0203`, returns the LW1 bits (`safetyStatuswordBitsV1`).
 * - If `0x0203` or greater, returns the LW2 bits (`safetyStatuswordBitsV2`).
 *
 * @param parameterStructureVersion - Optional version of the parameter structure used to select
 *                                    which set of statusword bits to return.
 * @returns An array of `SafetyStatuswordBit` objects for the appropriate non-SMM, LW1, or LW2 set.
 */
function getSafetyStatuswordBits(parameterStructureVersion) {
    if (parameterStructureVersion === undefined) {
        return exports.safetyStatuswordBits;
    }
    return parameterStructureVersion < 0x0203 ? exports.safetyStatuswordBitsV1 : exports.safetyStatuswordBitsV2;
}
exports.getSafetyStatuswordBits = getSafetyStatuswordBits;
/**
 * Retrieves the names or descriptions of safety statusword bits based on the parameter structure version.
 *
 * This function returns a 2D array, where each inner array corresponds to a byte in the safety statusword
 * and contains the names or descriptions of the bits in that byte. If a bit has a `name`, it is used;
 * otherwise, the `description` is returned.
 *
 * The function uses `getSafetyStatuswordBits` to select the appropriate set of bits:
 * - If `parameterStructureVersion` is `undefined` or not provided, returns the non-SMM bits (2 bytes).
 * - If less than `0x0203`, returns the LW1 bits (4 bytes).
 * - If `0x0203` or greater, returns the LW2 bits (4 bytes).
 *
 * @param parameterStructureVersion - Optional version of the parameter structure used to select
 *                                    which set of statusword bits to use.
 * @returns A 2D array of strings, where each inner array contains the names or descriptions of the
 *          safety statusword bits for that byte. Non-SMM arrays contain only 2 inner arrays (bytes).
 */
function getSafetyStatuswordBitNames(parameterStructureVersion) {
    return getSafetyStatuswordBits(parameterStructureVersion).reduce((acc, bit, index) => {
        var _a;
        const byteIndex = Math.floor(index / 8);
        if (!acc[byteIndex]) {
            acc[byteIndex] = [];
        }
        acc[byteIndex].push((_a = (bit.name ? bit.name : bit.description)) !== null && _a !== void 0 ? _a : '');
        return acc;
    }, []);
}
exports.getSafetyStatuswordBitNames = getSafetyStatuswordBitNames;
/**
 * Converts a grouped map of SMM parameters/values into a flat array suitable for transmission.
 *
 * During transmission, the Safety-Limited Speed (SLS) groups must be transposed.
 * That is, instead of sending the values grouped by SLS1, SLS2, SLS3, and SLS4 in order,
 * the values are interleaved such that:
 *
 * Input:
 * - SLS1: [a1, a2, a3, ...]
 * - SLS2: [b1, b2, b3, ...]
 * - SLS3: [c1, c2, c3, ...]
 * - SLS4: [d1, d2, d3, ...]
 *
 * Output:
 * - [a1, b1, c1, d1, a2, b2, c2, d2, a3, b3, c3, d3, ...]
 *
 * This function:
 * - Accepts an object mapping group names to arrays of parameters/values (e.g., `"Safety functions (SOS)"`, `"SLS1"`, etc.).
 * - Locates the starting index of the SLS groups by finding the first key that includes `"SLS"`.
 * - Extracts and transposes the four SLS groups into `n` arrays of 4 elements each.
 * - Inserts the transposed data in place of the original SLS groups.
 * - Preserves the order of all other groups before and after the SLS block.
 * - Returns the entire structure as a flattened one-dimensional array.
 *
 * @template T - The type of parameter values contained in each group.
 * @param obj - An object whose keys are group names and values are arrays of parameters/values.
 * @returns A flat array of all parameter values, reordered for transmission.
 */
function convertGroupedSmmParametersToTransmitData(obj) {
    const keys = Object.keys(obj);
    const groupedParameters = Object.values(obj);
    const index = keys.findIndex((key) => key.includes('SLS'));
    let parameters = groupedParameters.slice(0, index);
    const n = groupedParameters[index].length;
    for (let i = 0; i < n; i++) {
        parameters.push(new Array(4).fill(0));
    }
    for (let i = 0; i < 4; i++) {
        for (let j = 0; j < n; j++) {
            parameters[index + j][i] = groupedParameters[index + i][j];
        }
    }
    parameters = parameters.concat(groupedParameters.slice(index + 4));
    return parameters.flat();
}
exports.convertGroupedSmmParametersToTransmitData = convertGroupedSmmParametersToTransmitData;
/**
 * Parses the binary header of an SMM firmware image and extracts structured metadata.
 *
 * This function automatically detects and supports both LW1 and LW2 firmware header versions.
 * It unpacks the corresponding fields and returns an object containing detailed header information,
 * including hardware/software versions, header length, binary lengths, CRCs, and other custom fields.
 *
 * Header version detection:
 * - LW1: header version < 1.0.0
 * - LW2: header version >= 1.0.0
 *
 * Extracted fields include:
 * - `headerVersion`: Encoded numeric header version.
 * - `swVersion`: Encoded software version.
 * - `lengthBinaryA` / `lengthBinaryB`: Lengths of the binary parts.
 * - `crcA` / `crcB`: Checksums of the binary parts (hexadecimal string).
 *
 * @param smmFirmwareBinary - Raw binary data of the SMM firmware as a `Uint8Array`.
 * @returns An `SmmBinaryHeader` object containing the parsed header fields.
 *
 * @throws If the binary header cannot be parsed or is invalid.
 */
function parseSmmBinaryHeaders(smmFirmwareBinary) {
    const header = buffer_1.Buffer.from(smmFirmwareBinary.slice(0, 500));
    const endianness = '<';
    // LW2 fields
    let headerVersionField = `${endianness}H`;
    let customHeaderField = `${endianness}L10s16s174s`;
    let headerField = `${endianness}IHIIII`;
    let headerVersion = python_struct_1.default.unpack(headerVersionField, header)[0];
    // LW1 field (header versions lower than 1.0.0)
    if (headerVersion < 256) {
        customHeaderField = `${endianness}L10s186s`;
    }
    const fields = `${headerVersionField}${customHeaderField}${headerField}`;
    const data = python_struct_1.default.unpack(fields, header);
    if (headerVersion < 256) {
        // LW1 firmware
        const swVersion = data[5];
        const crcA = data[8];
        const crcB = data[9];
        return {
            headerVersion,
            swVersion,
            lengthBinaryA: data[6],
            lengthBinaryB: data[7],
            crcA,
            crcB,
        };
    }
    else {
        // LW2 firmware
        const swVersion = data[6];
        const crcA = data[9];
        const crcB = data[10];
        return {
            headerVersion,
            swVersion,
            lengthBinaryA: data[7],
            lengthBinaryB: data[8],
            crcA,
            crcB,
        };
    }
}
exports.parseSmmBinaryHeaders = parseSmmBinaryHeaders;
/**
 * Encodes a semantic version string or number into a single number using bitwise operations.
 * The encoding packs the major version in the high byte and the minor version in the low byte:
 *
 *   encoded = (major << 8) | minor
 *
 * Examples:
 *   '1.3'  => 0x0103  (259)
 *   '77.0' => 0x4d00  (19712)
 *
 * @param semver - Semantic version as a string (e.g., "1.3") or a numeric code.
 * @returns Bitwise encoded version as a number. Returns 0 if parsing fails.
 */
function bitwiseEncodeSemverString(semver) {
    if (typeof semver === 'string') {
        const version = (0, semver_1.parse)(semver);
        return version ? (version.major << 8) | (version.minor & 0xff) : 0;
    }
    return semver !== null && semver !== void 0 ? semver : 0;
}
exports.bitwiseEncodeSemverString = bitwiseEncodeSemverString;
/**
 * Decodes a number version into a semantic version string using bitwise operations.
 * The decoding unpacks the major version from the high byte and the minor version from the low byte:
 *
 *   decoded = `${(version >> 8) & 0xff}.${(version & 0xff)}`
 *
 * Examples:
 *   '259'  => 0x0103  (1.3)
 *   '19712' => 0x4d00  (77.0)
 *
 * @param version - Bitwise encoded version as a number.
 * @returns Semantic version string (e.g., "1.3"). Returns "0.0" if decoding fails.
 */
function bitwiseDecodeNumberVersion(version) {
    return version ? `${(version >> 8) & 0xff}.${version & 0xff}` : '0.0';
}
exports.bitwiseDecodeNumberVersion = bitwiseDecodeNumberVersion;
/**
 * Determines whether a target SMM firmware version can be installed on a device,
 * based on the device's serial number, its current firmware version, and the target version.
 *
 * Certain SMM firmware versions have special rules:
 * - Versions v77.0, v77.1, and v99.0 are intentionally numbered high to avoid confusion
 *   with versions 0.77.0, 0.77.1, and 0.99.0.
 *
 * Rules:
 * 1. v99.0 can always be installed.
 *    - v77.0 can only be installed on encrypted Circulo 7 devices **after** v99.0.
 *    - v77.1 can only be installed on encrypted Circulo 9 devices **after** v99.0.
 * 2. Versions v1.92 - v1.99 are unencrypted and **cannot** be installed on devices
 *    that already have encrypted firmware.
 * 3. Encrypted firmware versions (>= v1.3) can only be installed on devices with
 *    encrypted firmware, and unencrypted versions can only be installed on devices
 *    with unencrypted firmware.
 *
 * @param deviceSerialNumber - The serial number of the device.
 * @param deviceSmmFirmwareVersion - The current SMM firmware version of the device
 *                                   (number or semver string).
 * @param targetSmmFirmwareVersion - The target SMM firmware version to install
 *                                   (number or semver string).
 * @param force - If `true`, bypasses certain checks and forces the installation.
 * @returns `true` if installation is allowed, `false` otherwise.
 */
function checkSmmFirmwareInstallationAllowed(deviceSerialNumber, deviceSmmFirmwareVersion, targetSmmFirmwareVersion, force = false) {
    var _a;
    const productGroup = (_a = (0, product_1.findSomanetProductById)(deviceSerialNumber)) === null || _a === void 0 ? void 0 : _a.group;
    const deviceSmmVersionInfo = getInformationFromSmmFirmwareVersion(deviceSmmFirmwareVersion);
    const binarySmmVersionInfo = getInformationFromSmmFirmwareVersion(targetSmmFirmwareVersion);
    // Device has v99.0 installed
    if (deviceSmmVersionInfo.version === 25344) {
        // v77.0
        if (binarySmmVersionInfo.version === 19712) {
            return productGroup === 'Circulo 7';
        }
        // v77.1
        if (binarySmmVersionInfo.version === 19713) {
            return productGroup === 'Circulo 9';
        }
        // Allow only encrypted binaries to be installed, unless it's forced
        return binarySmmVersionInfo.isEncrypted || force;
    }
    // Device has v77.0 installed
    if (deviceSmmVersionInfo.version === 19712) {
        return productGroup === 'Circulo 7' && binarySmmVersionInfo.isEncrypted;
    }
    // Device has v77.1 installed
    if (deviceSmmVersionInfo.version === 19713) {
        return productGroup === 'Circulo 9' && binarySmmVersionInfo.isEncrypted;
    }
    // Target is v99.0
    if (binarySmmVersionInfo.version === 25344) {
        return true;
    }
    // Target is v77.0
    if (binarySmmVersionInfo.version === 19712) {
        return false;
    }
    // Target is v77.1
    if (binarySmmVersionInfo.version === 19713) {
        return false;
    }
    // Target is v1.92 - v1.99 (unencrypted)
    if (exports.smmFirmwareUnencryptedVersions.includes(binarySmmVersionInfo.version)) {
        return !deviceSmmVersionInfo.isEncrypted;
    }
    // All other versions: encrypted can only go on encrypted, unencrypted on unencrypted
    return ((deviceSmmVersionInfo.isEncrypted && binarySmmVersionInfo.isEncrypted) ||
        (!deviceSmmVersionInfo.isEncrypted && !binarySmmVersionInfo.isEncrypted));
}
exports.checkSmmFirmwareInstallationAllowed = checkSmmFirmwareInstallationAllowed;
// Special SMM firmware versions: v77.0, v77.1, and v99.0
exports.smmFirmwareSpecialVersions = [19712, 19713, 25344];
// Firmware versions that are *not encrypted*.
// Generally, versions >= 1.3 are encrypted, except for v1.92 - v1.99 and v99.0.
exports.smmFirmwareUnencryptedVersions = [348, 349, 350, 351, 352, 353, 354, 355, 25344];
/**
 * Extracts metadata from a given SMM firmware version, including whether it is a special
 * version and whether it is encrypted.
 *
 * Internally, the firmware version is first encoded using `bitwiseEncodeSemverString`.
 * The function then:
 * 1. Checks if the version is considered "special" (currently v77.0, v77.1, or v99.0).
 * 2. Determines if the firmware is encrypted based on version rules:
 *    - Versions >= 1.3 are encrypted.
 *    - Exceptions: versions v1.92 through v1.99 are not encrypted.
 *
 * @param firmwareVersion - The firmware version to analyze, provided as a number or string.
 * @returns An object containing:
 *   - `version`: The encoded firmware version number.
 *   - `isSpecial`: `true` if the version is in the list of special versions.
 *   - `isEncrypted`: `true` if the version is considered encrypted according to the rules.
 */
function getInformationFromSmmFirmwareVersion(firmwareVersion) {
    const version = bitwiseEncodeSemverString(firmwareVersion);
    const isSpecial = exports.smmFirmwareSpecialVersions.includes(version);
    const isEncrypted = version >= 259 && !exports.smmFirmwareUnencryptedVersions.includes(version);
    return { version, isSpecial, isEncrypted };
}
exports.getInformationFromSmmFirmwareVersion = getInformationFromSmmFirmwareVersion;
//# sourceMappingURL=smm.js.map