import { CodeError, isCodeError } from '@iotize/common/error';
import { FirmwareState } from '@iotize/tap/service/impl/firmware';
import { hexStringToBuffer } from '@iotize/common/byte-converter';
import { StepOperations } from '@iotize/common/task-manager';
import { deepCopy, sleep } from '@iotize/common/utility';
import { SpecialFeature, AppPathType, TapError } from '@iotize/tap';
import { DEFAULT_SCRAM_SALT_GENERATOR, ScramAuth, hashLoginPassword } from '@iotize/tap/auth';
import { TapRequestFrame, ResultCode } from '@iotize/tap/client/api';
import { TapStreamWriter, NumberConverter } from '@iotize/tap/client/impl';
import { variableDataTypeToByteSize, getConverterFromVariableType } from '@iotize/tap/data';
import { CloudPlatform, LowPowerOptimizationLevel, TargetProtocol, HostProtocol, NfcConnectionPriority, WifiKeyVisibility, WifiMode, WifiProtocol, WifiSSIDVisibility, VariableType, converters as converters$1, FirmwareState as FirmwareState$1 } from '@iotize/tap/service/all';
import { VariableMetaConfigConverter } from '@iotize/tap/service/impl/variable';
import { TapConfigJsonSchema, CommonTypeJsonSchema, TapTypeJsonSchema } from '@iotize/tap/config/schema/v1';
import Ajv from 'ajv';
import { SerialSettings, TargetCanProtocolConfiguration } from '@iotize/tap/service/impl/target';
import { converters } from '@iotize/tap/service/core';

/**
 * TODO move to TapConfiguratorError
 */
class ConfigKeyNotSupportedByFirmwareVersion extends Error {
    constructor(configKey, firmwareVersion, constraint) {
        super(`Configuration key "${configKey}" is not available for you firmware version (${firmwareVersion}). \
${constraint.minApiVersion
            ? `Minimal firmware version: ${constraint.minApiVersion}.`
            : ''}\
${constraint.maxApiVersion
            ? `Maximal firmware version: ${constraint.maxApiVersion}.`
            : ''}`);
        this.configKey = configKey;
        this.firmwareVersion = firmwareVersion;
        this.constraint = constraint;
    }
}
class TapConfiguratorError extends CodeError {
    constructor(code, msg, cause) {
        super(msg, code);
        this.cause = cause;
    }
    static internalError(msg, cause) {
        return new TapConfiguratorError(TapConfiguratorError.Code.InternalError, msg, cause);
    }
    static unexpectedFirmwareState(state) {
        return new TapConfiguratorError(TapConfiguratorError.Code.UnexpectedFirmwareState, `Firmware state is not in expected mode. It should be ${FirmwareState.NORMAL} but found ${state}`);
    }
    static invalidConfiguration(message) {
        return new TapConfiguratorError(TapConfiguratorError.Code.InvalidSchema, message);
    }
}
(function (TapConfiguratorError) {
    let Code;
    (function (Code) {
        Code["UnexpectedFirmwareState"] = "TapConfiguratorErrorUnexpectedFirmwareState";
        Code["InvalidSchema"] = "TapConfiguratorErrorIvalidSchema";
        Code["InternalError"] = "TapConfiguratorErrorInternalError";
    })(Code = TapConfiguratorError.Code || (TapConfiguratorError.Code = {}));
    class InvalidSchemaError extends TapConfiguratorError {
        constructor(validate) {
            super(Code.InvalidSchema, `\
Invalid Tap configuration file:
${InvalidSchemaError.ajvErrorsToString(validate.errors)}`);
            this.validate = validate;
            this.errors = this.validate.errors || [];
        }
        static ajvErrorsToString(errors) {
            // console.log('errors', JSON.stringify(errors, undefined, 4));
            return errors
                ? errors
                    .map((error) => {
                    let message;
                    switch (error.keyword) {
                        case 'additionalProperties':
                            const params = error.params;
                            message = `${error.dataPath} should not have additional property "${params.additionalProperty}"`;
                            break;
                        default:
                            message = `${error.dataPath} ${error.message} (keyword: ${error.keyword})`;
                    }
                    return `\t- ${message}`;
                })
                    .join('\n')
                : 'Unknown errors';
        }
    }
    TapConfiguratorError.InvalidSchemaError = InvalidSchemaError;
})(TapConfiguratorError || (TapConfiguratorError = {}));

const ANONYMOUS_USER = {
    username: 'anonymous',
    password: '',
    id: 0,
    lifeTime: 30,
};
const ADMIN_USER = {
    username: 'admin',
    password: 'admin',
    id: 0xffff,
    lifeTime: 30,
};
const SUPERVISOR_USER = {
    username: 'supervisor',
    password: 'supervisor',
    id: 0xfffe,
    lifeTime: 30,
};
const SERIAL_SERVER_USER = {
    username: 'SerialServer',
    password: 'SerialServer',
    lifeTime: 4000,
    id: 0xfffc,
};
const SPECIAL_USERS = [
    ANONYMOUS_USER,
    ADMIN_USER,
    SUPERVISOR_USER,
];
const DEFAULT_HASH_ITERATION_COUNT = 5000;

function getProfileIdFromConfig(profile) {
    const specialUserInfo = SPECIAL_USERS.find((v) => profile.username === v.username);
    if (specialUserInfo) {
        return specialUserInfo.id;
    }
    const profileId = profile['id']; // TODO check
    if (profileId === undefined) {
        throw TapConfiguratorError.invalidConfiguration(`Profile id is required for ${profile['username']}`);
    }
    return profileId;
}
/**
 *
 * @param versionToCheck
 * @param versionToCompareTo
 * @returns
 *   1 if versionToCheck > versionToCompareTo
 *   0 if versionToCheck == versionToCompareTo
 *   -1 if versionToCheck < versionToCompareTo
 */
function compareVersionNumbers(versionToCheck, versionToCompareTo) {
    var v1parts = versionToCheck.split('.').map((v) => {
        const asInt = parseInt(v);
        if (isNaN(asInt)) {
            throw new Error(`Value "${versionToCheck}" is not a valid string version`);
        }
        return asInt;
    });
    var v2parts = versionToCompareTo.split('.').map((v) => {
        const asInt = parseInt(v);
        if (isNaN(asInt)) {
            throw new Error(`Value "${versionToCompareTo}" is not a valid string version`);
        }
        return asInt;
    });
    for (var i = 0; i < v1parts.length; ++i) {
        if (v2parts.length === i) {
            return 1;
        }
        if (v1parts[i] === v2parts[i]) {
            continue;
        }
        if (v1parts[i] > v2parts[i]) {
            return 1;
        }
        return -1;
    }
    if (v1parts.length != v2parts.length) {
        return -1;
    }
    return 0;
}
/**
 * Return true if versionToCheck >= versionToCompareTo
 * @param versionToCheck version to check
 * @param versionToCompareTo version to compare to
 * @returns versionToCheck >= versionToCompareTo
 */
function isTapFirmwareVersionGreaterThanOrEqual(versionToCheck, versionToCompareTo) {
    return compareVersionNumbers(versionToCheck, versionToCompareTo) >= 0;
}
/**
 * Return true if versionToCheck <= versionToCompareTo
 * @param versionToCheck
 * @param versionToCompareTo
 * @returns versionToCheck <= versionToCompareTo
 */
function isTapFirmwareVersionLessThanOrEqual(versionToCheck, versionToCompareTo) {
    return compareVersionNumbers(versionToCheck, versionToCompareTo) <= 0;
}
function enumKeyToEnumValue(value, enumMapping) {
    return toEnum(value, enumMapping);
}
function enumKeysToEnumValues(values, enumMapping) {
    return values.map((value) => enumKeyToEnumValue(value, enumMapping));
}
/**
 * Convert
 */
function toEnum(value, enumMapping) {
    if (!value) {
        throw new Error(`Missing first attribute for toEnum. ${Object.keys(enumMapping).join(', ')}`);
    }
    if (typeof value !== 'string') {
        throw new Error(`Enum key must be a string. Given: ${JSON.stringify(value)}. ${Object.keys(enumMapping).join(', ')}`);
    }
    value = value.toUpperCase();
    if (!(value in enumMapping)) {
        throw new Error(`Missing enum value "${value}". Available: ${Object.keys(enumMapping).join(', ')}`);
    }
    return enumMapping[value];
}
function enumToString(value, mapping) {
    if (!(value in mapping)) {
        throw new Error(`Missing mapping for ${value}. Available: ${Object.keys(mapping).join(', ')}`);
    }
    return mapping[value];
}
function isSpecialProfileUsername(username) {
    return SPECIAL_USERS.filter((v) => username === v.username).length > 0;
}
function isSpecialProfileId(id) {
    return SPECIAL_USERS.filter((v) => id === v.id).length > 0;
}
function configValueToBytes(input) {
    if (input instanceof Uint8Array) {
        return input;
    }
    else if (typeof input === 'string') {
        return hexStringToBuffer(input);
    }
    else {
        return Uint8Array.from(input);
    }
}
function getByAttr(filterKey, v, data) {
    let array;
    if (Array.isArray(data)) {
        array = data;
    }
    else {
        array = Object.keys(data).map((key) => data[key]);
    }
    return array.filter((value) => value[filterKey] == v)[0] || undefined;
}

const TSTYPE_CLASS = {
    Uint8Array,
};
const ɵ0$4 = (schema) => {
    const classObject = TSTYPE_CLASS[schema];
    return function (data) {
        return !classObject || data instanceof classObject;
    };
};
const tsTypeKeyword = {
    compile: ɵ0$4,
};
function getJsonSchemaValidator(schemas) {
    schemas = schemas || [
        TapConfigJsonSchema,
        CommonTypeJsonSchema,
        TapTypeJsonSchema,
    ];
    const ajv = new Ajv({
        schemas,
    });
    ajv.addKeyword('tsType', tsTypeKeyword);
    const schemaId = 'http://iotize.com/schemas/v1/tap-config.json'; // TODO config must match schema api file value
    const validator = ajv.getSchema(schemaId);
    if (!validator) {
        throw new Error(`Cannot create JSON validator function from schema ${schemaId}`);
    }
    return validator;
}
// export function checkErrors(config: Object) {
//     const validator = getJsonSchemaValidator();
//     validator(config);
//     return validator.errors;
// }
/**
 * Validate schema. Throw error if schema is not valid
 * @throws @{link TapConfiguratorError}
 * @param config the tap config object
 * @param schemas optial schema used for validation. If not provided, default one will be used
 *
 * @returns validator function
 */
function validateTapConfiguratorConfig(config, schemas) {
    const validator = getJsonSchemaValidator(schemas);
    const valid = validator(config);
    if (!valid) {
        throw new TapConfiguratorError.InvalidSchemaError(validator);
    }
    return validator;
}
function parseTapConfiguratorConfig(input) {
    let config;
    try {
        config = JSON.parse(input);
    }
    catch (err) {
        throw new Error(`Cannot parse Tap configurator config content. ${err.message}`);
    }
    validateTapConfiguratorConfig(config);
    return config;
}

/**
 *   Generated file. Do not edit
 */
const ɵ0$3 = (context) => {
    return context.service.interface.putApplicationDataCall(configValueToBytes(context.value));
}, ɵ1$2 = (context) => {
    return context.service.interface.putAppNameCall(context.value);
}, ɵ2$1 = (context) => {
    if (context.firmwareVersion) {
        if (!isTapFirmwareVersionGreaterThanOrEqual(context.firmwareVersion, '1.113')) {
            return new ConfigKeyNotSupportedByFirmwareVersion('application.productCode', context.firmwareVersion, {
                minApiVersion: '1.113',
                maxApiVersion: undefined,
            });
        }
    }
    return context.service.interface.putProductCodeCall(context.value);
}, ɵ3$1 = (context) => {
    return context.service.interface.putUniversalLinkCall(context.value);
}, ɵ4$1 = (context) => {
    return context.service.interface.putAppPathCall(context.value);
}, ɵ5$1 = (context) => {
    return context.service.cloud.putDataLogPublishTopicCall(context.value);
}, ɵ6$1 = (context) => {
    return context.service.cloud.putDataLogSubscribeTopicCall(context.value);
}, ɵ7$1 = (context) => {
    return context.service.cloud.putDataLogUploadPeriodCall(context.value);
}, ɵ8$1 = (context) => {
    if (context.firmwareVersion) {
        if (!isTapFirmwareVersionLessThanOrEqual(context.firmwareVersion, '1.103')) {
            return new ConfigKeyNotSupportedByFirmwareVersion('cloud.mqtt.brokerPort', context.firmwareVersion, {
                minApiVersion: undefined,
                maxApiVersion: '1.103',
            });
        }
    }
    return context.service.cloud.putMqttBrokerPortCall(context.value);
}, ɵ9$1 = (context) => {
    if (context.firmwareVersion) {
        if (!isTapFirmwareVersionLessThanOrEqual(context.firmwareVersion, '1.103')) {
            return new ConfigKeyNotSupportedByFirmwareVersion('cloud.mqtt.clientId', context.firmwareVersion, {
                minApiVersion: undefined,
                maxApiVersion: '1.103',
            });
        }
    }
    return context.service.cloud.putMqttClientIdCall(context.value);
}, ɵ10$1 = (context) => {
    if (context.firmwareVersion) {
        if (!isTapFirmwareVersionLessThanOrEqual(context.firmwareVersion, '1.103')) {
            return new ConfigKeyNotSupportedByFirmwareVersion('cloud.mqtt.password', context.firmwareVersion, {
                minApiVersion: undefined,
                maxApiVersion: '1.103',
            });
        }
    }
    return context.service.cloud.putMqttPasswordCall(context.value);
}, ɵ11$1 = (context) => {
    if (context.firmwareVersion) {
        if (!isTapFirmwareVersionLessThanOrEqual(context.firmwareVersion, '1.103')) {
            return new ConfigKeyNotSupportedByFirmwareVersion('cloud.mqtt.username', context.firmwareVersion, {
                minApiVersion: undefined,
                maxApiVersion: '1.103',
            });
        }
    }
    return context.service.cloud.putMqttUsernameCall(context.value);
}, ɵ12$1 = (context) => {
    return context.service.cloud.putPlatformCall(enumKeyToEnumValue(context.value, CloudPlatform));
}, ɵ13$1 = (context) => {
    return context.service.bundle.createCall(context.param);
}, ɵ14$1 = (context) => {
    return context.service.bundle.putAclCall(context.param, context.value);
}, ɵ15$1 = (context) => {
    return context.service.bundle.putDataLogPeriodCall(context.param, context.value);
}, ɵ16$1 = (context) => {
    return context.service.bundle.putNameCall(context.param, context.value);
}, ɵ17$1 = (context) => {
    if (context.firmwareVersion) {
        if (!isTapFirmwareVersionGreaterThanOrEqual(context.firmwareVersion, '1.23')) {
            return new ConfigKeyNotSupportedByFirmwareVersion('data.features[].bundleId', context.firmwareVersion, {
                minApiVersion: '1.23',
                maxApiVersion: undefined,
            });
        }
    }
    return context.service.interface.setSpecialFeatureProfileCall(context.param, context.value);
}, ɵ18$1 = (context) => {
    return context.service.group.createCall(context.param);
}, ɵ19$1 = (context) => {
    return context.service.group.putAliasCall(context.param, context.value);
}, ɵ20$1 = (context) => {
    return context.service.group.changePasswordKeyCall(context.param, configValueToBytes(context.value));
}, ɵ21$1 = (context) => {
    return context.service.group.putSessionLifetimeCall(context.param, context.value);
}, ɵ22$1 = (context) => {
    return context.service.group.changePasswordCall(context.param, context.value);
}, ɵ23$1 = (context) => {
    return context.service.group.putNameCall(context.param, context.value);
}, ɵ24$1 = (context) => {
    return context.service.variable.createCall(context.param, context.value);
}, ɵ25$1 = (context) => {
    return context.service.variable.putVariableAddressCall(context.param, context.value);
}, ɵ26$1 = (context) => {
    return context.service.variable.putBundleIdCall(context.param, context.value);
}, ɵ27$1 = (context) => {
    return context.service.variable.putNumberOfElementsCall(context.param, context.value);
}, ɵ28$1 = (context) => {
    if (context.firmwareVersion) {
        if (!isTapFirmwareVersionGreaterThanOrEqual(context.firmwareVersion, '1.73')) {
            return new ConfigKeyNotSupportedByFirmwareVersion('data.variables[].meta', context.firmwareVersion, {
                minApiVersion: '1.73',
                maxApiVersion: undefined,
            });
        }
    }
    return context.service.variable.putMetaCall(context.param, context.value);
}, ɵ29$1 = (context) => {
    return context.service.variable.putModbusVariableAddressCall(context.param, context.value);
}, ɵ30$1 = (context) => {
    if (context.firmwareVersion) {
        if (!isTapFirmwareVersionGreaterThanOrEqual(context.firmwareVersion, '1.73')) {
            return new ConfigKeyNotSupportedByFirmwareVersion('data.variables[].rawMeta', context.firmwareVersion, {
                minApiVersion: '1.73',
                maxApiVersion: undefined,
            });
        }
    }
    return context.service.variable.putRawMetaCall(context.param, configValueToBytes(context.value));
}, ɵ31$1 = (context) => {
    return context.service.variable.putTypeCall(context.param, context.value);
}, ɵ32$1 = (context) => {
    return context.service.variable.setValueCall(context.param, configValueToBytes(context.value));
}, ɵ33$1 = (context) => {
    if (context.firmwareVersion) {
        if (!isTapFirmwareVersionGreaterThanOrEqual(context.firmwareVersion, '1.90')) {
            return new ConfigKeyNotSupportedByFirmwareVersion('jvm.code', context.firmwareVersion, {
                minApiVersion: '1.90',
                maxApiVersion: undefined,
            });
        }
    }
    return context.service.jvm.appendCodeAppendCalls(configValueToBytes(context.value));
}, ɵ34$1 = (context) => {
    return context.service.jvm.putProfileIdCall(context.value);
}, ɵ35$1 = (context) => {
    if (context.firmwareVersion) {
        if (!isTapFirmwareVersionGreaterThanOrEqual(context.firmwareVersion, '2.7')) {
            return new ConfigKeyNotSupportedByFirmwareVersion('ports.ethernet.gatewayIp', context.firmwareVersion, {
                minApiVersion: '2.7',
                maxApiVersion: undefined,
            });
        }
    }
    return context.service.ethernet.putGatewayIpCall(context.value);
}, ɵ36$1 = (context) => {
    if (context.firmwareVersion) {
        if (!isTapFirmwareVersionGreaterThanOrEqual(context.firmwareVersion, '2.7')) {
            return new ConfigKeyNotSupportedByFirmwareVersion('ports.ethernet.ip', context.firmwareVersion, {
                minApiVersion: '2.7',
                maxApiVersion: undefined,
            });
        }
    }
    return context.service.ethernet.putIpCall(context.value);
}, ɵ37$1 = (context) => {
    if (context.firmwareVersion) {
        if (!isTapFirmwareVersionGreaterThanOrEqual(context.firmwareVersion, '2.7')) {
            return new ConfigKeyNotSupportedByFirmwareVersion('ports.ethernet.ipMask', context.firmwareVersion, {
                minApiVersion: '2.7',
                maxApiVersion: undefined,
            });
        }
    }
    return context.service.ethernet.putIpMaskCall(context.value);
}, ɵ38$1 = (context) => {
    if (context.firmwareVersion) {
        if (!isTapFirmwareVersionGreaterThanOrEqual(context.firmwareVersion, '1.86')) {
            return new ConfigKeyNotSupportedByFirmwareVersion('tap.certificate.privateKey', context.firmwareVersion, {
                minApiVersion: '1.86',
                maxApiVersion: undefined,
            });
        }
    }
    return context.service.interface.putCertificatePrivateKeyAppendCalls(configValueToBytes(context.value));
}, ɵ39$1 = (context) => {
    if (context.firmwareVersion) {
        if (!isTapFirmwareVersionGreaterThanOrEqual(context.firmwareVersion, '1.86')) {
            return new ConfigKeyNotSupportedByFirmwareVersion('tap.certificate.publicKey', context.firmwareVersion, {
                minApiVersion: '1.86',
                maxApiVersion: undefined,
            });
        }
    }
    return context.service.interface.putCertificatePublicKeyAppendCalls(configValueToBytes(context.value));
}, ɵ40$1 = (context) => {
    if (context.firmwareVersion) {
        if (!isTapFirmwareVersionGreaterThanOrEqual(context.firmwareVersion, '1.86')) {
            return new ConfigKeyNotSupportedByFirmwareVersion('tap.certificate.rootKey', context.firmwareVersion, {
                minApiVersion: '1.86',
                maxApiVersion: undefined,
            });
        }
    }
    return context.service.interface.putCertificateRootKeyAppendCalls(configValueToBytes(context.value));
}, ɵ41$1 = (context) => {
    return context.service.interface.putPowerOptimisationLevelCall(enumKeyToEnumValue(context.value, LowPowerOptimizationLevel));
}, ɵ42$1 = (context) => {
    if (context.firmwareVersion) {
        if (!isTapFirmwareVersionGreaterThanOrEqual(context.firmwareVersion, '1.11')) {
            return new ConfigKeyNotSupportedByFirmwareVersion('tap.scram.hashIteration', context.firmwareVersion, {
                minApiVersion: '1.11',
                maxApiVersion: undefined,
            });
        }
    }
    return context.service.scram.putHashIterationCall(context.value);
}, ɵ43$1 = (context) => {
    return context.service.interface.putSecurityOptionsCall(context.value);
}, ɵ44$1 = (context) => {
    if (context.firmwareVersion) {
        if (!isTapFirmwareVersionGreaterThanOrEqual(context.firmwareVersion, '1.107')) {
            return new ConfigKeyNotSupportedByFirmwareVersion('tap.time.ntpServer', context.firmwareVersion, {
                minApiVersion: '1.107',
                maxApiVersion: undefined,
            });
        }
    }
    return context.service.time.putNtpServerCall(context.value);
}, ɵ45$1 = (context) => {
    if (context.firmwareVersion) {
        if (!isTapFirmwareVersionGreaterThanOrEqual(context.firmwareVersion, '1.107')) {
            return new ConfigKeyNotSupportedByFirmwareVersion('tap.time.zone', context.firmwareVersion, {
                minApiVersion: '1.107',
                maxApiVersion: undefined,
            });
        }
    }
    return context.service.time.putTimeZoneCall(context.value);
}, ɵ46$1 = (context) => {
    return context.service.target.putFirmwareVersionCall(context.value);
}, ɵ47$1 = (context) => {
    return context.service.target.putProtocolCall(enumKeyToEnumValue(context.value, TargetProtocol));
}, ɵ48$1 = (context) => {
    return context.service.interface.putConfigVersionCall(context.value);
}, ɵ49$1 = (context) => {
    return context.service.interface.putAuthorizedHostProtocolCall(enumKeysToEnumValues(context.value, HostProtocol));
}, ɵ50$1 = (context) => {
    return context.service.interface.putHostInactivityPeriodCall(context.value);
}, ɵ51$1 = (context) => {
    return context.service.interface.putPairingOptionsCall(context.value);
}, ɵ52$1 = (context) => {
    return context.service.lora.putConfigCall(context.value);
}, ɵ53$1 = (context) => {
    return context.service.lora.putIdsCall(configValueToBytes(context.value));
}, ɵ54$1 = (context) => {
    if (context.firmwareVersion) {
        if (!isTapFirmwareVersionGreaterThanOrEqual(context.firmwareVersion, '1.86')) {
            return new ConfigKeyNotSupportedByFirmwareVersion('wireless.protocols.mqtt.brokerCertificate', context.firmwareVersion, {
                minApiVersion: '1.86',
                maxApiVersion: undefined,
            });
        }
    }
    return context.service.mqtt.putBrokerCertificateAppendCalls(configValueToBytes(context.value));
}, ɵ55$1 = (context) => {
    return context.service.mqtt.putBrokerHostnameCall(context.value);
}, ɵ56$1 = (context) => {
    return context.service.mqtt.putBrokerPortCall(context.value);
}, ɵ57$1 = (context) => {
    if (context.firmwareVersion) {
        if (!isTapFirmwareVersionGreaterThanOrEqual(context.firmwareVersion, '1.86')) {
            return new ConfigKeyNotSupportedByFirmwareVersion('wireless.protocols.mqtt.clientId', context.firmwareVersion, {
                minApiVersion: '1.86',
                maxApiVersion: undefined,
            });
        }
    }
    return context.service.mqtt.putClientIdCall(context.value);
}, ɵ58$1 = (context) => {
    return context.service.mqtt.putPasswordCall(context.value);
}, ɵ59$1 = (context) => {
    return context.service.mqtt.putRelayNetKeyCall(context.value);
}, ɵ60$1 = (context) => {
    if (context.firmwareVersion) {
        if (!isTapFirmwareVersionGreaterThanOrEqual(context.firmwareVersion, '1.86')) {
            return new ConfigKeyNotSupportedByFirmwareVersion('wireless.protocols.mqtt.requestTopic', context.firmwareVersion, {
                minApiVersion: '1.86',
                maxApiVersion: undefined,
            });
        }
    }
    return context.service.mqtt.putRequestTopicCall(context.value);
}, ɵ61 = (context) => {
    if (context.firmwareVersion) {
        if (!isTapFirmwareVersionGreaterThanOrEqual(context.firmwareVersion, '1.107')) {
            return new ConfigKeyNotSupportedByFirmwareVersion('wireless.protocols.mqtt.responseTopic', context.firmwareVersion, {
                minApiVersion: '1.107',
                maxApiVersion: undefined,
            });
        }
    }
    return context.service.mqtt.putResponseTopicCall(context.value);
}, ɵ62 = (context) => {
    if (context.firmwareVersion) {
        if (!isTapFirmwareVersionGreaterThanOrEqual(context.firmwareVersion, '1.86')) {
            return new ConfigKeyNotSupportedByFirmwareVersion('wireless.protocols.mqtt.topicPrefix', context.firmwareVersion, {
                minApiVersion: '1.86',
                maxApiVersion: undefined,
            });
        }
    }
    return context.service.mqtt.putTopicPrefixCall(context.value);
}, ɵ63 = (context) => {
    if (context.firmwareVersion) {
        if (!isTapFirmwareVersionGreaterThanOrEqual(context.firmwareVersion, '1.86')) {
            return new ConfigKeyNotSupportedByFirmwareVersion('wireless.protocols.mqtt.username', context.firmwareVersion, {
                minApiVersion: '1.86',
                maxApiVersion: undefined,
            });
        }
    }
    return context.service.mqtt.putUsernameCall(context.value);
}, ɵ64 = (context) => {
    if (context.firmwareVersion) {
        if (!isTapFirmwareVersionGreaterThanOrEqual(context.firmwareVersion, '1.55')) {
            return new ConfigKeyNotSupportedByFirmwareVersion('wireless.protocols.nfc.autoLogProfileId', context.firmwareVersion, {
                minApiVersion: '1.55',
                maxApiVersion: undefined,
            });
        }
    }
    return context.service.interface.putNfcAutologProfileIdCall(context.value);
}, ɵ65 = (context) => {
    if (context.firmwareVersion) {
        if (!isTapFirmwareVersionGreaterThanOrEqual(context.firmwareVersion, '1.85')) {
            return new ConfigKeyNotSupportedByFirmwareVersion('wireless.protocols.nfc.connectionPriority', context.firmwareVersion, {
                minApiVersion: '1.85',
                maxApiVersion: undefined,
            });
        }
    }
    return context.service.interface.putNfcConnectionPriorityCall(enumKeyToEnumValue(context.value, NfcConnectionPriority));
}, ɵ66 = (context) => {
    if (context.firmwareVersion) {
        if (!isTapFirmwareVersionGreaterThanOrEqual(context.firmwareVersion, '1.88')) {
            return new ConfigKeyNotSupportedByFirmwareVersion('wireless.protocols.wifi.countryCode', context.firmwareVersion, {
                minApiVersion: '1.88',
                maxApiVersion: undefined,
            });
        }
    }
    return context.service.wifi.putCountryCodeCall(context.value);
}, ɵ67 = (context) => {
    return context.service.wifi.putGatewayIpCall(context.value);
}, ɵ68 = (context) => {
    return context.service.wifi.putIpCall(context.value);
}, ɵ69 = (context) => {
    return context.service.wifi.putKeyCall(context.value);
}, ɵ70 = (context) => {
    if (context.firmwareVersion) {
        if (!isTapFirmwareVersionGreaterThanOrEqual(context.firmwareVersion, '1.88')) {
            return new ConfigKeyNotSupportedByFirmwareVersion('wireless.protocols.wifi.keyVisibility', context.firmwareVersion, {
                minApiVersion: '1.88',
                maxApiVersion: undefined,
            });
        }
    }
    return context.service.wifi.putKeyVisibilityCall(enumKeyToEnumValue(context.value, WifiKeyVisibility));
}, ɵ71 = (context) => {
    return context.service.wifi.putIpMaskCall(context.value);
}, ɵ72 = (context) => {
    return context.service.wifi.putModeCall(enumKeyToEnumValue(context.value, WifiMode));
}, ɵ73 = (context) => {
    if (context.firmwareVersion) {
        if (!isTapFirmwareVersionGreaterThanOrEqual(context.firmwareVersion, '1.88')) {
            return new ConfigKeyNotSupportedByFirmwareVersion('wireless.protocols.wifi.protocol', context.firmwareVersion, {
                minApiVersion: '1.88',
                maxApiVersion: undefined,
            });
        }
    }
    return context.service.wifi.putProtocolCall(enumKeysToEnumValues(context.value, WifiProtocol));
}, ɵ74 = (context) => {
    return context.service.wifi.putSSIDCall(context.value);
}, ɵ75 = (context) => {
    if (context.firmwareVersion) {
        if (!isTapFirmwareVersionGreaterThanOrEqual(context.firmwareVersion, '1.88')) {
            return new ConfigKeyNotSupportedByFirmwareVersion('wireless.protocols.wifi.ssidVisibility', context.firmwareVersion, {
                minApiVersion: '1.88',
                maxApiVersion: undefined,
            });
        }
    }
    return context.service.wifi.putSSIDVisibilityCall(enumKeyToEnumValue(context.value, WifiSSIDVisibility));
}, ɵ76 = (context) => {
    if (context.firmwareVersion) {
        if (!isTapFirmwareVersionGreaterThanOrEqual(context.firmwareVersion, '1.88')) {
            return new ConfigKeyNotSupportedByFirmwareVersion('wireless.protocols.wifi.txPower', context.firmwareVersion, {
                minApiVersion: '1.88',
                maxApiVersion: undefined,
            });
        }
    }
    return context.service.wifi.putTxPowerCall(context.value);
};
const WRITE_CONFIGURATOR_CONFIG$1 = {
    'application.data': ɵ0$3,
    'application.name': ɵ1$2,
    'application.productCode': ɵ2$1,
    'application.universalLink': ɵ3$1,
    'application.uri': ɵ4$1,
    'cloud.dataLog.mqttPublishTopic': ɵ5$1,
    'cloud.dataLog.mqttSubscribeTopic': ɵ6$1,
    'cloud.dataLog.uploadPeriod': ɵ7$1,
    'cloud.mqtt.brokerPort': ɵ8$1,
    'cloud.mqtt.clientId': ɵ9$1,
    'cloud.mqtt.password': ɵ10$1,
    'cloud.mqtt.username': ɵ11$1,
    'cloud.platform': ɵ12$1,
    'data.bundles[]': ɵ13$1,
    'data.bundles[].acl[].$': ɵ14$1,
    'data.bundles[].dataLog.period': ɵ15$1,
    'data.bundles[].name': ɵ16$1,
    'data.features[].bundleId': ɵ17$1,
    'data.profiles[]': ɵ18$1,
    'data.profiles[].alias': ɵ19$1,
    'data.profiles[].key': ɵ20$1,
    'data.profiles[].lifeTime': ɵ21$1,
    'data.profiles[].password': ɵ22$1,
    'data.profiles[].username': ɵ23$1,
    'data.variables[]': ɵ24$1,
    'data.variables[].address': ɵ25$1,
    'data.variables[].bundleId': ɵ26$1,
    'data.variables[].length': ɵ27$1,
    'data.variables[].meta': ɵ28$1,
    'data.variables[].modbusAddress': ɵ29$1,
    'data.variables[].rawMeta': ɵ30$1,
    'data.variables[].type': ɵ31$1,
    'data.variables[].value': ɵ32$1,
    'jvm.code': ɵ33$1,
    'jvm.profileId': ɵ34$1,
    'ports.ethernet.gatewayIp': ɵ35$1,
    'ports.ethernet.ip': ɵ36$1,
    'ports.ethernet.ipMask': ɵ37$1,
    'tap.certificate.privateKey': ɵ38$1,
    'tap.certificate.publicKey': ɵ39$1,
    'tap.certificate.rootKey': ɵ40$1,
    'tap.powerOptimization': ɵ41$1,
    'tap.scram.hashIteration': ɵ42$1,
    'tap.security': ɵ43$1,
    'tap.time.ntpServer': ɵ44$1,
    'tap.time.zone': ɵ45$1,
    'target.firmwareVersion': ɵ46$1,
    'target.protocol.type': ɵ47$1,
    version: ɵ48$1,
    'wireless.options.authorizedProtocols': ɵ49$1,
    'wireless.options.inactivityTimeout': ɵ50$1,
    'wireless.options.pairing': ɵ51$1,
    'wireless.protocols.lora.config': ɵ52$1,
    'wireless.protocols.lora.ids': ɵ53$1,
    'wireless.protocols.mqtt.brokerCertificate': ɵ54$1,
    'wireless.protocols.mqtt.brokerHostname': ɵ55$1,
    'wireless.protocols.mqtt.brokerPort': ɵ56$1,
    'wireless.protocols.mqtt.clientId': ɵ57$1,
    'wireless.protocols.mqtt.password': ɵ58$1,
    'wireless.protocols.mqtt.relayNetKey': ɵ59$1,
    'wireless.protocols.mqtt.requestTopic': ɵ60$1,
    'wireless.protocols.mqtt.responseTopic': ɵ61,
    'wireless.protocols.mqtt.topicPrefix': ɵ62,
    'wireless.protocols.mqtt.username': ɵ63,
    'wireless.protocols.nfc.autoLogProfileId': ɵ64,
    'wireless.protocols.nfc.connectionPriority': ɵ65,
    'wireless.protocols.wifi.countryCode': ɵ66,
    'wireless.protocols.wifi.gatewayIp': ɵ67,
    'wireless.protocols.wifi.ip': ɵ68,
    'wireless.protocols.wifi.key': ɵ69,
    'wireless.protocols.wifi.keyVisibility': ɵ70,
    'wireless.protocols.wifi.mask': ɵ71,
    'wireless.protocols.wifi.mode': ɵ72,
    'wireless.protocols.wifi.protocol': ɵ73,
    'wireless.protocols.wifi.ssid': ɵ74,
    'wireless.protocols.wifi.ssidVisibility': ɵ75,
    'wireless.protocols.wifi.txPower': ɵ76,
};

const ɵ0$2 = (context) => {
    var _a, _b, _c, _d, _e;
    const protocolType = ((_b = (_a = context.config) === null || _a === void 0 ? void 0 : _a.target) === null || _b === void 0 ? void 0 : _b.type) === 'MCU'
        ? (_e = (_d = (_c = context.config) === null || _c === void 0 ? void 0 : _c.target) === null || _d === void 0 ? void 0 : _d.protocol) === null || _e === void 0 ? void 0 : _e.type
        : 'MODBUS';
    switch (protocolType) {
        case 'NONE':
        case 'SWD':
        // case 'JTAG':
        case 'GPIO':
        case 'CUSTOM':
            return TapConfiguratorError.invalidConfiguration(`Target protocol "${protocolType}" does not accept any additional configuration`);
        case 'S3P':
            return context.service.target.putS3PConfigurationCall(context.value);
        case 'MODBUS_TCP':
            if (context.firmwareVersion) {
                if (!isTapFirmwareVersionGreaterThanOrEqual(context.firmwareVersion, '2.6')) {
                    return new ConfigKeyNotSupportedByFirmwareVersion('target.protocol.config', context.firmwareVersion, {
                        minApiVersion: '2.6',
                        maxApiVersion: undefined,
                    });
                }
            }
            return context.service.target.putModbusTcpConfigurationCall(context.value);
        case 'MODBUS':
        case 'SERIAL':
            const tapnpassConfigJson = context.value;
            const tapnpassConfigApi = tapnpassConfigJsonToApi(tapnpassConfigJson);
            return context.service.target.putSerialModbusConfigurationCall(tapnpassConfigApi);
        case 'CAN':
            if (context.firmwareVersion) {
                if (!isTapFirmwareVersionGreaterThanOrEqual(context.firmwareVersion, '2.6')) {
                    return new ConfigKeyNotSupportedByFirmwareVersion('target.protocol.config', context.firmwareVersion, {
                        minApiVersion: '2.6',
                        maxApiVersion: undefined,
                    });
                }
            }
            const canConfigJson = context.value;
            const canConfigApi = canConfigJsonToApi(canConfigJson);
            return context.service.target.putCanConfigurationCall(canConfigApi);
        default:
            return TapConfiguratorError.invalidConfiguration(`Target protocol "${protocolType}" is not supported by this version of the configurator`);
    }
}, ɵ1$1 = (context) => {
    var _a;
    if (context.firmwareVersion) {
        if (!isTapFirmwareVersionGreaterThanOrEqual(context.firmwareVersion, '2.6' // TODO check version
        )) {
            return new ConfigKeyNotSupportedByFirmwareVersion('slaveServer', context.firmwareVersion, {
                minApiVersion: '2.6',
                maxApiVersion: undefined,
            });
        }
    }
    const protocolType = (_a = context.value) === null || _a === void 0 ? void 0 : _a.type;
    switch (protocolType) {
        case undefined:
            throw new Error(`Undefined property`);
        case 'SPI':
            return context.service.slaveServer.putSpiProtocolConfigurationCall();
        case 'MODBUS_TCP':
            const modbusTcpConfig = context.value
                .config;
            return context.service.slaveServer.putModbusTcpProtocolConfigurationCall(modbusTcpConfig);
        case 'MODBUS_RTU':
            const tapnpassConfigJson = context.value
                .config;
            const tapnpassConfigApi = tapnpassConfigJsonToApi(tapnpassConfigJson);
            return context.service.slaveServer.putModbusRtuProtocolConfigurationCall(tapnpassConfigApi);
        case 'CAN':
            const canConfigJson = context.value.config;
            const canConfigApi = canConfigJsonToApi(canConfigJson);
            return context.service.slaveServer.putCanProtocolConfigurationCall(canConfigApi);
        default:
            return TapConfiguratorError.invalidConfiguration(`Slave server protocol "${protocolType}" is not supported by this version of the configurator`);
    }
};
const WRITE_CONFIGURATOR_CUSTOM_CONFIG = {
    'target.protocol.config': ɵ0$2,
    slaveServer: ɵ1$1,
};
const WRITE_CONFIGURATOR_CONFIG = Object.assign(Object.assign({}, WRITE_CONFIGURATOR_CONFIG$1), WRITE_CONFIGURATOR_CUSTOM_CONFIG);
function tapnpassConfigJsonToApi(jsonConfig) {
    return {
        modbus: jsonConfig.modbus,
        serial: {
            baudRate: jsonConfig.serial.baudRate,
            bitParity: toEnum(jsonConfig.serial.bitParity, SerialSettings.BitParity),
            dataBits: toEnum(jsonConfig.serial.dataBits, SerialSettings.DataBits),
            handshakeDelimiter: toEnum(jsonConfig.serial.handshakeDelimiter, SerialSettings.HandshakeDelimiter),
            hardwareFlowControl: toEnum(jsonConfig.serial.hardwareFlowControl, SerialSettings.HardwareFlowControl),
            identification: jsonConfig.serial.identification,
            ledStatus: jsonConfig.serial.ledStatus,
            physicalPort: toEnum(jsonConfig.serial.physicalPort, SerialSettings.PhysicalPort),
            stopBits: toEnum(jsonConfig.serial.stopBits, SerialSettings.StopBits),
            timeout: jsonConfig.serial.timeout,
        },
        usb: jsonConfig.usb,
    };
}
function canConfigJsonToApi(canConfigJson) {
    const canConfigApi = {
        code: converters.uint32.decode(configValueToBytes(canConfigJson.code)),
        mask: converters.uint32.decode(configValueToBytes(canConfigJson.mask)),
        filter: toEnum(canConfigJson.filter, TargetCanProtocolConfiguration.Filter),
        frequency: canConfigJson.frequency,
        mode: toEnum(canConfigJson.mode, TargetCanProtocolConfiguration.Mode),
    };
    return canConfigApi;
}

var __awaiter$1 = (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());
    });
};
const variableMetaConverter = new VariableMetaConfigConverter();
const IGNORE_KEYS = ['version', 'tap.security'];
const USER_CREATE_KEY = 'data.profiles[]';
const CREATE_VARIABLE_CONFIG_KEY = 'data.variables[]';
function isHexString(input) {
    if (typeof input !== 'string') {
        return false;
    }
    return !!input.match(/^(0x)?([a-fA-F0-9])*$/);
}
function tapConfigToServiceCalls(config, options) {
    var _a;
    function addCalls(calls, newCalls) {
        if (Array.isArray(newCalls)) {
            calls.push(...newCalls);
        }
        else {
            calls.push(newCalls);
        }
    }
    function createWriteConfigCall(key, callContext) {
        if (!(key in WRITE_CONFIGURATOR_CONFIG)) {
            throw new Error(`Invalid API configuration. Configuration key "${key}" does not exist`);
        }
        const callFactory = WRITE_CONFIGURATOR_CONFIG[key];
        return callFactory(callContext);
    }
    function baseConfigObjectToCalls(entry, configKey, pathParameter) {
        const calls = [];
        if (entry === undefined) {
            return calls;
        }
        else if (Array.isArray(entry)) {
            const newConfigKey = configKey + '[]';
            for (const itemValue of entry) {
                if (itemValue.id || itemValue.id === 0) {
                    pathParameter = itemValue.id;
                }
                if (pathParameter === undefined) {
                    throw new Error(`Item must have an id for key "${newConfigKey}`);
                    // TODO we need to know parent profile ?
                }
                // If there is a create instance function
                if (newConfigKey in WRITE_CONFIGURATOR_CONFIG) {
                    if (newConfigKey === USER_CREATE_KEY &&
                        isSpecialProfileId(pathParameter)) {
                        // If it's a special user profile, we dont need the create call
                    }
                    else {
                        // throw new Error(`Missing create method for config key "${newConfigKey}"`);
                        let createBody;
                        if (newConfigKey === CREATE_VARIABLE_CONFIG_KEY) {
                            if (itemValue.metaDataLength) {
                                createBody = itemValue.metaDataLength;
                            }
                            else if (itemValue.rawMeta) {
                                createBody = itemValue.rawMeta.length;
                            }
                        }
                        const createCallParam = {
                            firmwareVersion: options.firmwareVersion,
                            service: options.service,
                            value: createBody,
                            param: pathParameter,
                        };
                        if (createBody === undefined) {
                            delete createCallParam.value;
                        }
                        try {
                            const newCalls = WRITE_CONFIGURATOR_CONFIG[newConfigKey](createCallParam);
                            addCalls(calls, newCalls);
                        }
                        catch (err) {
                            throw TapConfiguratorError.internalError(`Cannot generate configuration requests from configuration key ${newConfigKey}. Cause: ${err.message}`, err);
                        }
                    }
                }
                calls.push(...baseConfigObjectToCalls(itemValue, newConfigKey, pathParameter));
            }
        }
        else {
            if (`${configKey}.$` in WRITE_CONFIGURATOR_CONFIG) {
                try {
                    addCalls(calls, createWriteConfigCall(`${configKey}.$`, {
                        firmwareVersion: options.firmwareVersion,
                        service: options.service,
                        value: entry,
                        param: pathParameter,
                        config,
                    }));
                }
                catch (err) {
                    throw TapConfiguratorError.internalError(`Cannot generate create resource request for configuration key ${configKey} for identifier "${pathParameter}". Cause: ${err.message}`, err);
                }
            }
            else {
                for (const subKey in entry) {
                    const newConfigKey = configKey ? `${configKey}.${subKey}` : subKey;
                    if (newConfigKey === `${CREATE_VARIABLE_CONFIG_KEY}.metaDataLength`) {
                        continue;
                    }
                    const value = entry[subKey];
                    // We skip version key as it's a special key
                    if (value === undefined) {
                        continue;
                    }
                    else if (IGNORE_KEYS.indexOf(newConfigKey) >= 0) {
                        continue;
                    }
                    else if (pathParameter !== undefined &&
                        subKey === 'id' &&
                        value === pathParameter) {
                        // Skip id field
                        continue;
                    }
                    else if (newConfigKey in WRITE_CONFIGURATOR_CONFIG) {
                        const callContext = {
                            firmwareVersion: options.firmwareVersion,
                            service: options.service,
                            value: value,
                            param: pathParameter,
                        };
                        addCalls(calls, createWriteConfigCall(newConfigKey, callContext));
                    }
                    else if (typeof value === 'object') {
                        calls.push(...baseConfigObjectToCalls(value, newConfigKey, pathParameter));
                    }
                    else {
                        throw new Error(`Invalid config key "${newConfigKey}" with value ${value}. ${pathParameter ? `Current path parameter: ${pathParameter}` : ''}`);
                    }
                }
            }
        }
        return calls;
    }
    const flattenConfig = toFlatTapConfig(config, options);
    const { service } = options;
    const calls = [
        service.interface.putConfigVersionCall('255.255.65535'),
    ];
    const security = ((_a = config.tap) === null || _a === void 0 ? void 0 : _a.security) || {};
    if (security.scramActivated) {
        // Usually security options are encoded on 2 bytes
        // However to enable scram during configuration, we must give 0x10 value
        // Don't know why
        calls.push({
            method: TapRequestFrame.MethodType.PUT,
            path: '/1024//6',
            body: Uint8Array.from([0x10]),
        });
    }
    calls.push(...baseConfigObjectToCalls(flattenConfig, '', undefined));
    if (security.scramActivated) {
        security.hashPassword = true;
    }
    calls.push(service.interface.putSecurityOptionsCall(security));
    calls.push(service.interface.putConfigVersionCall(config.version || '1.0.0'));
    return calls;
}
function arrayPadEnd(data, length, padValue = 0) {
    if (data.length >= length) {
        return data;
    }
    else {
        return TapStreamWriter.create(length)
            .writeBytes(data)
            .writeBytes(new Uint8Array(length - data.length).fill(padValue, 0, length - data.length)).toBytes;
    }
}
function arrayPadStart(data, length, padValue = 0) {
    if (data.length >= length) {
        return data;
    }
    else {
        return TapStreamWriter.create(length)
            .writeBytes(new Uint8Array(length - data.length).fill(padValue, 0, length - data.length))
            .writeBytes(data).toBytes;
    }
}
function invalidVariableValueError(bundle, variable, msg) {
    return TapConfiguratorError.invalidConfiguration(`data.bundle[${bundle.id}].variable[${variable.id}].value ${msg}`);
}
function encodeInitialVariableValue(variable, bundle) {
    const variableLength = variable.length || 1;
    const variableDataType = variable.dataType === undefined
        ? VariableType.Data.UINT8
        : VariableType.Data[variable.dataType];
    const itemByteSize = variableDataTypeToByteSize(variableDataType);
    function _encodeItem(value, index = 0) {
        let result;
        if (value instanceof Uint8Array) {
            result = value;
        }
        else if (isHexString(value)) {
            result = hexStringToBuffer(value);
        }
        else if (typeof value === 'number') {
            const converter = getConverterFromVariableType(variableDataType, 1);
            if (!converter) {
                throw invalidVariableValueError(bundle, variable, `cannot be converted to a buffer. No registered converter exist for this variable type.`);
            }
            result = converter.encode(value);
        }
        else {
            throw invalidVariableValueError(bundle, variable, `has type ${typeof value} which is not supported by the configurator`);
        }
        if (result.length > itemByteSize) {
            throw invalidVariableValueError(bundle, variable, `at index ${index} is on ${result.length} bytes but expecting a maximum length of ${itemByteSize} byte(s)`);
        }
        else {
            result = arrayPadStart(result, itemByteSize);
        }
        return result;
    }
    let encodedValue;
    if (typeof variable.value === 'string' &&
        variableDataType === VariableType.Data.ASCII) {
        encodedValue = converters$1.ascii.encode(variable.value);
    }
    else if (Array.isArray(variable.value)) {
        const buffers = variable.value.map((v, index) => {
            return _encodeItem(v, index);
        });
        encodedValue = TapStreamWriter.mergeArrays(...buffers);
    }
    else {
        encodedValue = _encodeItem(variable.value);
    }
    const byteLength = variableLength * itemByteSize;
    if (encodedValue.length > byteLength) {
        throw invalidVariableValueError(bundle, variable, `is encoded on ${encodedValue.length} byte(s) but you have declared a maximum length of ${byteLength} byte(s)`);
    }
    return arrayPadEnd(encodedValue, byteLength);
}
function toVariableFlatConfig(variable, bundle) {
    const variableCopy = deepCopy(variable);
    if (variableCopy.length === undefined) {
        variableCopy.length = 1;
    }
    const variableFlatConfig = variableCopy;
    delete variableCopy.dataType;
    delete variableCopy.domain;
    variableFlatConfig.bundleId = bundle.id;
    if (variableFlatConfig.rawMeta !== undefined) {
        variableFlatConfig.rawMeta =
            typeof variableFlatConfig.rawMeta === 'string'
                ? hexStringToBuffer(variableFlatConfig.rawMeta)
                : variableFlatConfig.rawMeta;
    }
    else if (variableCopy.meta) {
        variableFlatConfig.rawMeta = variableMetaConverter.encode(variableCopy.meta);
        delete variableCopy.meta;
    }
    variableFlatConfig.type = {
        domain: variable.domain !== undefined ? variable.domain : 'TARGET',
        data: variable.dataType !== undefined ? variable.dataType : 'UINT8',
    };
    if (variable.value !== undefined && variable.value !== null) {
        variableFlatConfig.value = encodeInitialVariableValue(variable, bundle);
    }
    return variableFlatConfig;
}
function toFlatTapConfig(config, options) {
    var _a, _b, _c, _d, _e, _f, _g;
    const configCopy = deepCopy(config);
    const flatProfiles = [];
    if (configCopy.target) {
        delete configCopy.target.type;
    }
    if (configCopy.data) {
        // Flaten profiles
        if (configCopy.data.profiles) {
            for (const profile of (_a = configCopy.data) === null || _a === void 0 ? void 0 : _a.profiles) {
                const flatenProfile = profile;
                if (flatenProfile.id === undefined) {
                    flatenProfile.id = getProfileIdFromConfig(flatenProfile);
                }
                if (isSpecialProfileId(flatenProfile.id)) {
                    delete flatenProfile.username;
                }
                const subProfiles = flatenProfile.users || [];
                delete flatenProfile.users;
                flatProfiles.push(flatenProfile);
                for (const subProfile of subProfiles) {
                    subProfile.alias = flatenProfile.id;
                    flatProfiles.push(subProfile);
                    if (isSpecialProfileId(subProfile.id)) {
                        delete subProfile.username;
                    }
                }
            }
        }
        // Flaten acls and special features
        const flatSpecialFeatures = [];
        const flatVariables = [];
        if (configCopy.data.bundles) {
            for (const bundle of configCopy.data.bundles) {
                const acls = bundle.acls || {};
                delete bundle.acls;
                const flatAcls = [];
                for (const username in acls) {
                    const acl = acls[username];
                    let profile = flatProfiles.find((p) => p.username === username);
                    if (!profile) {
                        profile = SPECIAL_USERS.find((p) => p.username === username);
                        if (!profile) {
                            throw new Error(`Cannot find profile with username "${username}"`); // TODO proper error
                        }
                    }
                    flatAcls.push({
                        groupId: profile.id,
                        read: acl.read,
                        write: acl.write,
                    });
                }
                if (flatAcls.length > 0) {
                    bundle.acl = flatAcls;
                }
                const variables = bundle.variables || [];
                delete bundle.variables;
                for (const variable of variables) {
                    flatVariables.push(toVariableFlatConfig(variable, bundle));
                }
                const features = bundle.features || [];
                delete bundle.features;
                for (const featureKey of features) {
                    const featureId = enumKeyToEnumValue(featureKey, SpecialFeature);
                    if (flatSpecialFeatures.find((f) => f.id === featureId)) {
                        throw new Error(`Special feature "${featureId}" has already be given to another bundle`);
                    }
                    flatSpecialFeatures.push({
                        id: featureId,
                        bundleId: bundle.id,
                    });
                }
            }
        }
        if (flatSpecialFeatures.length > 0) {
            configCopy.data.features = flatSpecialFeatures;
        }
        if (flatVariables.length > 0) {
            configCopy.data.variables = flatVariables;
        }
    }
    if ((_c = (_b = config.tap) === null || _b === void 0 ? void 0 : _b.security) === null || _c === void 0 ? void 0 : _c.scramActivated) {
        if (!configCopy.tap) {
            configCopy.tap = {};
        }
        if (!configCopy.tap.scram) {
            configCopy.tap.scram = {};
        }
        if (!configCopy.tap.scram.hashIteration) {
            configCopy.tap.scram.hashIteration = DEFAULT_HASH_ITERATION_COUNT;
        }
    }
    const hashIteration = (_e = (_d = configCopy.tap) === null || _d === void 0 ? void 0 : _d.scram) === null || _e === void 0 ? void 0 : _e.hashIteration;
    for (const specialProfile of [ADMIN_USER, SUPERVISOR_USER]) {
        if (!flatProfiles.find((p) => p.id === specialProfile.id)) {
            flatProfiles.push({
                id: specialProfile.id,
                password: specialProfile.password || specialProfile.username,
                lifeTime: specialProfile.lifeTime || 30,
            });
        }
    }
    for (const profile of flatProfiles) {
        prepareUserPassword(profile, ((_f = configCopy === null || configCopy === void 0 ? void 0 : configCopy.tap) === null || _f === void 0 ? void 0 : _f.security) || {}, (input) => {
            if (!hashIteration) {
                throw TapConfiguratorError.internalError(`Hash iteration value must be set`);
            }
            const salt = (options === null || options === void 0 ? void 0 : options.saltGenerator)
                ? options.saltGenerator()
                : DEFAULT_SCRAM_SALT_GENERATOR();
            return ScramAuth.createScramPasswordKey(input, {
                salt,
                iterationNumber: hashIteration,
            });
        });
    }
    if (!configCopy.data) {
        configCopy.data = {};
    }
    configCopy.data.profiles = flatProfiles;
    if ((_g = configCopy.application) === null || _g === void 0 ? void 0 : _g.uri) {
        if (typeof configCopy.application.uri === 'object') {
            const appUri = configCopy.application.uri;
            configCopy.application.uri = `${AppPathType[appUri.type]}${appUri.value}`;
        }
    }
    return configCopy;
}
function prepareUserPassword(profile, securityConfig, scramPasswordGenerator) {
    if (profile.password) {
        if (profile.key) {
            throw TapConfiguratorError.invalidConfiguration(`You cannot specify both password and passwordKey for profile configuration. Specify only one of those two options.`);
        }
        if (securityConfig.scramActivated) {
            const passwordBytes = scramPasswordGenerator(profile.password);
            // debug(
            //     `Writing SCRAM password for user id ${userId} with hash: ${bufferToHexString(passwordBytes)}`
            // );
            delete profile.password;
            profile.key = passwordBytes;
        }
        else if (securityConfig.hashPassword) {
            const passwordBytes = hashLoginPassword(profile.password);
            // debug(
            //     `Writing password for user id ${userId} with hash: ${bufferToHexString(
            //         passwordBytes
            //     )}`
            // );
            delete profile.password;
            profile.key = passwordBytes;
        }
        else {
            // debug(
            //     `Writing password for user id ${userId} without hash: ${password}`
            // );
            // OK nothing to do
        }
    }
    // console.log('profile.passwordKey', typeof profile.passwordKey, profile.passwordKey?.constructor?.name);
}
function stepEvent$1(observer, step, context, extraData) {
    observer.next(Object.assign({ step, progress: {
            loaded: context.index + 1,
            total: context.total,
        } }, (extraData || {})));
}
/**
 * Configure tap from a configuration file schema
 *
 */
class TapConfiguratorWriter {
    constructor(options = {
        delayMs: 400,
        throwOnNotImplementedConfigKey: false,
    }, host) {
        this.options = options;
        this.host = host;
    }
    createConfiguratorForTap(tap, config, options) {
        return __awaiter$1(this, void 0, void 0, function* () {
            const firmwareVersion = (yield tap.service.device.getFirmwareVersion()).body();
            return this.configure(tap, firmwareVersion, config, options);
        });
    }
    configure(tap, firmwareVersion, config, options) {
        var _a, _b;
        const configureContext = {
            tap,
            options: config.meta || {},
            tapConfig: config.config,
        };
        const operations = [];
        config = this._adaptConfigurationToCurrentVersion(config);
        if (!(options === null || options === void 0 ? void 0 : options.skipSchemaValidation)) {
            operations.push((observer, context) => __awaiter$1(this, void 0, void 0, function* () {
                stepEvent$1(observer, TapConfiguratorWriter.Step.validatesConfig, context);
                validateTapConfiguratorConfig(config);
            }));
        }
        if (!configureContext.options.skipFactoryReset) {
            operations.push((observer, context) => __awaiter$1(this, void 0, void 0, function* () {
                stepEvent$1(observer, TapConfiguratorWriter.Step.factoryReset, context);
                (yield tap.service.device.factoryReset()).successful();
            }));
        }
        const useScram = !!((_b = (_a = configureContext.tapConfig.tap) === null || _a === void 0 ? void 0 : _a.security) === null || _b === void 0 ? void 0 : _b.scramActivated);
        if (useScram) {
            operations.push((observer, context) => __awaiter$1(this, void 0, void 0, function* () {
                stepEvent$1(observer, TapConfiguratorWriter.Step.enableSecureCom, context);
                if (useScram) {
                    yield tap.encryption.start();
                }
            }));
        }
        const calls = tapConfigToServiceCalls(configureContext.tapConfig, {
            firmwareVersion,
            service: tap.service,
        });
        // operations.push(async (observer, context) => {
        //     observer.next({
        //         step: TapConfigurator.Step.
        //     } as TapConfigurator.Event);
        //     const firmwareVersion = (await context.tap.service.device.getFirmwareVersion()).body();
        // });
        // operations.push(
        //     async (observer, context) => {
        //         stepEvent(observer, TapConfiguratorWriter.Step.setupConfigurationState, context);
        //         await this.setupConfigState(configureContext.tap);
        //     }
        // );
        // if (useScram) {
        //     operations.push(async (observer, context) => {
        //         // To write user password keys, we need to put scram configuration first
        //         stepEvent(observer, TapConfiguratorWriter.Step.writeSecurityOptions, context);
        //         tap.encryption.pause();
        //         (await tap.service.interface.putSecurityOptions({
        //             scramActivated: true
        //         } as SecurityOptions)).successful();
        //         tap.encryption.resume();
        //     });
        // }
        this.addConfigurCalls(configureContext, calls, operations);
        // operations.push(
        //     async (observer, context) => {
        //         stepEvent(observer, TapConfiguratorWriter.Step.applyConfig, context);
        //         await this.applyConfig(configureContext.tap, configureContext.tapConfig.version || '1.0.0');
        //     }
        // );
        operations.push((observer, context) => __awaiter$1(this, void 0, void 0, function* () {
            stepEvent$1(observer, TapConfiguratorWriter.Step.logout, context);
            yield configureContext.tap.auth.logout();
            tap.auth.clearCache();
        }));
        return StepOperations.sequential(operations);
    }
    setupConfigState(tap) {
        return __awaiter$1(this, void 0, void 0, function* () {
            const state = (yield tap.service.firmware.getState()).body();
            if (state !== FirmwareState$1.NORMAL) {
                throw TapConfiguratorError.unexpectedFirmwareState(state);
            }
            (yield tap.service.interface.putConfigVersion('255.255.65535')).successful();
        });
    }
    // TODO move from here
    isConfigured(tap) {
        return __awaiter$1(this, void 0, void 0, function* () {
            const configVersionCalled = yield tap.service.interface.getConfigVersion();
            configVersionCalled.successful();
            const intValue = configVersionCalled.body(NumberConverter.uint32());
            return intValue !== 0;
        });
    }
    /***
     *
     */
    applyConfig(tap, newVersion = '1.0.0') {
        return __awaiter$1(this, void 0, void 0, function* () {
            yield this.sleep(this.options.delayMs);
            (yield tap.service.interface.putConfigVersion(newVersion)).successful();
            yield this.sleep(this.options.delayMs);
            (yield tap.service.interface.reloadConfig()).successful();
            yield this.sleep(this.options.delayMs);
        });
    }
    addConfigurCalls(configureContext, calls, operations) {
        calls.forEach((callOrError, index) => {
            operations.push((observer, context) => __awaiter$1(this, void 0, void 0, function* () {
                const stepProgress = {
                    total: calls.length,
                    loaded: index + 1,
                };
                if (callOrError instanceof Error) {
                    stepEvent$1(observer, TapConfiguratorWriter.Step.executeCall, context, {
                        error: callOrError,
                        stepProgress,
                    });
                }
                else {
                    stepEvent$1(observer, TapConfiguratorWriter.Step.executeCall, context, {
                        stepProgress,
                        call: callOrError,
                    });
                    try {
                        (yield configureContext.tap.lwm2m.execute(callOrError)).successful();
                    }
                    catch (err) {
                        if (!this.options.throwOnNotImplementedConfigKey &&
                            isCodeError(TapError.Code.ResponseStatusError, err)) {
                            const responseStatusError = err;
                            if (responseStatusError.response.status ===
                                ResultCode.NOT_IMPLEMENTED) {
                                stepEvent$1(observer, TapConfiguratorWriter.Step.executeCall, context, {
                                    error: err,
                                    stepProgress,
                                });
                                return;
                            }
                        }
                        throw err;
                    }
                }
            }));
        });
    }
    sleep(delayMs) {
        return sleep(delayMs);
    }
    _adaptConfigurationToCurrentVersion(config) {
        var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k;
        if (config.config.wireless) {
            let blePairing = undefined;
            let nfcPairingMode;
            if (((_c = (_b = (_a = config.config.wireless) === null || _a === void 0 ? void 0 : _a.protocols) === null || _b === void 0 ? void 0 : _b.nfc) === null || _c === void 0 ? void 0 : _c.pairingMode) !== undefined) {
                nfcPairingMode = config.config.wireless.protocols.nfc.pairingMode;
                delete config.config.wireless.protocols.nfc.pairingMode;
            }
            if (((_f = (_e = (_d = config.config.wireless) === null || _d === void 0 ? void 0 : _d.protocols) === null || _e === void 0 ? void 0 : _e.ble) === null || _f === void 0 ? void 0 : _f.pairing) !== undefined) {
                blePairing = (_j = (_h = (_g = config.config.wireless) === null || _g === void 0 ? void 0 : _g.protocols) === null || _h === void 0 ? void 0 : _h.ble) === null || _j === void 0 ? void 0 : _j.pairing;
                (_k = config.config.wireless.protocols.ble) === null || _k === void 0 ? true : delete _k.pairing;
            }
            if (!config.config.wireless.options) {
                config.config.wireless.options = {};
            }
            if (blePairing !== undefined || nfcPairingMode !== undefined) {
                config.config.wireless.options.pairing = {
                    nfcPairingMode,
                    blePairing,
                };
            }
        }
        return config;
    }
}
(function (TapConfiguratorWriter) {
    let Step;
    (function (Step) {
        Step["validatesConfig"] = "TapConfiguratorStepValidatesConfig";
        Step["enableSecureCom"] = "TapConfiguratorStepenableSecureCommunication";
        Step["factoryReset"] = "TapConfiguratorStepFactoryReset";
        Step["setupConfigurationState"] = "TapConfiguratorStepSetupConfigurationState";
        Step["executeCall"] = "TapConfiguratorStepExecuteCall";
        Step["logout"] = "TapConfiguratorSteplogout";
        Step["applyConfig"] = "TapConfiguratorStepApplyConfig";
        Step["writeSecurityOptions"] = "TapConfiguratorStepWriteSecurityOptions";
    })(Step = TapConfiguratorWriter.Step || (TapConfiguratorWriter.Step = {}));
})(TapConfiguratorWriter || (TapConfiguratorWriter = {}));

/**
 *   Generated file. Do not edit
 */
const ɵ0$1 = (context) => {
    return context.service.interface.getApplicationDataCall();
}, ɵ1 = (context) => {
    return context.service.interface.getAppNameCall();
}, ɵ2 = (context) => {
    if (context.firmwareVersion) {
        if (!isTapFirmwareVersionGreaterThanOrEqual(context.firmwareVersion, '1.113')) {
            return new ConfigKeyNotSupportedByFirmwareVersion('application.productCode', context.firmwareVersion, {
                minApiVersion: '1.113',
                maxApiVersion: undefined,
            });
        }
    }
    return context.service.interface.getProductCodeCall();
}, ɵ3 = (context) => {
    return context.service.interface.getUniversalLinkCall();
}, ɵ4 = (context) => {
    return context.service.interface.getAppPathCall();
}, ɵ5 = (context) => {
    return context.service.cloud.getDataLogPublishTopicCall();
}, ɵ6 = (context) => {
    return context.service.cloud.getDataLogSubscribeTopicCall();
}, ɵ7 = (context) => {
    return context.service.cloud.getDataLogUploadPeriodCall();
}, ɵ8 = (context) => {
    if (context.firmwareVersion) {
        if (!isTapFirmwareVersionLessThanOrEqual(context.firmwareVersion, '1.103')) {
            return new ConfigKeyNotSupportedByFirmwareVersion('cloud.mqtt.brokerPort', context.firmwareVersion, {
                minApiVersion: undefined,
                maxApiVersion: '1.103',
            });
        }
    }
    return context.service.cloud.getMqttBrokerPortCall();
}, ɵ9 = (context) => {
    if (context.firmwareVersion) {
        if (!isTapFirmwareVersionLessThanOrEqual(context.firmwareVersion, '1.103')) {
            return new ConfigKeyNotSupportedByFirmwareVersion('cloud.mqtt.clientId', context.firmwareVersion, {
                minApiVersion: undefined,
                maxApiVersion: '1.103',
            });
        }
    }
    return context.service.cloud.getMqttClientIdCall();
}, ɵ10 = (context) => {
    if (context.firmwareVersion) {
        if (!isTapFirmwareVersionLessThanOrEqual(context.firmwareVersion, '1.103')) {
            return new ConfigKeyNotSupportedByFirmwareVersion('cloud.mqtt.password', context.firmwareVersion, {
                minApiVersion: undefined,
                maxApiVersion: '1.103',
            });
        }
    }
    return context.service.cloud.getMqttPasswordCall();
}, ɵ11 = (context) => {
    if (context.firmwareVersion) {
        if (!isTapFirmwareVersionLessThanOrEqual(context.firmwareVersion, '1.103')) {
            return new ConfigKeyNotSupportedByFirmwareVersion('cloud.mqtt.username', context.firmwareVersion, {
                minApiVersion: undefined,
                maxApiVersion: '1.103',
            });
        }
    }
    return context.service.cloud.getMqttUsernameCall();
}, ɵ12 = (context) => {
    return context.service.cloud.getPlatformCall();
}, ɵ13 = (context) => {
    return context.service.interface.getConfigFormatVersionCall();
}, ɵ14 = (context) => {
    return context.service.bundle.getDataLogPeriodCall(context.param);
}, ɵ15 = (context) => {
    return context.service.bundle.getNameCall(context.param);
}, ɵ16 = (context) => {
    return context.service.group.getAliasCall(context.param);
}, ɵ17 = (context) => {
    return context.service.group.getSessionLifetimeCall(context.param);
}, ɵ18 = (context) => {
    return context.service.group.getNameCall(context.param);
}, ɵ19 = (context) => {
    return context.service.variable.getVariableAddressCall(context.param);
}, ɵ20 = (context) => {
    return context.service.variable.getBundleIdCall(context.param);
}, ɵ21 = (context) => {
    return context.service.variable.getNumberOfElementsCall(context.param);
}, ɵ22 = (context) => {
    if (context.firmwareVersion) {
        if (!isTapFirmwareVersionGreaterThanOrEqual(context.firmwareVersion, '1.73')) {
            return new ConfigKeyNotSupportedByFirmwareVersion('data.variables[].meta', context.firmwareVersion, {
                minApiVersion: '1.73',
                maxApiVersion: undefined,
            });
        }
    }
    return context.service.variable.getMetaCall(context.param);
}, ɵ23 = (context) => {
    if (context.firmwareVersion) {
        if (!isTapFirmwareVersionGreaterThanOrEqual(context.firmwareVersion, '1.73')) {
            return new ConfigKeyNotSupportedByFirmwareVersion('data.variables[].rawMeta', context.firmwareVersion, {
                minApiVersion: '1.73',
                maxApiVersion: undefined,
            });
        }
    }
    return context.service.variable.getRawMetaCall(context.param);
}, ɵ24 = (context) => {
    return context.service.variable.getTypeCall(context.param);
}, ɵ25 = (context) => {
    return context.service.jvm.getProfileIdCall();
}, ɵ26 = (context) => {
    if (context.firmwareVersion) {
        if (!isTapFirmwareVersionGreaterThanOrEqual(context.firmwareVersion, '2.7')) {
            return new ConfigKeyNotSupportedByFirmwareVersion('ports.ethernet.gatewayIp', context.firmwareVersion, {
                minApiVersion: '2.7',
                maxApiVersion: undefined,
            });
        }
    }
    return context.service.ethernet.getGatewayIpCall();
}, ɵ27 = (context) => {
    if (context.firmwareVersion) {
        if (!isTapFirmwareVersionGreaterThanOrEqual(context.firmwareVersion, '2.7')) {
            return new ConfigKeyNotSupportedByFirmwareVersion('ports.ethernet.ip', context.firmwareVersion, {
                minApiVersion: '2.7',
                maxApiVersion: undefined,
            });
        }
    }
    return context.service.ethernet.getIpCall();
}, ɵ28 = (context) => {
    if (context.firmwareVersion) {
        if (!isTapFirmwareVersionGreaterThanOrEqual(context.firmwareVersion, '2.7')) {
            return new ConfigKeyNotSupportedByFirmwareVersion('ports.ethernet.ipMask', context.firmwareVersion, {
                minApiVersion: '2.7',
                maxApiVersion: undefined,
            });
        }
    }
    return context.service.ethernet.getIpMaskCall();
}, ɵ29 = (context) => {
    return context.service.interface.getPowerOptimisationLevelCall();
}, ɵ30 = (context) => {
    if (context.firmwareVersion) {
        if (!isTapFirmwareVersionGreaterThanOrEqual(context.firmwareVersion, '1.11')) {
            return new ConfigKeyNotSupportedByFirmwareVersion('tap.scram.hashIteration', context.firmwareVersion, {
                minApiVersion: '1.11',
                maxApiVersion: undefined,
            });
        }
    }
    return context.service.scram.getHashIterationCall();
}, ɵ31 = (context) => {
    return context.service.interface.getSecurityOptionsCall();
}, ɵ32 = (context) => {
    if (context.firmwareVersion) {
        if (!isTapFirmwareVersionGreaterThanOrEqual(context.firmwareVersion, '1.107')) {
            return new ConfigKeyNotSupportedByFirmwareVersion('tap.time.ntpServer', context.firmwareVersion, {
                minApiVersion: '1.107',
                maxApiVersion: undefined,
            });
        }
    }
    return context.service.time.getNtpServerCall();
}, ɵ33 = (context) => {
    if (context.firmwareVersion) {
        if (!isTapFirmwareVersionGreaterThanOrEqual(context.firmwareVersion, '1.107')) {
            return new ConfigKeyNotSupportedByFirmwareVersion('tap.time.zone', context.firmwareVersion, {
                minApiVersion: '1.107',
                maxApiVersion: undefined,
            });
        }
    }
    return context.service.time.getTimeZoneCall();
}, ɵ34 = (context) => {
    return context.service.target.getFirmwareVersionCall();
}, ɵ35 = (context) => {
    return context.service.target.getProtocolCall();
}, ɵ36 = (context) => {
    return context.service.interface.getConfigVersionCall();
}, ɵ37 = (context) => {
    return context.service.interface.getAuthorizedHostProtocolCall();
}, ɵ38 = (context) => {
    return context.service.interface.getHostInactivityPeriodCall();
}, ɵ39 = (context) => {
    return context.service.interface.getPairingOptionsCall();
}, ɵ40 = (context) => {
    return context.service.lora.getConfigCall();
}, ɵ41 = (context) => {
    return context.service.lora.getIdsCall();
}, ɵ42 = (context) => {
    return context.service.mqtt.getBrokerHostnameCall();
}, ɵ43 = (context) => {
    return context.service.mqtt.getBrokerPortCall();
}, ɵ44 = (context) => {
    if (context.firmwareVersion) {
        if (!isTapFirmwareVersionGreaterThanOrEqual(context.firmwareVersion, '1.86')) {
            return new ConfigKeyNotSupportedByFirmwareVersion('wireless.protocols.mqtt.clientId', context.firmwareVersion, {
                minApiVersion: '1.86',
                maxApiVersion: undefined,
            });
        }
    }
    return context.service.mqtt.getClientIdCall();
}, ɵ45 = (context) => {
    return context.service.mqtt.getPasswordCall();
}, ɵ46 = (context) => {
    return context.service.mqtt.getRelayNetKeyCall();
}, ɵ47 = (context) => {
    if (context.firmwareVersion) {
        if (!isTapFirmwareVersionGreaterThanOrEqual(context.firmwareVersion, '1.86')) {
            return new ConfigKeyNotSupportedByFirmwareVersion('wireless.protocols.mqtt.username', context.firmwareVersion, {
                minApiVersion: '1.86',
                maxApiVersion: undefined,
            });
        }
    }
    return context.service.mqtt.getUsernameCall();
}, ɵ48 = (context) => {
    if (context.firmwareVersion) {
        if (!isTapFirmwareVersionGreaterThanOrEqual(context.firmwareVersion, '1.55')) {
            return new ConfigKeyNotSupportedByFirmwareVersion('wireless.protocols.nfc.autoLogProfileId', context.firmwareVersion, {
                minApiVersion: '1.55',
                maxApiVersion: undefined,
            });
        }
    }
    return context.service.interface.getNfcAutologProfileIdCall();
}, ɵ49 = (context) => {
    if (context.firmwareVersion) {
        if (!isTapFirmwareVersionGreaterThanOrEqual(context.firmwareVersion, '1.85')) {
            return new ConfigKeyNotSupportedByFirmwareVersion('wireless.protocols.nfc.connectionPriority', context.firmwareVersion, {
                minApiVersion: '1.85',
                maxApiVersion: undefined,
            });
        }
    }
    return context.service.interface.getNfcConnectionPriorityCall();
}, ɵ50 = (context) => {
    if (context.firmwareVersion) {
        if (!isTapFirmwareVersionGreaterThanOrEqual(context.firmwareVersion, '1.88')) {
            return new ConfigKeyNotSupportedByFirmwareVersion('wireless.protocols.wifi.countryCode', context.firmwareVersion, {
                minApiVersion: '1.88',
                maxApiVersion: undefined,
            });
        }
    }
    return context.service.wifi.getCountryCodeCall();
}, ɵ51 = (context) => {
    return context.service.wifi.getGatewayIpCall();
}, ɵ52 = (context) => {
    return context.service.wifi.getIpCall();
}, ɵ53 = (context) => {
    return context.service.wifi.getKeyCall();
}, ɵ54 = (context) => {
    if (context.firmwareVersion) {
        if (!isTapFirmwareVersionGreaterThanOrEqual(context.firmwareVersion, '1.88')) {
            return new ConfigKeyNotSupportedByFirmwareVersion('wireless.protocols.wifi.keyVisibility', context.firmwareVersion, {
                minApiVersion: '1.88',
                maxApiVersion: undefined,
            });
        }
    }
    return context.service.wifi.getKeyVisibilityCall();
}, ɵ55 = (context) => {
    return context.service.wifi.getIpMaskCall();
}, ɵ56 = (context) => {
    return context.service.wifi.getModeCall();
}, ɵ57 = (context) => {
    if (context.firmwareVersion) {
        if (!isTapFirmwareVersionGreaterThanOrEqual(context.firmwareVersion, '1.88')) {
            return new ConfigKeyNotSupportedByFirmwareVersion('wireless.protocols.wifi.protocol', context.firmwareVersion, {
                minApiVersion: '1.88',
                maxApiVersion: undefined,
            });
        }
    }
    return context.service.wifi.getProtocolCall();
}, ɵ58 = (context) => {
    return context.service.wifi.getSSIDCall();
}, ɵ59 = (context) => {
    if (context.firmwareVersion) {
        if (!isTapFirmwareVersionGreaterThanOrEqual(context.firmwareVersion, '1.88')) {
            return new ConfigKeyNotSupportedByFirmwareVersion('wireless.protocols.wifi.ssidVisibility', context.firmwareVersion, {
                minApiVersion: '1.88',
                maxApiVersion: undefined,
            });
        }
    }
    return context.service.wifi.getSSIDVisibilityCall();
}, ɵ60 = (context) => {
    if (context.firmwareVersion) {
        if (!isTapFirmwareVersionGreaterThanOrEqual(context.firmwareVersion, '1.88')) {
            return new ConfigKeyNotSupportedByFirmwareVersion('wireless.protocols.wifi.txPower', context.firmwareVersion, {
                minApiVersion: '1.88',
                maxApiVersion: undefined,
            });
        }
    }
    return context.service.wifi.getTxPowerCall();
};
const READ_CONFIGURATOR_CONFIG$1 = {
    'application.data': ɵ0$1,
    'application.name': ɵ1,
    'application.productCode': ɵ2,
    'application.universalLink': ɵ3,
    'application.uri': ɵ4,
    'cloud.dataLog.mqttPublishTopic': ɵ5,
    'cloud.dataLog.mqttSubscribeTopic': ɵ6,
    'cloud.dataLog.uploadPeriod': ɵ7,
    'cloud.mqtt.brokerPort': ɵ8,
    'cloud.mqtt.clientId': ɵ9,
    'cloud.mqtt.password': ɵ10,
    'cloud.mqtt.username': ɵ11,
    'cloud.platform': ɵ12,
    configFormatVersion: ɵ13,
    'data.bundles[].dataLog.period': ɵ14,
    'data.bundles[].name': ɵ15,
    'data.profiles[].alias': ɵ16,
    'data.profiles[].lifeTime': ɵ17,
    'data.profiles[].username': ɵ18,
    'data.variables[].address': ɵ19,
    'data.variables[].bundleId': ɵ20,
    'data.variables[].length': ɵ21,
    'data.variables[].meta': ɵ22,
    'data.variables[].rawMeta': ɵ23,
    'data.variables[].type': ɵ24,
    'jvm.profileId': ɵ25,
    'ports.ethernet.gatewayIp': ɵ26,
    'ports.ethernet.ip': ɵ27,
    'ports.ethernet.ipMask': ɵ28,
    'tap.powerOptimization': ɵ29,
    'tap.scram.hashIteration': ɵ30,
    'tap.security': ɵ31,
    'tap.time.ntpServer': ɵ32,
    'tap.time.zone': ɵ33,
    'target.firmwareVersion': ɵ34,
    'target.protocol.type': ɵ35,
    version: ɵ36,
    'wireless.options.authorizedProtocols': ɵ37,
    'wireless.options.inactivityTimeout': ɵ38,
    'wireless.options.pairing': ɵ39,
    'wireless.protocols.lora.config': ɵ40,
    'wireless.protocols.lora.ids': ɵ41,
    'wireless.protocols.mqtt.brokerHostname': ɵ42,
    'wireless.protocols.mqtt.brokerPort': ɵ43,
    'wireless.protocols.mqtt.clientId': ɵ44,
    'wireless.protocols.mqtt.password': ɵ45,
    'wireless.protocols.mqtt.relayNetKey': ɵ46,
    'wireless.protocols.mqtt.username': ɵ47,
    'wireless.protocols.nfc.autoLogProfileId': ɵ48,
    'wireless.protocols.nfc.connectionPriority': ɵ49,
    'wireless.protocols.wifi.countryCode': ɵ50,
    'wireless.protocols.wifi.gatewayIp': ɵ51,
    'wireless.protocols.wifi.ip': ɵ52,
    'wireless.protocols.wifi.key': ɵ53,
    'wireless.protocols.wifi.keyVisibility': ɵ54,
    'wireless.protocols.wifi.mask': ɵ55,
    'wireless.protocols.wifi.mode': ɵ56,
    'wireless.protocols.wifi.protocol': ɵ57,
    'wireless.protocols.wifi.ssid': ɵ58,
    'wireless.protocols.wifi.ssidVisibility': ɵ59,
    'wireless.protocols.wifi.txPower': ɵ60,
};

const ɵ0 = (context) => {
    var _a, _b, _c, _d, _e;
    const protocolType = ((_b = (_a = context.config) === null || _a === void 0 ? void 0 : _a.target) === null || _b === void 0 ? void 0 : _b.type) === 'MCU'
        ? (_e = (_d = (_c = context.config) === null || _c === void 0 ? void 0 : _c.target) === null || _d === void 0 ? void 0 : _d.protocol) === null || _e === void 0 ? void 0 : _e.type
        : 'MODBUS';
    switch (protocolType) {
        case undefined:
        case 'NONE':
        case 'SWD':
        //   case 'JTAG':
        case 'GPIO':
        case 'CUSTOM':
            // Nothing more to read
            return undefined;
        case 'S3P':
            return context.service.target.getS3PConfigurationCall();
        case 'MODBUS':
        case 'SERIAL':
            return context.service.target.getSerialModbusConfigurationCall();
        case 'CAN':
            if (context.firmwareVersion) {
                if (!isTapFirmwareVersionGreaterThanOrEqual(context.firmwareVersion, '2.6')) {
                    return new ConfigKeyNotSupportedByFirmwareVersion('target.protocol.config', context.firmwareVersion, {
                        minApiVersion: '2.6',
                        maxApiVersion: undefined,
                    });
                }
            }
            return context.service.target.getCanConfigurationCall();
        default:
            return TapConfiguratorError.invalidConfiguration(`Target protocol "${protocolType}" is not supported by this version of the configurator`);
    }
};
const READ_CONFIGURATOR_CUSTOM_CONFIG = {
    'target.protocol.config': ɵ0,
};
const READ_CONFIGURATOR_CONFIG = Object.assign(Object.assign({}, READ_CONFIGURATOR_CONFIG$1), READ_CONFIGURATOR_CUSTOM_CONFIG);

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());
    });
};
function stepEvent(observer, step, context, extraData) {
    observer.next(Object.assign({ step, progress: {
            loaded: context.index + 1,
            total: context.total,
        } }, (extraData || {})));
}
/**
 * Configure tap from a configuration file schema
 *
 */
class TapConfiguratorReader {
    constructor(calls = READ_CONFIGURATOR_CONFIG) {
        this.calls = calls;
    }
    createReadOperations(tap) {
        const configureContext = {
            tap,
        };
        const operations = [];
        const outputConfig = {};
        this.addConfigurCalls(configureContext, this.calls, operations, outputConfig, tap);
        return StepOperations.sequential(operations);
    }
    addConfigurCalls(configureContext, calls, operations, outputConfig, tap) {
        let index = 0;
        for (const key in calls) {
            const context = {
                firmwareVersion: '9999.9999',
                service: tap.service,
                param: undefined,
                config: outputConfig,
            };
            const configKey = key;
            const factoryFct = calls[configKey];
            if (!factoryFct) {
                index++;
                continue;
            }
            const callOrErrorOrUndefined = factoryFct(context);
            if (callOrErrorOrUndefined) {
                operations.push((observer, context) => __awaiter(this, void 0, void 0, function* () {
                    if (callOrErrorOrUndefined instanceof Error) {
                        stepEvent(observer, TapConfiguratorReader.Step.readKeyError, context, {
                            configKey,
                            error: callOrErrorOrUndefined,
                            // call: callOrError
                        });
                    }
                    else {
                        try {
                            outputConfig[configKey] = (yield configureContext.tap.lwm2m.execute(callOrErrorOrUndefined)).body();
                            stepEvent(observer, TapConfiguratorReader.Step.readKeySuccess, context, {
                                configKey,
                                value: outputConfig[configKey],
                                call: callOrErrorOrUndefined,
                            });
                        }
                        catch (error) {
                            stepEvent(observer, TapConfiguratorReader.Step.readKeyError, context, {
                                configKey,
                                call: callOrErrorOrUndefined,
                                error,
                            });
                        }
                    }
                }));
            }
            index++;
        }
        operations.push((observer, context) => __awaiter(this, void 0, void 0, function* () {
            stepEvent(observer, TapConfiguratorReader.Step.done, context, {
                config: outputConfig,
            });
        }));
    }
    sleep(delayMs) {
        return sleep(delayMs);
    }
}
(function (TapConfiguratorReader) {
    let Step;
    (function (Step) {
        Step["readKeySuccess"] = "TapConfiguratorReaderreadKeySuccess";
        Step["readKeyError"] = "TapConfiguratorReaderReadKeyError";
        Step["done"] = "TapConfiguratorReaderDone";
    })(Step = TapConfiguratorReader.Step || (TapConfiguratorReader.Step = {}));
})(TapConfiguratorReader || (TapConfiguratorReader = {}));

/**
 * Utility class to build a configuration
 */
class TapConfiguratorConfigBuilder {
    constructor(_schema) {
        this._schema = _schema;
    }
    get configData() {
        if (!this._schema.config.data) {
            this._schema.config.data = {};
        }
        return this._schema.config.data;
    }
    get profiles() {
        if (!this.configData.profiles) {
            this.configData.profiles = [];
        }
        return this.configData.profiles;
    }
    get tapConfig() {
        if (!this._schema.config.tap) {
            this._schema.config.tap = {};
        }
        return this._schema.config.tap;
    }
    /**
     * @deprecated
     * TODO move from here as factory method should not polute the class
     */
    static default() {
        return new TapConfiguratorConfigBuilder({
            meta: {},
            config: {
                application: {
                    name: 'TAP',
                },
                version: '1.0.0',
                tap: {
                    security: {
                        disableHardwareFactoryReset: false,
                        hashPassword: true,
                        disableLoginWithUID: false,
                        disableResourceFactoryReset: false,
                        scramActivated: false,
                    },
                    scram: {
                        hashIteration: 5000,
                    },
                },
                data: {
                    profiles: SPECIAL_USERS,
                },
            },
        });
    }
    /**
     * Set application name
     * @param appName
     */
    appName(appName) {
        if (!this._schema.config.application) {
            this._schema.config.application = {};
        }
        this._schema.config.application.name = appName;
        return this;
    }
    /**
     * Edit user identified by username
     * If user does not it will be created with default values
     * @param username
     * @param editor
     */
    user(username, editor) {
        const userDataIndex = this.profiles.findIndex((p) => p.username === username);
        let userData = userDataIndex >= 0 ? this.profiles[userDataIndex] : undefined;
        if (!userData) {
            // TODO check SpecialProfileConfig
            userData = {
                username,
                lifeTime: 300,
                password: username,
            };
        }
        userData = editor(userData);
        this.profiles[userDataIndex] = userData;
        return this;
    }
    /**
     * Enable/disable scram
     * @param enabled true to enable scram
     */
    scram(enabled) {
        // TODO hash iteration count ?
        if (!this.tapConfig.security) {
            this.tapConfig.security = {
                scramActivated: false,
                disableHardwareFactoryReset: false,
                hashPassword: true,
                disableResourceFactoryReset: false,
                disableLoginWithUID: false,
            };
        }
        else {
            this.tapConfig.security.scramActivated = enabled;
            if (enabled) {
                this.tapConfig.security.hashPassword = true;
            }
        }
        return this;
    }
    build() {
        return this._schema;
    }
}

/**
 * Generated bundle index. Do not edit.
 */

export { ADMIN_USER, ANONYMOUS_USER, READ_CONFIGURATOR_CONFIG, SERIAL_SERVER_USER, SPECIAL_USERS, SUPERVISOR_USER, TapConfiguratorConfigBuilder, TapConfiguratorError, TapConfiguratorReader, TapConfiguratorWriter, WRITE_CONFIGURATOR_CONFIG, isSpecialProfileUsername as isSpecialUser, parseTapConfiguratorConfig, tapConfigToServiceCalls, validateTapConfiguratorConfig };
//# sourceMappingURL=iotize-tap-configurator.js.map
