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

var __extends = (this && this.__extends) || (function () {
    var extendStatics = function (d, b) {
        extendStatics = Object.setPrototypeOf ||
            ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
            function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
        return extendStatics(d, b);
    };
    return function (d, b) {
        extendStatics(d, b);
        function __() { this.constructor = d; }
        d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
    };
})();
/**
 * TODO move to TapConfiguratorError
 */
var ConfigKeyNotSupportedByFirmwareVersion = /** @class */ (function (_super) {
    __extends(ConfigKeyNotSupportedByFirmwareVersion, _super);
    function ConfigKeyNotSupportedByFirmwareVersion(configKey, firmwareVersion, constraint) {
        var _this = _super.call(this, "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;
        _this.configKey = configKey;
        _this.firmwareVersion = firmwareVersion;
        _this.constraint = constraint;
        return _this;
    }
    return ConfigKeyNotSupportedByFirmwareVersion;
}(Error));
var TapConfiguratorError = /** @class */ (function (_super) {
    __extends(TapConfiguratorError, _super);
    function TapConfiguratorError(code, msg, cause) {
        var _this = _super.call(this, msg, code) || this;
        _this.cause = cause;
        return _this;
    }
    TapConfiguratorError.internalError = function (msg, cause) {
        return new TapConfiguratorError(TapConfiguratorError.Code.InternalError, msg, cause);
    };
    TapConfiguratorError.unexpectedFirmwareState = function (state) {
        return new TapConfiguratorError(TapConfiguratorError.Code.UnexpectedFirmwareState, "Firmware state is not in expected mode. It should be " + FirmwareState.NORMAL + " but found " + state);
    };
    TapConfiguratorError.invalidConfiguration = function (message) {
        return new TapConfiguratorError(TapConfiguratorError.Code.InvalidSchema, message);
    };
    return TapConfiguratorError;
}(CodeError));
(function (TapConfiguratorError) {
    var Code;
    (function (Code) {
        Code["UnexpectedFirmwareState"] = "TapConfiguratorErrorUnexpectedFirmwareState";
        Code["InvalidSchema"] = "TapConfiguratorErrorIvalidSchema";
        Code["InternalError"] = "TapConfiguratorErrorInternalError";
    })(Code = TapConfiguratorError.Code || (TapConfiguratorError.Code = {}));
    var InvalidSchemaError = /** @class */ (function (_super) {
        __extends(InvalidSchemaError, _super);
        function InvalidSchemaError(validate) {
            var _this = _super.call(this, Code.InvalidSchema, "Invalid Tap configuration file:\n" + InvalidSchemaError.ajvErrorsToString(validate.errors)) || this;
            _this.validate = validate;
            _this.errors = _this.validate.errors || [];
            return _this;
        }
        InvalidSchemaError.ajvErrorsToString = function (errors) {
            // console.log('errors', JSON.stringify(errors, undefined, 4));
            return errors
                ? errors
                    .map(function (error) {
                    var message;
                    switch (error.keyword) {
                        case 'additionalProperties':
                            var 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';
        };
        return InvalidSchemaError;
    }(TapConfiguratorError));
    TapConfiguratorError.InvalidSchemaError = InvalidSchemaError;
})(TapConfiguratorError || (TapConfiguratorError = {}));

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

function getProfileIdFromConfig(profile) {
    var specialUserInfo = SPECIAL_USERS.find(function (v) { return profile.username === v.username; });
    if (specialUserInfo) {
        return specialUserInfo.id;
    }
    var profileId = profile['id']; // TODO check
    if (profileId === undefined) {
        throw TapConfiguratorError.invalidConfiguration("Profile id is required for " + profile['username']);
    }
    return profileId;
}
function isTapFirmwareVersionGreaterThanOrEqual(fwVersionString, version) {
    fwVersionString = fwVersionString
        .split(' ', 1)[0]
        .padStart(10, '0')
        .padEnd(10, '0');
    version = version.padStart(10, '0').padEnd(10, '0');
    // console.info(`${fwVersionString} >= ${version}`)
    return fwVersionString >= version;
}
function isTapFirmwareVersionLessThanOrEqual(fwVersionString, version) {
    fwVersionString = fwVersionString
        .split(' ', 1)[0]
        .padStart(10, '0')
        .padEnd(10, '0');
    version = version.padStart(10, '0').padEnd(10, '0');
    // console.info(`${fwVersionString} >= ${version}`)
    return fwVersionString <= version;
}
function enumKeyToEnumValue(value, enumMapping) {
    return toEnum(value, enumMapping);
}
function enumKeysToEnumValues(values, enumMapping) {
    return values.map(function (value) { return 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(function (v) { return username === v.username; }).length > 0;
}
function isSpecialProfileId(id) {
    return SPECIAL_USERS.filter(function (v) { return 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) {
    var array;
    if (Array.isArray(data)) {
        array = data;
    }
    else {
        array = Object.keys(data).map(function (key) { return data[key]; });
    }
    return array.filter(function (value) { return value[filterKey] == v; })[0] || undefined;
}

/**
 *   Generated file. Do not edit
 */
var ɵ0 = function (context) {
    return context.service.interface.putApplicationDataCall(configValueToBytes(context.value));
}, ɵ1 = function (context) {
    return context.service.interface.putAppNameCall(context.value);
}, ɵ2 = function (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 = function (context) {
    return context.service.interface.putUniversalLinkCall(context.value);
}, ɵ4 = function (context) {
    return context.service.interface.putAppPathCall(context.value);
}, ɵ5 = function (context) {
    return context.service.cloud.putDataLogPublishTopicCall(context.value);
}, ɵ6 = function (context) {
    return context.service.cloud.putDataLogSubscribeTopicCall(context.value);
}, ɵ7 = function (context) {
    return context.service.cloud.putDataLogUploadPeriodCall(context.value);
}, ɵ8 = function (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 = function (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 = function (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 = function (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 = function (context) {
    return context.service.cloud.putPlatformCall(enumKeyToEnumValue(context.value, CloudPlatform));
}, ɵ13 = function (context) {
    return context.service.bundle.createCall(context.param);
}, ɵ14 = function (context) {
    return context.service.bundle.putAclCall(context.param, context.value);
}, ɵ15 = function (context) {
    return context.service.bundle.putDataLogPeriodCall(context.param, context.value);
}, ɵ16 = function (context) {
    return context.service.bundle.putNameCall(context.param, context.value);
}, ɵ17 = function (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 = function (context) {
    return context.service.group.createCall(context.param);
}, ɵ19 = function (context) {
    return context.service.group.putAliasCall(context.param, context.value);
}, ɵ20 = function (context) {
    return context.service.group.changePasswordKeyCall(context.param, configValueToBytes(context.value));
}, ɵ21 = function (context) {
    return context.service.group.putSessionLifetimeCall(context.param, context.value);
}, ɵ22 = function (context) {
    return context.service.group.changePasswordCall(context.param, context.value);
}, ɵ23 = function (context) {
    return context.service.group.putNameCall(context.param, context.value);
}, ɵ24 = function (context) {
    return context.service.variable.createCall(context.param, context.value);
}, ɵ25 = function (context) {
    return context.service.variable.putVariableAddressCall(context.param, context.value);
}, ɵ26 = function (context) {
    return context.service.variable.putBundleIdCall(context.param, context.value);
}, ɵ27 = function (context) {
    return context.service.variable.putNumberOfElementsCall(context.param, context.value);
}, ɵ28 = function (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 = function (context) {
    return context.service.variable.putModbusVariableAddressCall(context.param, context.value);
}, ɵ30 = function (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 = function (context) {
    return context.service.variable.putTypeCall(context.param, context.value);
}, ɵ32 = function (context) {
    return context.service.variable.setValueCall(context.param, configValueToBytes(context.value));
}, ɵ33 = function (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 = function (context) {
    return context.service.jvm.putProfileIdCall(context.value);
}, ɵ35 = function (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));
}, ɵ36 = function (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));
}, ɵ37 = function (context) {
    return context.service.interface.putPowerOptimisationLevelCall(enumKeyToEnumValue(context.value, LowPowerOptimizationLevel));
}, ɵ38 = function (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);
}, ɵ39 = function (context) {
    return context.service.interface.putSecurityOptionsCall(context.value);
}, ɵ40 = function (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);
}, ɵ41 = function (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);
}, ɵ42 = function (context) {
    return context.service.target.putFirmwareVersionCall(context.value);
}, ɵ43 = function (context) {
    return context.service.target.putSerialModbusConfigurationCall(context.value);
}, ɵ44 = function (context) {
    return context.service.target.putProtocolCall(enumKeyToEnumValue(context.value, TargetProtocol));
}, ɵ45 = function (context) {
    return context.service.interface.putConfigVersionCall(context.value);
}, ɵ46 = function (context) {
    return context.service.interface.putAuthorizedHostProtocolCall(enumKeysToEnumValues(context.value, HostProtocol));
}, ɵ47 = function (context) {
    return context.service.interface.putHostInactivityPeriodCall(context.value);
}, ɵ48 = function (context) {
    return context.service.lora.putConfigCall(context.value);
}, ɵ49 = function (context) {
    return context.service.lora.putIdsCall(configValueToBytes(context.value));
}, ɵ50 = function (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));
}, ɵ51 = function (context) {
    return context.service.mqtt.putBrokerHostnameCall(context.value);
}, ɵ52 = function (context) {
    return context.service.mqtt.putBrokerPortCall(context.value);
}, ɵ53 = function (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);
}, ɵ54 = function (context) {
    return context.service.mqtt.putPasswordCall(context.value);
}, ɵ55 = function (context) {
    return context.service.mqtt.putRelayNetKeyCall(context.value);
}, ɵ56 = function (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);
}, ɵ57 = function (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);
}, ɵ58 = function (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);
}, ɵ59 = function (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);
}, ɵ60 = function (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);
}, ɵ61 = function (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));
}, ɵ62 = function (context) {
    return context.service.interface.putNfcPairingModeCall(enumKeyToEnumValue(context.value, NfcPairingMode));
}, ɵ63 = function (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);
}, ɵ64 = function (context) {
    return context.service.wifi.putGatewayIpCall(context.value);
}, ɵ65 = function (context) {
    return context.service.wifi.putIpCall(context.value);
}, ɵ66 = function (context) {
    return context.service.wifi.putKeyCall(context.value);
}, ɵ67 = function (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));
}, ɵ68 = function (context) {
    return context.service.wifi.putIpMaskCall(context.value);
}, ɵ69 = function (context) {
    return context.service.wifi.putModeCall(enumKeyToEnumValue(context.value, WifiMode));
}, ɵ70 = function (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));
}, ɵ71 = function (context) {
    return context.service.wifi.putSSIDCall(context.value);
}, ɵ72 = function (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));
}, ɵ73 = function (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);
};
var WRITE_CONFIGURATOR_CONFIG = {
    "application.data": ɵ0,
    "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,
    "data.bundles[]": ɵ13,
    "data.bundles[].acl[].$": ɵ14,
    "data.bundles[].dataLog.period": ɵ15,
    "data.bundles[].name": ɵ16,
    "data.features[].bundleId": ɵ17,
    "data.profiles[]": ɵ18,
    "data.profiles[].alias": ɵ19,
    "data.profiles[].key": ɵ20,
    "data.profiles[].lifeTime": ɵ21,
    "data.profiles[].password": ɵ22,
    "data.profiles[].username": ɵ23,
    "data.variables[]": ɵ24,
    "data.variables[].address": ɵ25,
    "data.variables[].bundleId": ɵ26,
    "data.variables[].length": ɵ27,
    "data.variables[].meta": ɵ28,
    "data.variables[].modbusAddress": ɵ29,
    "data.variables[].rawMeta": ɵ30,
    "data.variables[].type": ɵ31,
    "data.variables[].value": ɵ32,
    "jvm.code": ɵ33,
    "jvm.profileId": ɵ34,
    "tap.certificate.privateKey": ɵ35,
    "tap.certificate.publicKey": ɵ36,
    "tap.powerOptimization": ɵ37,
    "tap.scram.hashIteration": ɵ38,
    "tap.security": ɵ39,
    "tap.time.ntpServer": ɵ40,
    "tap.time.zone": ɵ41,
    "target.firmwareVersion": ɵ42,
    "target.protocol.config": ɵ43,
    "target.protocol.type": ɵ44,
    version: ɵ45,
    "wireless.options.authorizedProtocols": ɵ46,
    "wireless.options.inactivityTimeout": ɵ47,
    "wireless.protocols.lora.config": ɵ48,
    "wireless.protocols.lora.ids": ɵ49,
    "wireless.protocols.mqtt.brokerCertificate": ɵ50,
    "wireless.protocols.mqtt.brokerHostname": ɵ51,
    "wireless.protocols.mqtt.brokerPort": ɵ52,
    "wireless.protocols.mqtt.clientId": ɵ53,
    "wireless.protocols.mqtt.password": ɵ54,
    "wireless.protocols.mqtt.relayNetKey": ɵ55,
    "wireless.protocols.mqtt.requestTopic": ɵ56,
    "wireless.protocols.mqtt.responseTopic": ɵ57,
    "wireless.protocols.mqtt.topicPrefix": ɵ58,
    "wireless.protocols.mqtt.username": ɵ59,
    "wireless.protocols.nfc.autoLogProfileId": ɵ60,
    "wireless.protocols.nfc.connectionPriority": ɵ61,
    "wireless.protocols.nfc.pairingMode": ɵ62,
    "wireless.protocols.wifi.countryCode": ɵ63,
    "wireless.protocols.wifi.gatewayIp": ɵ64,
    "wireless.protocols.wifi.ip": ɵ65,
    "wireless.protocols.wifi.key": ɵ66,
    "wireless.protocols.wifi.keyVisibility": ɵ67,
    "wireless.protocols.wifi.mask": ɵ68,
    "wireless.protocols.wifi.mode": ɵ69,
    "wireless.protocols.wifi.protocol": ɵ70,
    "wireless.protocols.wifi.ssid": ɵ71,
    "wireless.protocols.wifi.ssidVisibility": ɵ72,
    "wireless.protocols.wifi.txPower": ɵ73
};

var TSTYPE_CLASS = {
    Uint8Array: Uint8Array
};
var ɵ0$1 = function (schema) {
    var classObject = TSTYPE_CLASS[schema];
    return function (data) {
        return !classObject || data instanceof classObject;
    };
};
var tsTypeKeyword = {
    compile: ɵ0$1
};
function getJsonSchemaValidator(schemas) {
    schemas = schemas || [
        TapConfigJsonSchema,
        CommonTypeJsonSchema,
        TapTypeJsonSchema
    ];
    var ajv = new Ajv({
        schemas: schemas
    });
    ajv.addKeyword('tsType', tsTypeKeyword);
    var schemaId = 'http://iotize.com/schemas/v1/tap-config.json'; // TODO config must match schema api file value
    var 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) {
    var validator = getJsonSchemaValidator(schemas);
    var valid = validator(config);
    if (!valid) {
        throw new TapConfiguratorError.InvalidSchemaError(validator);
    }
    return validator;
}
function parseTapConfiguratorConfig(input) {
    var config;
    try {
        config = JSON.parse(input);
    }
    catch (err) {
        throw new Error("Cannot parse Tap configurator config content. " + err.message);
    }
    validateTapConfiguratorConfig(config);
    return config;
}

var __assign = (this && this.__assign) || function () {
    __assign = Object.assign || function(t) {
        for (var s, i = 1, n = arguments.length; i < n; i++) {
            s = arguments[i];
            for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
                t[p] = s[p];
        }
        return t;
    };
    return __assign.apply(this, arguments);
};
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());
    });
};
var __generator = (this && this.__generator) || function (thisArg, body) {
    var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
    return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
    function verb(n) { return function (v) { return step([n, v]); }; }
    function step(op) {
        if (f) throw new TypeError("Generator is already executing.");
        while (_) try {
            if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
            if (y = 0, t) op = [op[0] & 2, t.value];
            switch (op[0]) {
                case 0: case 1: t = op; break;
                case 4: _.label++; return { value: op[1], done: false };
                case 5: _.label++; y = op[1]; op = [0]; continue;
                case 7: op = _.ops.pop(); _.trys.pop(); continue;
                default:
                    if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
                    if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
                    if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
                    if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
                    if (t[2]) _.ops.pop();
                    _.trys.pop(); continue;
            }
            op = body.call(thisArg, _);
        } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
        if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
    }
};
var __read = (this && this.__read) || function (o, n) {
    var m = typeof Symbol === "function" && o[Symbol.iterator];
    if (!m) return o;
    var i = m.call(o), r, ar = [], e;
    try {
        while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value);
    }
    catch (error) { e = { error: error }; }
    finally {
        try {
            if (r && !r.done && (m = i["return"])) m.call(i);
        }
        finally { if (e) throw e.error; }
    }
    return ar;
};
var __spread = (this && this.__spread) || function () {
    for (var ar = [], i = 0; i < arguments.length; i++) ar = ar.concat(__read(arguments[i]));
    return ar;
};
var __values = (this && this.__values) || function(o) {
    var s = typeof Symbol === "function" && Symbol.iterator, m = s && o[s], i = 0;
    if (m) return m.call(o);
    if (o && typeof o.length === "number") return {
        next: function () {
            if (o && i >= o.length) o = void 0;
            return { value: o && o[i++], done: !o };
        }
    };
    throw new TypeError(s ? "Object is not iterable." : "Symbol.iterator is not defined.");
};
var variableMetaConverter = new VariableMetaConfigConverter();
var IGNORE_KEYS = [
    'version',
    'tap.security'
];
var USER_CREATE_KEY = 'data.profiles[]';
var 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.apply(calls, __spread(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");
        }
        var callFactory = WRITE_CONFIGURATOR_CONFIG[key];
        return callFactory(callContext);
    }
    function baseConfigObjectToCalls(entry, configKey, pathParameter) {
        var e_1, _a;
        var calls = [];
        if (entry === undefined) {
            return calls;
        }
        else if (Array.isArray(entry)) {
            var newConfigKey = configKey + '[]';
            try {
                for (var entry_1 = __values(entry), entry_1_1 = entry_1.next(); !entry_1_1.done; entry_1_1 = entry_1.next()) {
                    var itemValue = entry_1_1.value;
                    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}"`);
                            var createBody = void 0;
                            if (newConfigKey === CREATE_VARIABLE_CONFIG_KEY) {
                                if (itemValue.rawMeta) {
                                    createBody = itemValue.rawMeta.length;
                                }
                            }
                            var createCallParam = {
                                firmwareVersion: options.firmwareVersion,
                                service: options.service,
                                value: createBody,
                                param: pathParameter
                            };
                            if (createBody === undefined) {
                                delete createCallParam.value;
                            }
                            try {
                                var 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.apply(calls, __spread(baseConfigObjectToCalls(itemValue, newConfigKey, pathParameter)));
                }
            }
            catch (e_1_1) { e_1 = { error: e_1_1 }; }
            finally {
                try {
                    if (entry_1_1 && !entry_1_1.done && (_a = entry_1.return)) _a.call(entry_1);
                }
                finally { if (e_1) throw e_1.error; }
            }
        }
        else {
            if (configKey + ".$" in WRITE_CONFIGURATOR_CONFIG) {
                try {
                    addCalls(calls, createWriteConfigCall(configKey + ".$", {
                        firmwareVersion: options.firmwareVersion,
                        service: options.service,
                        value: entry,
                        param: pathParameter
                    }));
                }
                catch (err) {
                    throw TapConfiguratorError.internalError("Cannot generate create resource request for configuration key " + configKey + " for identifier \"" + pathParameter + "\". Cause: " + err.message, err);
                }
            }
            else {
                for (var subKey in entry) {
                    var newConfigKey = configKey ? configKey + "." + subKey : subKey;
                    var 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) {
                        var callContext = {
                            firmwareVersion: options.firmwareVersion,
                            service: options.service,
                            value: value,
                            param: pathParameter
                        };
                        addCalls(calls, createWriteConfigCall(newConfigKey, callContext));
                    }
                    else if (typeof value === 'object') {
                        calls.push.apply(calls, __spread(baseConfigObjectToCalls(value, newConfigKey, pathParameter)));
                    }
                    else {
                        throw new Error("Invalid config key \"" + newConfigKey + "\" with value " + value + ". " + (pathParameter ? "Current path parameter: " + pathParameter : ''));
                    }
                }
            }
        }
        return calls;
    }
    var flattenConfig = toFlatTapConfig(config, options);
    var service = options.service;
    var calls = [
        service.interface.putConfigVersionCall("255.255.65535")
    ];
    var 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.apply(calls, __spread(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) {
    if (padValue === void 0) { 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) {
    if (padValue === void 0) { 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) {
    var variableLength = variable.length || 1;
    var variableDataType = variable.dataType === undefined ? VariableType.Data.UINT8 : VariableType.Data[variable.dataType];
    var itemByteSize = variableDataTypeToByteSize(variableDataType);
    function _encodeItem(value, index) {
        if (index === void 0) { index = 0; }
        var result;
        if (value instanceof Uint8Array) {
            result = value;
        }
        else if (isHexString(value)) {
            result = hexStringToBuffer(value);
        }
        else if (typeof value === 'number') {
            var 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;
    }
    var encodedValue;
    if (typeof variable.value === 'string' && variableDataType === VariableType.Data.ASCII) {
        encodedValue = converters.ascii.encode(variable.value);
    }
    else if (Array.isArray(variable.value)) {
        var buffers = variable.value
            .map(function (v, index) {
            return _encodeItem(v, index);
        });
        encodedValue = TapStreamWriter.mergeArrays.apply(TapStreamWriter, __spread(buffers));
    }
    else {
        encodedValue = _encodeItem(variable.value);
    }
    var 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) {
    var variableLength = variable.length || 1;
    var variableDataType = variable.dataType === undefined ? VariableType.Data.UINT8 : VariableType.Data[variable.dataType];
    var variableType = {
        domain: variable.domain === undefined ? VariableType.Domain.TARGET : VariableType.Domain[variable.domain],
        data: variableDataType
    };
    var variableFlatConfig = {
        id: variable.id,
        length: variableLength,
        type: {
            domain: VariableType.Domain[variableType.domain],
            data: VariableType.Data[variableType.data]
        },
        bundleId: bundle.id
    };
    if (variable.address !== undefined) {
        variableFlatConfig.address = variable.address;
    }
    if (variable.rawMeta !== undefined) {
        variableFlatConfig.rawMeta = typeof variable.rawMeta === 'string' ? hexStringToBuffer(variable.rawMeta) : variable.rawMeta;
    }
    if (variable.modbusAddress !== undefined) {
        variableFlatConfig.modbusAddress = variable.modbusAddress;
    }
    if (variable.meta) {
        if (typeof variable.meta === 'object') {
            variableFlatConfig.rawMeta = variableMetaConverter.encode(variable.meta);
        }
        else {
            variableFlatConfig.rawMeta = hexStringToBuffer(variable.meta);
        }
        // delete variable.meta;
    }
    if (variable.value !== undefined && variable.value !== null) {
        variableFlatConfig.value = encodeInitialVariableValue(variable, bundle);
    }
    return variableFlatConfig;
}
function toFlatTapConfig(config, options) {
    var e_2, _a, e_3, _b, e_4, _c, e_5, _d, e_6, _e, e_7, _f, e_8, _g;
    var _h, _j, _k, _l, _m, _o, _p;
    var configCopy = deepCopy(config);
    var flatProfiles = [];
    if (configCopy.target) {
        delete configCopy.target.type;
    }
    if (configCopy.data) {
        // Flaten profiles
        if (configCopy.data.profiles) {
            try {
                for (var _q = __values((_h = configCopy.data) === null || _h === void 0 ? void 0 : _h.profiles), _r = _q.next(); !_r.done; _r = _q.next()) {
                    var profile = _r.value;
                    var flatenProfile = profile;
                    if (flatenProfile.id === undefined) {
                        flatenProfile.id = getProfileIdFromConfig(flatenProfile);
                    }
                    if (isSpecialProfileId(flatenProfile.id)) {
                        delete flatenProfile.username;
                    }
                    var subProfiles = flatenProfile.users || [];
                    delete flatenProfile.users;
                    flatProfiles.push(flatenProfile);
                    try {
                        for (var subProfiles_1 = (e_3 = void 0, __values(subProfiles)), subProfiles_1_1 = subProfiles_1.next(); !subProfiles_1_1.done; subProfiles_1_1 = subProfiles_1.next()) {
                            var subProfile = subProfiles_1_1.value;
                            subProfile.alias = flatenProfile.id;
                            flatProfiles.push(subProfile);
                            if (isSpecialProfileId(subProfile.id)) {
                                delete subProfile.username;
                            }
                        }
                    }
                    catch (e_3_1) { e_3 = { error: e_3_1 }; }
                    finally {
                        try {
                            if (subProfiles_1_1 && !subProfiles_1_1.done && (_b = subProfiles_1.return)) _b.call(subProfiles_1);
                        }
                        finally { if (e_3) throw e_3.error; }
                    }
                }
            }
            catch (e_2_1) { e_2 = { error: e_2_1 }; }
            finally {
                try {
                    if (_r && !_r.done && (_a = _q.return)) _a.call(_q);
                }
                finally { if (e_2) throw e_2.error; }
            }
        }
        // Flaten acls and special features
        var flatSpecialFeatures = [];
        var flatVariables = [];
        if (configCopy.data.bundles) {
            try {
                for (var _s = __values(configCopy.data.bundles), _t = _s.next(); !_t.done; _t = _s.next()) {
                    var bundle = _t.value;
                    var acls = bundle.acls || {};
                    delete bundle.acls;
                    var flatAcls = [];
                    var _loop_1 = function (username) {
                        var acl = acls[username];
                        var profile = flatProfiles.find(function (p) { return p.username === username; });
                        if (!profile) {
                            profile = SPECIAL_USERS.find(function (p) { return 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
                        });
                    };
                    for (var username in acls) {
                        _loop_1(username);
                    }
                    if (flatAcls.length > 0) {
                        bundle.acl = flatAcls;
                    }
                    var variables = bundle.variables || [];
                    delete bundle.variables;
                    try {
                        for (var variables_1 = (e_5 = void 0, __values(variables)), variables_1_1 = variables_1.next(); !variables_1_1.done; variables_1_1 = variables_1.next()) {
                            var variable = variables_1_1.value;
                            flatVariables.push(toVariableFlatConfig(variable, bundle));
                        }
                    }
                    catch (e_5_1) { e_5 = { error: e_5_1 }; }
                    finally {
                        try {
                            if (variables_1_1 && !variables_1_1.done && (_d = variables_1.return)) _d.call(variables_1);
                        }
                        finally { if (e_5) throw e_5.error; }
                    }
                    var features = bundle.features || [];
                    delete bundle.features;
                    var _loop_2 = function (featureKey) {
                        var featureId = enumKeyToEnumValue(featureKey, SpecialFeature);
                        if (flatSpecialFeatures.find(function (f) { return f.id === featureId; })) {
                            throw new Error("Special feature \"" + featureId + "\" has already be given to another bundle");
                        }
                        flatSpecialFeatures.push({
                            id: featureId,
                            bundleId: bundle.id
                        });
                    };
                    try {
                        for (var features_1 = (e_6 = void 0, __values(features)), features_1_1 = features_1.next(); !features_1_1.done; features_1_1 = features_1.next()) {
                            var featureKey = features_1_1.value;
                            _loop_2(featureKey);
                        }
                    }
                    catch (e_6_1) { e_6 = { error: e_6_1 }; }
                    finally {
                        try {
                            if (features_1_1 && !features_1_1.done && (_e = features_1.return)) _e.call(features_1);
                        }
                        finally { if (e_6) throw e_6.error; }
                    }
                }
            }
            catch (e_4_1) { e_4 = { error: e_4_1 }; }
            finally {
                try {
                    if (_t && !_t.done && (_c = _s.return)) _c.call(_s);
                }
                finally { if (e_4) throw e_4.error; }
            }
        }
        if (flatSpecialFeatures.length > 0) {
            configCopy.data.features = flatSpecialFeatures;
        }
        if (flatVariables.length > 0) {
            configCopy.data.variables = flatVariables;
        }
    }
    if ((_k = (_j = config.tap) === null || _j === void 0 ? void 0 : _j.security) === null || _k === void 0 ? void 0 : _k.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;
        }
    }
    var hashIteration = (_m = (_l = configCopy.tap) === null || _l === void 0 ? void 0 : _l.scram) === null || _m === void 0 ? void 0 : _m.hashIteration;
    var _loop_3 = function (specialProfile) {
        if (!flatProfiles.find(function (p) { return p.id === specialProfile.id; })) {
            flatProfiles.push({
                id: specialProfile.id,
                password: specialProfile.password || specialProfile.username,
                lifeTime: specialProfile.lifeTime || 30
            });
        }
    };
    try {
        for (var _u = __values([
            ADMIN_USER,
            SUPERVISOR_USER
        ]), _v = _u.next(); !_v.done; _v = _u.next()) {
            var specialProfile = _v.value;
            _loop_3(specialProfile);
        }
    }
    catch (e_7_1) { e_7 = { error: e_7_1 }; }
    finally {
        try {
            if (_v && !_v.done && (_f = _u.return)) _f.call(_u);
        }
        finally { if (e_7) throw e_7.error; }
    }
    try {
        for (var flatProfiles_1 = __values(flatProfiles), flatProfiles_1_1 = flatProfiles_1.next(); !flatProfiles_1_1.done; flatProfiles_1_1 = flatProfiles_1.next()) {
            var profile = flatProfiles_1_1.value;
            prepareUserPassword(profile, ((_o = configCopy === null || configCopy === void 0 ? void 0 : configCopy.tap) === null || _o === void 0 ? void 0 : _o.security) || {}, function (input) {
                if (!hashIteration) {
                    throw TapConfiguratorError.internalError("Hash iteration value must be set");
                }
                var salt = (options === null || options === void 0 ? void 0 : options.saltGenerator) ? options.saltGenerator() : DEFAULT_SCRAM_SALT_GENERATOR();
                return ScramAuth.createScramPasswordKey(input, {
                    salt: salt,
                    iterationNumber: hashIteration
                });
            });
        }
    }
    catch (e_8_1) { e_8 = { error: e_8_1 }; }
    finally {
        try {
            if (flatProfiles_1_1 && !flatProfiles_1_1.done && (_g = flatProfiles_1.return)) _g.call(flatProfiles_1);
        }
        finally { if (e_8) throw e_8.error; }
    }
    if (!configCopy.data) {
        configCopy.data = {};
    }
    configCopy.data.profiles = flatProfiles;
    if ((_p = configCopy.application) === null || _p === void 0 ? void 0 : _p.uri) {
        if (typeof configCopy.application.uri === 'object') {
            var 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) {
            var 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) {
            var 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(observer, step, context, extraData) {
    observer.next(__assign({ step: step, progress: {
            loaded: context.index + 1,
            total: context.total
        } }, (extraData || {})));
}
/**
 * Configure tap from a configuration file schema
 *
 */
var TapConfiguratorWriter = /** @class */ (function () {
    function TapConfiguratorWriter(options, host) {
        if (options === void 0) { options = {
            delayMs: 400
        }; }
        this.options = options;
        this.host = host;
    }
    TapConfiguratorWriter.prototype.createConfiguratorForTap = function (tap, config, options) {
        return __awaiter(this, void 0, void 0, function () {
            var firmwareVersion;
            return __generator(this, function (_a) {
                switch (_a.label) {
                    case 0: return [4 /*yield*/, tap.service.device.getFirmwareVersion()];
                    case 1:
                        firmwareVersion = (_a.sent()).body();
                        return [2 /*return*/, this.configure(tap, firmwareVersion, config, options)];
                }
            });
        });
    };
    TapConfiguratorWriter.prototype.configure = function (tap, firmwareVersion, config, options) {
        var _this = this;
        var _a, _b;
        var configureContext = {
            tap: tap,
            options: config.meta || {},
            tapConfig: config.config
        };
        var operations = [];
        if (!(options === null || options === void 0 ? void 0 : options.skipSchemaValidation)) {
            operations.push(function (observer, context) { return __awaiter(_this, void 0, void 0, function () {
                return __generator(this, function (_a) {
                    stepEvent(observer, TapConfiguratorWriter.Step.validatesConfig, context);
                    validateTapConfiguratorConfig(config);
                    return [2 /*return*/];
                });
            }); });
        }
        if (!configureContext.options.skipFactoryReset) {
            operations.push(function (observer, context) { return __awaiter(_this, void 0, void 0, function () {
                return __generator(this, function (_a) {
                    switch (_a.label) {
                        case 0:
                            stepEvent(observer, TapConfiguratorWriter.Step.factoryReset, context);
                            return [4 /*yield*/, tap.service.device.factoryReset()];
                        case 1:
                            (_a.sent()).successful();
                            return [2 /*return*/];
                    }
                });
            }); });
        }
        var 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(function (observer, context) { return __awaiter(_this, void 0, void 0, function () {
                return __generator(this, function (_a) {
                    switch (_a.label) {
                        case 0:
                            stepEvent(observer, TapConfiguratorWriter.Step.enableSecureCom, context);
                            if (!useScram) return [3 /*break*/, 2];
                            return [4 /*yield*/, tap.encryption.start()];
                        case 1:
                            _a.sent();
                            _a.label = 2;
                        case 2: return [2 /*return*/];
                    }
                });
            }); });
        }
        var calls = tapConfigToServiceCalls(configureContext.tapConfig, {
            firmwareVersion: 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(function (observer, context) { return __awaiter(_this, void 0, void 0, function () {
            return __generator(this, function (_a) {
                switch (_a.label) {
                    case 0:
                        stepEvent(observer, TapConfiguratorWriter.Step.logout, context);
                        return [4 /*yield*/, configureContext.tap.auth.logout()];
                    case 1:
                        _a.sent();
                        tap.auth.clearCache();
                        return [2 /*return*/];
                }
            });
        }); });
        return StepOperations.sequential(operations);
    };
    TapConfiguratorWriter.prototype.setupConfigState = function (tap) {
        return __awaiter(this, void 0, void 0, function () {
            var state;
            return __generator(this, function (_a) {
                switch (_a.label) {
                    case 0: return [4 /*yield*/, tap.service.firmware.getState()];
                    case 1:
                        state = (_a.sent()).body();
                        if (state !== FirmwareState$1.NORMAL) {
                            throw TapConfiguratorError.unexpectedFirmwareState(state);
                        }
                        return [4 /*yield*/, tap.service.interface.putConfigVersion("255.255.65535")];
                    case 2:
                        (_a.sent()).successful();
                        return [2 /*return*/];
                }
            });
        });
    };
    // TODO move from here
    TapConfiguratorWriter.prototype.isConfigured = function (tap) {
        return __awaiter(this, void 0, void 0, function () {
            var configVersionCalled, intValue;
            return __generator(this, function (_a) {
                switch (_a.label) {
                    case 0: return [4 /*yield*/, tap.service.interface.getConfigVersion()];
                    case 1:
                        configVersionCalled = _a.sent();
                        configVersionCalled.successful();
                        intValue = configVersionCalled.body(NumberConverter.uint32());
                        return [2 /*return*/, intValue !== 0];
                }
            });
        });
    };
    /***
     *
     */
    TapConfiguratorWriter.prototype.applyConfig = function (tap, newVersion) {
        if (newVersion === void 0) { newVersion = "1.0.0"; }
        return __awaiter(this, void 0, void 0, function () {
            return __generator(this, function (_a) {
                switch (_a.label) {
                    case 0: return [4 /*yield*/, this.sleep(this.options.delayMs)];
                    case 1:
                        _a.sent();
                        return [4 /*yield*/, tap.service.interface.putConfigVersion(newVersion)];
                    case 2:
                        (_a.sent()).successful();
                        return [4 /*yield*/, this.sleep(this.options.delayMs)];
                    case 3:
                        _a.sent();
                        return [4 /*yield*/, tap.service.interface.reloadConfig()];
                    case 4:
                        (_a.sent()).successful();
                        return [4 /*yield*/, this.sleep(this.options.delayMs)];
                    case 5:
                        _a.sent();
                        return [2 /*return*/];
                }
            });
        });
    };
    TapConfiguratorWriter.prototype.addConfigurCalls = function (configureContext, calls, operations) {
        var _this = this;
        calls.forEach(function (callOrError, index) {
            operations.push(function (observer, context) { return __awaiter(_this, void 0, void 0, function () {
                return __generator(this, function (_a) {
                    switch (_a.label) {
                        case 0:
                            if (!(callOrError instanceof Error)) return [3 /*break*/, 1];
                            // TODO 
                            stepEvent(observer, TapConfiguratorWriter.Step.executeCall, context, {
                                error: callOrError,
                                stepProgress: {
                                    total: calls.length,
                                    loaded: index + 1
                                }
                                // call: callOrError
                            });
                            return [3 /*break*/, 3];
                        case 1:
                            stepEvent(observer, TapConfiguratorWriter.Step.executeCall, context, {
                                stepProgress: {
                                    total: calls.length,
                                    loaded: index + 1
                                },
                                call: callOrError
                            });
                            return [4 /*yield*/, configureContext.tap.lwm2m.execute(callOrError)];
                        case 2:
                            (_a.sent()).successful();
                            _a.label = 3;
                        case 3: return [2 /*return*/];
                    }
                });
            }); });
        });
    };
    TapConfiguratorWriter.prototype.sleep = function (delayMs) {
        return sleep(delayMs);
    };
    return TapConfiguratorWriter;
}());
(function (TapConfiguratorWriter) {
    ;
    var 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
 */
var ɵ0$2 = function (context) {
    return context.service.interface.getApplicationDataCall();
}, ɵ1$1 = function (context) {
    return context.service.interface.getAppNameCall();
}, ɵ2$1 = function (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$1 = function (context) {
    return context.service.interface.getUniversalLinkCall();
}, ɵ4$1 = function (context) {
    return context.service.interface.getAppPathCall();
}, ɵ5$1 = function (context) {
    return context.service.cloud.getDataLogPublishTopicCall();
}, ɵ6$1 = function (context) {
    return context.service.cloud.getDataLogSubscribeTopicCall();
}, ɵ7$1 = function (context) {
    return context.service.cloud.getDataLogUploadPeriodCall();
}, ɵ8$1 = function (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$1 = function (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$1 = function (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$1 = function (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$1 = function (context) {
    return context.service.cloud.getPlatformCall();
}, ɵ13$1 = function (context) {
    return context.service.interface.getConfigFormatVersionCall();
}, ɵ14$1 = function (context) {
    return context.service.bundle.getDataLogPeriodCall(context.param);
}, ɵ15$1 = function (context) {
    return context.service.bundle.getNameCall(context.param);
}, ɵ16$1 = function (context) {
    return context.service.group.getAliasCall(context.param);
}, ɵ17$1 = function (context) {
    return context.service.group.getSessionLifetimeCall(context.param);
}, ɵ18$1 = function (context) {
    return context.service.group.getNameCall(context.param);
}, ɵ19$1 = function (context) {
    return context.service.variable.getVariableAddressCall(context.param);
}, ɵ20$1 = function (context) {
    return context.service.variable.getBundleIdCall(context.param);
}, ɵ21$1 = function (context) {
    return context.service.variable.getNumberOfElementsCall(context.param);
}, ɵ22$1 = function (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$1 = function (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$1 = function (context) {
    return context.service.variable.getTypeCall(context.param);
}, ɵ25$1 = function (context) {
    return context.service.jvm.getProfileIdCall();
}, ɵ26$1 = function (context) {
    return context.service.interface.getPowerOptimisationLevelCall();
}, ɵ27$1 = function (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();
}, ɵ28$1 = function (context) {
    return context.service.interface.getSecurityOptionsCall();
}, ɵ29$1 = function (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();
}, ɵ30$1 = function (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();
}, ɵ31$1 = function (context) {
    return context.service.target.getFirmwareVersionCall();
}, ɵ32$1 = function (context) {
    return context.service.target.getSerialModbusConfigurationCall();
}, ɵ33$1 = function (context) {
    return context.service.target.getProtocolCall();
}, ɵ34$1 = function (context) {
    return context.service.interface.getConfigVersionCall();
}, ɵ35$1 = function (context) {
    return context.service.interface.getAuthorizedHostProtocolCall();
}, ɵ36$1 = function (context) {
    return context.service.interface.getHostInactivityPeriodCall();
}, ɵ37$1 = function (context) {
    return context.service.lora.getConfigCall();
}, ɵ38$1 = function (context) {
    return context.service.lora.getIdsCall();
}, ɵ39$1 = function (context) {
    return context.service.mqtt.getBrokerHostnameCall();
}, ɵ40$1 = function (context) {
    return context.service.mqtt.getBrokerPortCall();
}, ɵ41$1 = function (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();
}, ɵ42$1 = function (context) {
    return context.service.mqtt.getPasswordCall();
}, ɵ43$1 = function (context) {
    return context.service.mqtt.getRelayNetKeyCall();
}, ɵ44$1 = function (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();
}, ɵ45$1 = function (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();
}, ɵ46$1 = function (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();
}, ɵ47$1 = function (context) {
    return context.service.interface.getNfcPairingModeCall();
}, ɵ48$1 = function (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();
}, ɵ49$1 = function (context) {
    return context.service.wifi.getGatewayIpCall();
}, ɵ50$1 = function (context) {
    return context.service.wifi.getIpCall();
}, ɵ51$1 = function (context) {
    return context.service.wifi.getKeyCall();
}, ɵ52$1 = function (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();
}, ɵ53$1 = function (context) {
    return context.service.wifi.getIpMaskCall();
}, ɵ54$1 = function (context) {
    return context.service.wifi.getModeCall();
}, ɵ55$1 = function (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();
}, ɵ56$1 = function (context) {
    return context.service.wifi.getSSIDCall();
}, ɵ57$1 = function (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();
}, ɵ58$1 = function (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();
};
var READ_CONFIGURATOR_CONFIG = {
    "application.data": ɵ0$2,
    "application.name": ɵ1$1,
    "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,
    configFormatVersion: ɵ13$1,
    "data.bundles[].dataLog.period": ɵ14$1,
    "data.bundles[].name": ɵ15$1,
    "data.profiles[].alias": ɵ16$1,
    "data.profiles[].lifeTime": ɵ17$1,
    "data.profiles[].username": ɵ18$1,
    "data.variables[].address": ɵ19$1,
    "data.variables[].bundleId": ɵ20$1,
    "data.variables[].length": ɵ21$1,
    "data.variables[].meta": ɵ22$1,
    "data.variables[].rawMeta": ɵ23$1,
    "data.variables[].type": ɵ24$1,
    "jvm.profileId": ɵ25$1,
    "tap.powerOptimization": ɵ26$1,
    "tap.scram.hashIteration": ɵ27$1,
    "tap.security": ɵ28$1,
    "tap.time.ntpServer": ɵ29$1,
    "tap.time.zone": ɵ30$1,
    "target.firmwareVersion": ɵ31$1,
    "target.protocol.config": ɵ32$1,
    "target.protocol.type": ɵ33$1,
    version: ɵ34$1,
    "wireless.options.authorizedProtocols": ɵ35$1,
    "wireless.options.inactivityTimeout": ɵ36$1,
    "wireless.protocols.lora.config": ɵ37$1,
    "wireless.protocols.lora.ids": ɵ38$1,
    "wireless.protocols.mqtt.brokerHostname": ɵ39$1,
    "wireless.protocols.mqtt.brokerPort": ɵ40$1,
    "wireless.protocols.mqtt.clientId": ɵ41$1,
    "wireless.protocols.mqtt.password": ɵ42$1,
    "wireless.protocols.mqtt.relayNetKey": ɵ43$1,
    "wireless.protocols.mqtt.username": ɵ44$1,
    "wireless.protocols.nfc.autoLogProfileId": ɵ45$1,
    "wireless.protocols.nfc.connectionPriority": ɵ46$1,
    "wireless.protocols.nfc.pairingMode": ɵ47$1,
    "wireless.protocols.wifi.countryCode": ɵ48$1,
    "wireless.protocols.wifi.gatewayIp": ɵ49$1,
    "wireless.protocols.wifi.ip": ɵ50$1,
    "wireless.protocols.wifi.key": ɵ51$1,
    "wireless.protocols.wifi.keyVisibility": ɵ52$1,
    "wireless.protocols.wifi.mask": ɵ53$1,
    "wireless.protocols.wifi.mode": ɵ54$1,
    "wireless.protocols.wifi.protocol": ɵ55$1,
    "wireless.protocols.wifi.ssid": ɵ56$1,
    "wireless.protocols.wifi.ssidVisibility": ɵ57$1,
    "wireless.protocols.wifi.txPower": ɵ58$1
};

var __assign$1 = (this && this.__assign) || function () {
    __assign$1 = Object.assign || function(t) {
        for (var s, i = 1, n = arguments.length; i < n; i++) {
            s = arguments[i];
            for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
                t[p] = s[p];
        }
        return t;
    };
    return __assign$1.apply(this, arguments);
};
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());
    });
};
var __generator$1 = (this && this.__generator) || function (thisArg, body) {
    var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
    return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
    function verb(n) { return function (v) { return step([n, v]); }; }
    function step(op) {
        if (f) throw new TypeError("Generator is already executing.");
        while (_) try {
            if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
            if (y = 0, t) op = [op[0] & 2, t.value];
            switch (op[0]) {
                case 0: case 1: t = op; break;
                case 4: _.label++; return { value: op[1], done: false };
                case 5: _.label++; y = op[1]; op = [0]; continue;
                case 7: op = _.ops.pop(); _.trys.pop(); continue;
                default:
                    if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
                    if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
                    if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
                    if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
                    if (t[2]) _.ops.pop();
                    _.trys.pop(); continue;
            }
            op = body.call(thisArg, _);
        } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
        if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
    }
};
function stepEvent$1(observer, step, context, extraData) {
    observer.next(__assign$1({ step: step, progress: {
            loaded: context.index + 1,
            total: context.total
        } }, (extraData || {})));
}
/**
 * Configure tap from a configuration file schema
 *
 */
var TapConfiguratorReader = /** @class */ (function () {
    function TapConfiguratorReader(calls) {
        if (calls === void 0) { calls = READ_CONFIGURATOR_CONFIG; }
        this.calls = calls;
    }
    TapConfiguratorReader.prototype.createReadOperations = function (tap) {
        var configureContext = {
            tap: tap
        };
        var operations = [];
        // const calls = READ_CONFIGURATOR_CONFIG;
        var outputConfig = {};
        this.addConfigurCalls(configureContext, this.calls, operations, outputConfig, tap);
        return StepOperations.sequential(operations);
    };
    TapConfiguratorReader.prototype.addConfigurCalls = function (configureContext, calls, operations, outputConfig, tap) {
        var _this = this;
        var index = 0;
        var _loop_1 = function (key) {
            var context_1 = {
                firmwareVersion: '9999.9999',
                service: tap.service,
                param: undefined // TODO 
            };
            var configKey = key;
            var factoryFct = calls[configKey];
            if (!factoryFct) {
                index++;
                return "continue";
            }
            var callOrError = factoryFct(context_1);
            operations.push(function (observer, context) { return __awaiter$1(_this, void 0, void 0, function () {
                var _a, _b, error_1;
                return __generator$1(this, function (_c) {
                    switch (_c.label) {
                        case 0:
                            if (!(callOrError instanceof Error)) return [3 /*break*/, 1];
                            stepEvent$1(observer, TapConfiguratorReader.Step.readKeyError, context, {
                                configKey: configKey,
                                error: callOrError,
                            });
                            return [3 /*break*/, 4];
                        case 1:
                            _c.trys.push([1, 3, , 4]);
                            _a = outputConfig;
                            _b = configKey;
                            return [4 /*yield*/, configureContext.tap.lwm2m.execute(callOrError)];
                        case 2:
                            _a[_b] = (_c.sent()).body();
                            stepEvent$1(observer, TapConfiguratorReader.Step.readKeySuccess, context, {
                                configKey: configKey,
                                value: outputConfig[configKey],
                                call: callOrError
                            });
                            return [3 /*break*/, 4];
                        case 3:
                            error_1 = _c.sent();
                            stepEvent$1(observer, TapConfiguratorReader.Step.readKeyError, context, {
                                configKey: configKey,
                                call: callOrError,
                                error: error_1
                            });
                            return [3 /*break*/, 4];
                        case 4: return [2 /*return*/];
                    }
                });
            }); });
            index++;
        };
        for (var key in calls) {
            _loop_1(key);
        }
        operations.push(function (observer, context) { return __awaiter$1(_this, void 0, void 0, function () {
            return __generator$1(this, function (_a) {
                stepEvent$1(observer, TapConfiguratorReader.Step.done, context, {
                    config: outputConfig
                });
                return [2 /*return*/];
            });
        }); });
    };
    TapConfiguratorReader.prototype.sleep = function (delayMs) {
        return sleep(delayMs);
    };
    return TapConfiguratorReader;
}());
(function (TapConfiguratorReader) {
    var 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
 */
var TapConfiguratorConfigBuilder = /** @class */ (function () {
    function TapConfiguratorConfigBuilder(_schema) {
        this._schema = _schema;
    }
    Object.defineProperty(TapConfiguratorConfigBuilder.prototype, "configData", {
        get: function () {
            if (!this._schema.config.data) {
                this._schema.config.data = {};
            }
            return this._schema.config.data;
        },
        enumerable: true,
        configurable: true
    });
    Object.defineProperty(TapConfiguratorConfigBuilder.prototype, "profiles", {
        get: function () {
            if (!this.configData.profiles) {
                this.configData.profiles = [];
            }
            return this.configData.profiles;
        },
        enumerable: true,
        configurable: true
    });
    Object.defineProperty(TapConfiguratorConfigBuilder.prototype, "tapConfig", {
        get: function () {
            if (!this._schema.config.tap) {
                this._schema.config.tap = {};
            }
            return this._schema.config.tap;
        },
        enumerable: true,
        configurable: true
    });
    /**
     * @deprecated
     * TODO move from here as factory method should not polute the class
     */
    TapConfiguratorConfigBuilder.default = function () {
        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
     */
    TapConfiguratorConfigBuilder.prototype.appName = function (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
     */
    TapConfiguratorConfigBuilder.prototype.user = function (username, editor) {
        var userDataIndex = this.profiles.findIndex(function (p) { return p.username === username; });
        var userData = userDataIndex >= 0 ? this.profiles[userDataIndex] : undefined;
        if (!userData) {
            // TODO check SpecialProfileConfig
            userData = {
                username: username,
                lifeTime: 300,
                password: username,
            };
        }
        userData = editor(userData);
        this.profiles[userDataIndex] = userData;
        return this;
    };
    /**
     * Enable/disable scram
     * @param enabled true to enable scram
     */
    TapConfiguratorConfigBuilder.prototype.scram = function (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;
    };
    TapConfiguratorConfigBuilder.prototype.build = function () {
        return this._schema;
    };
    return TapConfiguratorConfigBuilder;
}());

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

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