import common from '@iotize/common';
import byteConverter from '@iotize/common/byte-converter';
import dataAccessor from '@iotize/common/data-accessor';
import deviceClient from '@iotize/device-client.js';
import api from '@iotize/device-client.js/client/api';
import impl from '@iotize/device-client.js/client/impl';
import v1 from '@iotize/device-client.js/config/schema/v1';
import ajv from 'ajv';
import buffer from 'buffer';
import fastXmlParser from 'fast-xml-parser';
import rxjs from 'rxjs';
import byteStream from '@iotize/common/byte-stream';

var commonjsGlobal = typeof globalThis !== 'undefined' ? globalThis : typeof window !== 'undefined' ? window : typeof global !== 'undefined' ? global : typeof self !== 'undefined' ? self : {};

function commonjsRequire () {
	throw new Error('Dynamic requires are not currently supported by rollup-plugin-commonjs');
}

function unwrapExports (x) {
	return x && x.__esModule && Object.prototype.hasOwnProperty.call(x, 'default') ? x['default'] : x;
}

function createCommonjsModule(fn, module) {
	return module = { exports: {} }, fn(module, module.exports), module.exports;
}

function getCjsExportFromNamespace (n) {
	return n && n['default'] || n;
}

var logger = createCommonjsModule(function (module, exports) {
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });

exports.debug = common.createDebugger('@iotize:device-client.js/configurator');

});

var logger$1 = unwrapExports(logger);
var logger_1 = logger.debug;

var definitions = createCommonjsModule(function (module, exports) {
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });

class InvalidTapConfig extends Error {
}
exports.InvalidTapConfig = InvalidTapConfig;
var TapConfigurationErrorCode;
(function (TapConfigurationErrorCode) {
    TapConfigurationErrorCode["UnexpectedFirmwareState"] = "UnexpectedFirmwareState";
    TapConfigurationErrorCode["InvalidConfiguration"] = "InvalidConfiguration";
})(TapConfigurationErrorCode = exports.TapConfigurationErrorCode || (exports.TapConfigurationErrorCode = {}));
class TapConfigurationError extends Error {
    constructor(code, msg) {
        super(msg);
        this.code = code;
    }
    static unexpectedFirmwareState(state) {
        return new TapConfigurationError(TapConfigurationErrorCode.UnexpectedFirmwareState, `Firmware state is not in expected mode. It should be ${deviceClient.FirmwareState.NORMAL} but found ${state}`);
    }
    static invalidConfiguration(message) {
        return new TapConfigurationError(TapConfigurationErrorCode.InvalidConfiguration, message);
    }
}
exports.TapConfigurationError = TapConfigurationError;

});

var definitions$1 = unwrapExports(definitions);
var definitions_1 = definitions.InvalidTapConfig;
var definitions_2 = definitions.TapConfigurationErrorCode;
var definitions_3 = definitions.TapConfigurationError;

var util = createCommonjsModule(function (module, exports) {
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });

exports.SPECIAL_USERS = [
    {
        username: 'anonymous',
        id: 0,
        lifeTime: 300
    },
    {
        username: 'admin',
        password: 'admin',
        id: 0xffff,
        lifeTime: 300
    },
    {
        username: 'supervisor',
        password: 'supervisor',
        id: 0xfffe,
        lifeTime: 300
    }
];
function toEnum(value, enumMapping) {
    if (!value) {
        throw new Error(`Missing first attribute for toEnum. ${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];
}
exports.toEnum = toEnum;
function enumToString(value, mapping) {
    if (!(value in mapping)) {
        throw new Error(`Missing mapping for ${value}. Available: ${Object.keys(mapping).join(', ')}`);
    }
    return mapping[value];
}
exports.enumToString = enumToString;
function strToHex(input) {
    return byteConverter.hexStringToBuffer(input.startsWith('0x') ? input.substr(2) : input);
}
exports.strToHex = strToHex;
function isSpecialUser(username) {
    return exports.SPECIAL_USERS.filter(v => username == v.username).length > 0;
}
exports.isSpecialUser = isSpecialUser;

});

var util$1 = unwrapExports(util);
var util_1 = util.SPECIAL_USERS;
var util_2 = util.toEnum;
var util_3 = util.enumToString;
var util_4 = util.strToHex;
var util_5 = util.isSpecialUser;

var deviceClientExtension = createCommonjsModule(function (module, exports) {
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });



class UsbConfigToApiBodyConverter {
    encode(conf) {
        if (!conf) {
            return {
                aliases: []
            };
        }
        return {
            aliases: (conf.aliases || []).map(alias => {
                return {
                    vid: util.strToHex(alias.pid),
                    pid: util.strToHex(alias.pid),
                    equ: alias.equ
                };
            })
        };
    }
    decode(conf) {
        if (!conf) {
            return {
                aliases: []
            };
        }
        return {
            aliases: (conf.aliases || []).map((alias) => {
                return {
                    vid: `0x${byteConverter.bufferToHexString(alias.vid)}`,
                    pid: `0x${byteConverter.bufferToHexString(alias.pid)}`,
                    equ: alias.equ
                };
            })
        };
    }
}
exports.UsbConfigToApiBodyConverter = UsbConfigToApiBodyConverter;
// export class ProtocolConfigurationDataConverter implements BodyConverter<ProtocolConfigurationDataConfig> {
//   constructor(public uartSettingsConverter: UartSettingsConverter = new UartSettingsConverter(),
//     public usbConfigConverter = new UsbConfigConverter(),
//     public protocolConfDataInner = new ProtocolConfigurationDataConfigConverter()) {
//     this.usbConverter = new ArrayConverter(usbConfigConverter, {
//       sizeOfItem: 5
//     });
//   }
//   decode(data: Uint8Array | TapStreamReader): ProtocolConfigurationDataConfig {
//     let stream = (data instanceof TapStreamReader) ? data : TapStreamReader.create(data)
//     let uartSettings = this.uartSettingsConverter.decode(stream);
//     let config = this.protocolConfDataInner.decode(uartSettings);
//     let usbConfig = this.usbConfigConverter.decode(stream as any);
//     // TODO add leds
//     return {
//       serial: config.serial,
//       modbus: config.modbus,
//       usb: usbConfig
//     }
//   }
//   encode(model: ProtocolConfigurationDataConfig, stream: TapStreamWriter = new TapStreamWriter()): Uint8Array {
//     let serialSettings = this.protocolConfDataInner.encode(model);
//     stream.writeUartSettings(serialSettings);
//     if (model.usb && model.usb.aliases) {
//       let extraSettings = this.usbConverter.encode(model.usb.aliases);
//       stream.writeBytes(extraSettings);
//     }
//     // TODO add leds
//     return stream.toBytes;
//   }
// }
class ProtocolConfigurationDataConfigConverter {
    constructor(usbConfigConverter = new UsbConfigToApiBodyConverter()) {
        this.usbConfigConverter = usbConfigConverter;
    }
    encode(settings) {
        let usbSettings = {
            aliases: []
        };
        if (settings.usb) {
            usbSettings = this.usbConfigConverter.encode(settings.usb);
        }
        return {
            baudRate: settings.serial.baudRate,
            bitParity: util.toEnum(settings.serial.parity, deviceClient.UartSettings.BitParity),
            dataBitsLength: util.toEnum(`_${settings.serial.dataBits}`, deviceClient.UartSettings.DataBitsLength),
            handshakeDelimiter: util.toEnum(settings.serial.delimiter, deviceClient.UartSettings.HandshakeDelimiter),
            handshakeValue: util.toEnum(settings.serial.hardwareFlowControl, deviceClient.UartSettings.Handshake),
            physicalPort: util.toEnum(settings.serial.physicalPort || 'AUTO', deviceClient.UartSettings.PhysicalPort),
            stopBit: util.toEnum(settings.serial.stopBits, deviceClient.UartSettings.StopBit),
            timeout: settings.serial.timeout || 0,
            slv: settings.modbus.slave || 0,
            ofs: false,
            identification: settings.LEDs
                ? settings.LEDs.identification || false
                : false,
            ledStatus: settings.LEDs ? settings.LEDs.enabled || false : false,
            usbSettings: usbSettings
        };
    }
    decode(protocolConf) {
        // debug('Converting UartSettings to protocolConf ', protocolConf);
        let modbusConfig = {
            offsetFirstRegister: protocolConf.ofs,
            slave: protocolConf.slv
        };
        let serialConfig = {
            baudRate: protocolConf.baudRate,
            dataBits: parseInt(util.enumToString(protocolConf.dataBitsLength, deviceClient.UartSettings.DataBitsLength).substr(1)),
            delimiter: util.enumToString(protocolConf.handshakeDelimiter, deviceClient.UartSettings.HandshakeDelimiter),
            parity: util.enumToString(protocolConf.bitParity, deviceClient.UartSettings.BitParity),
            stopBits: util.enumToString(protocolConf.stopBit, deviceClient.UartSettings.StopBit),
            physicalPort: util.enumToString(protocolConf.physicalPort, deviceClient.UartSettings.PhysicalPort),
            hardwareFlowControl: util.enumToString(protocolConf.handshakeValue, deviceClient.UartSettings.Handshake),
            timeout: protocolConf.timeout
        };
        let leds = {
            enabled: protocolConf.ledStatus,
            identification: protocolConf.identification
        };
        let usbConfig;
        if (protocolConf.usbSettings) {
            usbConfig = this.usbConfigConverter.decode(protocolConf.usbSettings);
        }
        return {
            modbus: modbusConfig,
            serial: serialConfig,
            LEDs: leds,
            usb: usbConfig
        };
    }
}
exports.ProtocolConfigurationDataConfigConverter = ProtocolConfigurationDataConfigConverter;

});

var deviceClientExtension$1 = unwrapExports(deviceClientExtension);
var deviceClientExtension_1 = deviceClientExtension.UsbConfigToApiBodyConverter;
var deviceClientExtension_2 = deviceClientExtension.ProtocolConfigurationDataConfigConverter;

var tapAppPathConverter = createCommonjsModule(function (module, exports) {
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
var TapPathType;
(function (TapPathType) {
    /**
     * Android application record
     */
    TapPathType["AAR"] = "AAR";
    /**
     * Custom URL
     */
    TapPathType["URL"] = "URL";
    /**
     * Primer cloud hosted application url
     */
    TapPathType["PRIMER_CLOUD_WEB_APP_URL"] = "PRIMER_CLOUD_WEB_APP_URL";
    /**
     * Primer cloud hosted configuration file
     */
    TapPathType["PRIMER_CLOUD_CONFIG_URL"] = "PRIMER_CLOUD_CONFIG_URL";
})(TapPathType = exports.TapPathType || (exports.TapPathType = {}));
exports.defaultUriMapping = {
    '$1/': TapPathType.PRIMER_CLOUD_WEB_APP_URL,
    '$2/': TapPathType.URL,
    '$4/': TapPathType.AAR,
    '$5/': TapPathType.PRIMER_CLOUD_CONFIG_URL,
    '$6/': TapPathType.AAR
};
class TapAppPathConverter {
    constructor(mapping = exports.defaultUriMapping) {
        this.mapping = mapping;
    }
    encode(info) {
        let prefix = '';
        for (let key in this.mapping) {
            if (this.mapping[key] === info.type) {
                prefix = key;
                break;
            }
        }
        return `${prefix}${info.value}`;
    }
    decode(value) {
        if (value.length >= 3) {
            let start = value.substr(0, 3);
            let valueWithoutPrefix = value.substr(3);
            if (start in this.mapping) {
                return {
                    type: this.mapping[start],
                    value: valueWithoutPrefix
                };
            }
        }
        return {
            type: TapPathType.URL,
            value: value
        };
    }
}
exports.TapAppPathConverter = TapAppPathConverter;

});

var tapAppPathConverter$1 = unwrapExports(tapAppPathConverter);
var tapAppPathConverter_1 = tapAppPathConverter.TapPathType;
var tapAppPathConverter_2 = tapAppPathConverter.defaultUriMapping;
var tapAppPathConverter_3 = tapAppPathConverter.TapAppPathConverter;

var tapConfigConverter = createCommonjsModule(function (module, exports) {
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });






function convertToVariableType(accessor) {
    let unsigned = accessor.value('GUI_unsigned', 'boolean', false);
    let float = accessor.value('GUI_float', 'boolean', false);
    let size = accessor.value('Unit', 'number');
    if (float) {
        return deviceClient.VariableType.FLOAT32;
    }
    switch (size) {
        case 0:
            return unsigned ? deviceClient.VariableType.UINT8 : deviceClient.VariableType.INT8;
        case 1:
            return unsigned ? deviceClient.VariableType.UINT8 : deviceClient.VariableType.INT8;
        case 2:
            return unsigned ? deviceClient.VariableType.UINT16 : deviceClient.VariableType.INT16;
        case 3:
            return unsigned ? deviceClient.VariableType.UINT32 : deviceClient.VariableType.INT32;
        default:
            throw new Error(`Unknown unit size: "${size}" (type=${typeof size})`);
    }
}
exports.convertToVariableType = convertToVariableType;
function fromMapping(data, attributeName, mapping) {
    let value = data.value(attributeName).toUpperCase();
    if (!(value in mapping)) {
        throw new Error(`Invalid value "${value}" for key "${attributeName}"`);
    }
    return mapping[value];
}
const NetworkModeMapping = {
    P2P: 'PEER_TO_PEER',
    NETWORK: 'INFRASTRUCTURE_ONLY',
    INFRA: 'INFRASTRUCTURE_ONLY',
    INFRA_CLOUD: 'INFRASTRUCTURE_CLOUD'
};
const LowPowerOptimisationLevelMapping = {
    '0': 'NO',
    '1': 'SHUTDOWN',
    '2': 'STANDBY' // TODO CHECK
};
const TargetProtocolMapping = {
    GPIO: 'GPIO',
    JTAG: 'JTAG',
    MODBUS: 'MODBUS',
    NONE: 'NONE',
    S3P: 'S3P',
    SERIAL_STANDARD: 'SERIAL_VIA_TAPNPASS',
    'SERIAL STANDARD': 'SERIAL_VIA_TAPNPASS',
    SERIAL_VIA_TAPNPASS: 'SERIAL_VIA_TAPNPASS',
    SWD: 'SWD'
};
const NFCParingModeMapping = {
    No: 'NO',
    NO: 'NO',
    Pairing: 'MANDATORY',
    PAIRING: 'MANDATORY',
    'Pairing+Login': 'MANDATORY_FOR_LOGIN',
    'PAIRING+LOGIN': 'MANDATORY_FOR_LOGIN'
};
function toAuthorizedHostProtocols(value) {
    switch (value) {
        case 'ALL':
            return 'ALL';
        default:
            return value.split(',').map((value) => {
                value = value.trim();
                return value;
            });
    }
}
// /**
//  * Checks if current bundle can be seen by the current user
//  * @param {string} profile name of the profile
//  * @returns {boolean} true if bundle is visible to the user
//  */
// isVisibleTo(profile: string) {
//         let isVisible;
//         switch (profile) {
//                 case 'admin':
//                 return true;
//                 case 'supervisor':
//                 return (this.rights.length !== 1 || this.rights.find( right => right.profile === 'admin') === undefined);
//                 default :
//                 isVisible = this.rights.find( right => right.profile === profile? true : right.profile === 'anonymous');
//         }
//         return isVisible !== undefined ? true : false;
// }
// /**
//  * Checks if current bundle can be written by the current user
//  * @param profile name of the profile
//  * @returns {boolean} true if bundle is writable by the user
//  */
// isWritableBy(profile: string) {
//         switch (profile) {
//                 case 'admin':
//                 return this.rights.find(right => right.write) !== undefined;
//                 case 'supervisor':
//                 return this.rights.find(right => (right.write && right.profile !== 'admin')) !== undefined;
//                 default :
//                 const profileRights = this.rights.find( right => right.profile === profile);
//                 const anonymousRights = this.rights.find( right => right.profile === 'anonymous');
//                 return (profileRights && profileRights.write)? true : (anonymousRights && anonymousRights.write)? true : false;
//         }
// }
// const FlatMapping = {
//   'config.application.name': 'IOTizeConfiguration.IOTizeApp.name',
// }
/**
 *  Convert an IoTize studio configuration file to the
 * TapConfigSchema
 */
class TapConfigConverter {
    constructor(modbusSerialConfigConverter = new deviceClientExtension.ProtocolConfigurationDataConfigConverter(), appPathConverter = new tapAppPathConverter.TapAppPathConverter()) {
        this.modbusSerialConfigConverter = modbusSerialConfigConverter;
        this.appPathConverter = appPathConverter;
    }
    convert(accessor) {
        let config = accessor.get('IOTizeConfiguration');
        let appConfig = config.get('IOTizeApp');
        let wifiConfigSource = appConfig.has('WIFI_Info') ? appConfig.get('WIFI_Info') : undefined;
        let wifiConfig = undefined;
        if (wifiConfigSource) {
            wifiConfig = {
                mode: fromMapping(wifiConfigSource, 'Network', NetworkModeMapping)
            };
            let ssid = wifiConfigSource.value('ModuleSSID', 'string', undefined);
            if (ssid) {
                wifiConfig.ssid = ssid;
            }
            let wepKey = wifiConfigSource.value('ModuleWEPKey', 'string', undefined);
            if (wepKey) {
                wifiConfig.wepKey = wepKey;
            }
        }
        let cloudService = appConfig.get('CloudServices');
        let bundles = config.list('IOTizeBundle', []);
        let profiles = config.list('IOTizeProfile', []);
        let lockSettingsEncoded;
        lockSettingsEncoded = appConfig.value('InterfaceLock', 'number', 0);
        // let serialNumber = appConfig.value('DevIoTizeSN');
        let lockOptions = new deviceClient.InterfaceLockConverter().decode(Uint8Array.from([0, lockSettingsEncoded]));
        let appPath = appConfig.has('GUI_Path')
            ? appConfig.value('GUI_Path', 'string', '')
            : appConfig.value('Path', 'string', '');
        let appPathInfo = this.appPathConverter.decode(appPath);
        let cloudOptions = undefined;
        if (cloudService.has('CloudMQTT_UID')) {
            cloudOptions = {
                password: cloudService
                    .get('CloudMQTT_UID')
                    .value('Name', 'string', ''),
                uploadPeriod: cloudService.value('CloudMqttPeriod', 'number', 0),
                url: `mqtt://${cloudService
                    .get('CloudHostName')
                    .value('Name', 'string', '')}:${cloudService
                    .get('CloudServiceName')
                    .value('Name', 'string', '')}`
            };
        }
        let versionString = config.value('Version', 'string');
        return {
            meta: {
                // TODO check
                version: '1.0.0'
            },
            config: {
                version: versionString,
                application: {
                    name: appConfig.value('Name', 'string', ''),
                    universalLink: appConfig.value('WebLink', 'string', ''),
                    uri: appPathInfo.type != tapAppPathConverter.TapPathType.AAR
                        ? appPath
                        : undefined,
                    android: appPathInfo.type == tapAppPathConverter.TapPathType.AAR
                        ? {
                            aar: appPathInfo.value
                        }
                        : undefined
                },
                data: {
                    bundles: this.converteBundles(bundles),
                    profiles: this.converteProfiles(profiles)
                },
                tap: {
                    cloud: cloudOptions,
                    wifi: wifiConfig,
                    mqttRelay: {
                        url: `mqtt://${appConfig
                            .get('GUI_DevMQTTHostName')
                            .value('Name', 'string', '')}:${appConfig
                            .get('GUI_DevMQTTServiceName')
                            .value('Name', 'string', '')}`,
                        key: `${appConfig
                            .get('GUI_DevMQTT_UID')
                            .value('Name', 'string', '')}`,
                        netKey: `${appConfig
                            .get('GUI_DevNetkey')
                            .value('Name', 'string', '')}`
                    },
                    security: lockOptions,
                    powerOptimization: fromMapping(appConfig, 'LowPowerLevel', LowPowerOptimisationLevelMapping),
                    // authorizedHostProtocols: toAuthorizedHostProtocols(appConfig.value('HostProtocol')),
                    availableHostProtocols: toAuthorizedHostProtocols(appConfig.value('HostProtocol')),
                    hostInactivityPeriod: appConfig.value('LowPowerHostTimeout', 'number', 0),
                    hashIterationCount: appConfig.value('Scram_Hash_It', 'number', 5000),
                    nfcPairingMode: fromMapping(appConfig, 'NFCPairingMandatory', NFCParingModeMapping)
                },
                target: this._convertTargetConfig(appConfig)
            }
        };
    }
    _convertTargetConfig(appConfig) {
        let data = {
            firmwareVersion: appConfig.value('Firmware', 'string', ''),
            protocol: fromMapping(appConfig, 'TargetProtocol', TargetProtocolMapping)
        };
        let TargetProtocolConfDataLen = appConfig.value('TargetProtocolConfDataLen', 'number', 0);
        if (TargetProtocolConfDataLen > 0) {
            let protocolData = util.strToHex(appConfig.value('TargetProtocolConfData', 'string'));
            if (protocolData.length < 8) {
                throw new definitions.InvalidTapConfig('Protocol configuration data should be at least 8 bytes long');
            }
            let ctr1 = protocolData.slice(0, 4);
            let ctr2 = protocolData.slice(4, 8);
            let remaining = protocolData.slice(8);
            //  KaitaiStreamWriter.mergeArrays(ctr2, ctr1).reverse(),
            protocolData = byteStream.KaitaiStreamWriter.mergeArrays(ctr1.reverse(), ctr2.reverse(), remaining);
            let protocolConf = deviceClient.ProtocolConfigurationConverterInstance.decode(protocolData);
            let modbusSerialConfig = this.modbusSerialConfigConverter.decode(protocolConf);
            data = Object.assign(Object.assign({}, data), modbusSerialConfig);
        }
        if (appConfig.value('GUI_TargetType', 'string', '0') == '1') {
            data.type = 'TAPNPASS';
        }
        else {
            data.type = 'MCU';
        }
        // if (appConfig.has('USB_Aliases')) {
        //   data.type = "TAPNPASS";
        //   (data as TapNPassTargetConfig).usb = this._convertUsbData(appConfig);
        // }
        return data;
    }
    // _convertUsbData(appConfig: IAccessor): UsbConfig {
    //   let aliases: UsbAlias[] = [];
    //   let usbAliasesAcccessor = appConfig.get('USB_Aliases');
    //   for (let aliasNum = 0; ; aliasNum++) {
    //     let aliasName = `Alias${aliasNum}`;
    //     if (!usbAliasesAcccessor.has(aliasName)) {
    //       break;
    //     }
    //     let usbAliasRawData = strToHex(usbAliasesAcccessor.value(aliasName));
    //     aliases.push(this._convertUsbAlias(usbAliasRawData));
    //   }
    //   return {
    //     aliases: aliases
    //   };
    // }
    // private _convertUsbAlias(data: Uint8Array): UsbAlias {
    //   return this.usbAliasConverter.decode(data);
    // }
    converteProfiles(profiles) {
        let results = [];
        for (let i = 0; i < profiles.length; i++) {
            let xmlProfile = profiles[i];
            let name = xmlProfile.value('Name');
            let profile;
            if (name == 'anonymous') {
                continue;
            }
            else if (util.isSpecialUser(name)) {
                let specialConfig = {
                    username: xmlProfile.value('Name', 'string'),
                    lifeTime: xmlProfile.value('LifeTime', 'number', 0)
                    // password: xmlProfile.value('Pwd') // TODO
                };
                this.fillWithKey(specialConfig, xmlProfile);
                profile = specialConfig;
            }
            else {
                let userConfig = {
                    username: xmlProfile.value('Name', 'string', ''),
                    id: xmlProfile.value('Id', 'number', 0),
                    // salt: 1,
                    lifeTime: xmlProfile.value('LifeTime', 'number', 0)
                    // password: xmlProfile.value('Pwd') // TODO
                };
                this.fillWithKey(userConfig, xmlProfile);
                profile = userConfig;
            }
            let subProfilesConfig = xmlProfile.list('IOTizeUser', []);
            if (subProfilesConfig.length > 0) {
                let subProfiles = this._converteUsers(subProfilesConfig);
                profile.users = subProfiles;
            }
            results.push(profile);
        }
        return results;
    }
    fillWithKey(specialConfig, xmlProfile) {
        let scramPwd = xmlProfile.value('Scram_Pwd', 'string', undefined);
        if (scramPwd) {
            // let userSalt = xmlProfile.value('Scram_User_Salt', 'number');
            // specialConfig.salt = userSalt;
            specialConfig.key = `0x${scramPwd}`;
        }
        else {
            let key = xmlProfile.value('Pwd', 'string', undefined);
            if (key) {
                specialConfig.key = `0x${key}`;
            }
        }
    }
    _converteUsers(subProfilesConfig) {
        let users = [];
        for (let i = 0; i < subProfilesConfig.length; i++) {
            let xmlUserConfig = subProfilesConfig[i];
            let user = {
                username: xmlUserConfig.value('Name', 'string', ''),
                id: xmlUserConfig.value('Id', 'number', 0),
                lifeTime: 0 // There is no lifetime for users (only profile)
            };
            this.fillWithKey(user, xmlUserConfig);
            users.push(user);
        }
        return users;
    }
    converteBundles(xmlBundles) {
        let result = [];
        for (let i = 0; i < xmlBundles.length; i++) {
            let xmlBundle = xmlBundles[i];
            let aclsXml = [];
            if (xmlBundle.has('IOTizeAccessControl')) {
                let aclContainer = xmlBundle.get('IOTizeAccessControl');
                aclsXml = aclContainer.list('IOTizeACL', []);
            }
            let acls = {};
            for (let j = 0; j < aclsXml.length; j++) {
                let aclXml = aclsXml[j];
                let profileName = aclXml.value('ProfileName');
                if (profileName === 'admin') {
                    continue;
                }
                let rights = aclXml.value('Rights');
                acls[profileName] = {
                    read: rights.includes('read'),
                    write: rights.includes('write')
                };
            }
            let bundleConfig = {
                name: xmlBundle.value('GUI_Name'),
                id: parseInt(xmlBundle.value('Id')),
                variables: this.converterVariables(xmlBundle.list('IOTizeVariable', [])),
                acls: acls,
                dataLog: {
                    period: xmlBundle.value('DataLogPeriod', 'number', 0)
                },
                features: this._convertFeatures(xmlBundle.list('Feature', []))
            };
            result.push(bundleConfig);
        }
        return result;
    }
    _convertFeatures(featuresXml) {
        if (featuresXml.length > 0) {
            let result = [];
            for (let i = 0; i < featuresXml.length; i++) {
                let featureId = featuresXml[i].value('FeatureResId', 'number');
                if (featureId in deviceClient.SpecialFeature) {
                    let value = deviceClient.SpecialFeature[featureId]; // TODO check
                    result.push(value);
                }
                else {
                    throw new Error(`Unexpected feature id: ${featureId}`);
                }
            }
            return result;
        }
        else {
            return undefined;
        }
    }
    converterVariables(variables) {
        let result = [];
        for (let i = 0; i < variables.length; i++) {
            let xmlVariable = variables[i];
            let variableConfig = {
                address: '0x' + xmlVariable.value('Address', 'number').toString(16),
                id: xmlVariable.value('Id', 'number'),
                length: xmlVariable.value('Quantity', 'number', 1),
                type: util.enumToString(convertToVariableType(xmlVariable), deviceClient.VariableType),
                name: xmlVariable.value('GUI_Name', 'string', '')
            };
            let unit = xmlVariable.value('GUI_DispUnit', 'string', '');
            if (unit) {
                variableConfig.unit = unit;
            }
            result.push(variableConfig);
        }
        return result;
    }
}
exports.TapConfigConverter = TapConfigConverter;

});

var tapConfigConverter$1 = unwrapExports(tapConfigConverter);
var tapConfigConverter_1 = tapConfigConverter.convertToVariableType;
var tapConfigConverter_2 = tapConfigConverter.TapConfigConverter;

var schemaTapConfigurator = createCommonjsModule(function (module, exports) {
"use strict";
var __awaiter = (commonjsGlobal && commonjsGlobal.__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());
    });
};
Object.defineProperty(exports, "__esModule", { value: true });

















function bytesToUtf8(input) {
    return buffer.Buffer.from(input).toString('utf-8');
}
exports.bytesToUtf8 = bytesToUtf8;
class InvalidTapSchemaConfig extends definitions.InvalidTapConfig {
    constructor(validate) {
        super(`Invalid tap configuration file. ${InvalidTapSchemaConfig.errorsToString(validate.errors)}`);
        this.validate = validate;
        this.errors = this.validate.errors || [];
    }
    static errorsToString(errors) {
        return errors
            ? errors
                .map(error => `${error.dataPath}.${error.keyword || ''} ${error.message}`)
                .join(', ')
            : 'Unknown errors';
    }
}
exports.InvalidTapSchemaConfig = InvalidTapSchemaConfig;
function getByAttr(filterKey, v, data) {
    let array;
    if (Array.isArray(data)) {
        array = data;
    }
    else {
        array = Object.keys(data).map(key => data[key]);
    }
    return array.filter((value) => value[filterKey] == v)[0] || undefined;
}
function enumValues(MyEnum) {
    return Object.keys(MyEnum)
        .filter(v => !isNaN(parseInt(v)))
        .map(v => parseInt(v));
}
function semanticVersionStringToInt(version) {
    if (typeof version === 'number') {
        return version;
    }
    let parts = version.split('.').map(parseInt);
    return (parts[0] << 24) + (parts[1] << 16) + parts[2];
}
exports.semanticVersionStringToInt = semanticVersionStringToInt;
function getProfileId(profile) {
    let specialUserInfo = util.SPECIAL_USERS.find(v => profile.username == v.username);
    if (specialUserInfo) {
        return specialUserInfo.id;
    }
    let profileId = profile['id'];
    if (profileId === undefined) {
        throw definitions.TapConfigurationError.invalidConfiguration(`Profile id is required for ${profile['username']}`);
    }
    return profileId;
}
const DEFAULT_HASH_ITERATION_COUNT = 5000;
function toEnumString(value, enumMapping) {
    // if (!value) {
    //     throw new Error(`Missing first attribute for toEnum. ${Object.keys(enumMapping).join(', ')}`)
    // }
    if (!(value in enumMapping)) {
        throw new Error(`Missing enum value "${value}". Available: ${Object.keys(enumMapping).join(', ')}`);
    }
    return enumMapping[value].toUpperCase();
}
function encodeModbusAddress(config) {
    let stream = impl.TapStreamWriter.create(4);
    stream.writeU(parseAddress(config.functionCode), 1);
    stream.writeU(parseAddress(config.slave), 1);
    stream.writeU(parseAddress(config.address), 2);
    return impl.NumberConverter.fromOpaqueMsb(stream.toBytes, 4 * 8) >>> 0;
}
function isSpecialFeatureSet(specialFeatureId, value) {
    return 0xfffe - (specialFeatureId - 100) != value;
}
function parseAddress(input) {
    if (typeof input == 'number')
        return input;
    return parseInt(input);
}
function _bodyOrDefault(response, defaultValue) {
    try {
        let body = response.body();
        if (!body) {
            return defaultValue;
        }
        return body;
    }
    catch (err) {
        console.warn(err);
        return defaultValue;
    }
}
const utf8Converter = new impl.StringConverter('utf8');
function parseTapConfig(fileContent) {
    if (typeof fileContent !== 'string') {
        fileContent = fileContent.toString();
    }
    if (isJsonString(fileContent)) {
        let config = JSON.parse(fileContent);
        // this.tapConfigPickerservice.onConfigSelected(config);
        return config;
    }
    else {
        // if (IotizeConfigFactory.fromXML(fileContent)) {
        return fromXml(fileContent);
    }
}
exports.parseTapConfig = parseTapConfig;
function isJsonString(str) {
    try {
        var json = JSON.parse(str);
        return typeof json === 'object';
    }
    catch (e) {
        return false;
    }
}
exports.isJsonString = isJsonString;
function fromXml(fileContent) {
    // console.log('File content', fileContent);
    let xmlConfig = fastXmlParser.parse(fileContent, {
        attributeNamePrefix: '',
        ignoreAttributes: false
    });
    let config = new tapConfigConverter.TapConfigConverter().convert(new dataAccessor.DotAccessor(xmlConfig));
    return config;
}
exports.fromXml = fromXml;
function getJsonSchemaValidator(schemas) {
    schemas = schemas || [
        v1.TapConfigJsonSchema,
        v1.CommonTypeJsonSchema,
        v1.TapTypeJsonSchema
    ];
    let ajv$1 = new ajv({
        schemas: schemas
    });
    let validate = ajv$1.getSchema('http://iotize.com/schemas/tap-config.json');
    return validate;
}
/**
 * Validate schema. Throw error if schema is not valid
 * @throws @{link InvalidTapSchemaConfig}
 * @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 validateTapConfig(config, schemas) {
    let validator = getJsonSchemaValidator(schemas);
    var valid = validator(config);
    if (!valid) {
        throw new InvalidTapSchemaConfig(validator);
    }
    return validator;
}
exports.validateTapConfig = validateTapConfig;
function checkErrors(config) {
    let validator = getJsonSchemaValidator();
    validator(config);
    return validator.errors;
}
exports.checkErrors = checkErrors;
function tapVersionToString(v) {
    return `${v.major}.${v.minor}.${v.patch}`;
}
/**
 * Configure tap from a configuration file schema
 *
 * JSON schema definition is here: TODO
 *
 * TODO:
 *  - Improve apply config with more comprehensive errors (add configuration that failed)
 *
 */
class SchemaTapConfigurator {
    constructor(
    /**
     * Schema config to write
     */
    _schema, options = {
        delayMs: 400
    }, bundleConfigurator = new MemoryBundleConfigurator(), protocolConfDataConverter = new deviceClientExtension.ProtocolConfigurationDataConfigConverter(), appPathConverter = new tapAppPathConverter.TapAppPathConverter()) {
        this._schema = _schema;
        this.options = options;
        this.bundleConfigurator = bundleConfigurator;
        this.protocolConfDataConverter = protocolConfDataConverter;
        this.appPathConverter = appPathConverter;
        this._tmpConfig = {};
        this.scramSaltGenerator = deviceClient.DEFAULT_SCRAM_SALT_GENERATOR;
    }
    get schema() {
        if (!this._schema) {
            this._schema = {
                meta: {
                    version: '1.0.0'
                },
                config: {
                    version: 0,
                    application: {
                        name: 'My new application'
                    }
                }
            };
        }
        return this._schema;
    }
    configure(tap) {
        this.tap = tap;
        this._writeSubject = new rxjs.Subject();
        if (this.bundleConfigurator.writeEvents) {
            this.bundleConfigurator.writeEvents.subscribe(this._writeSubject);
        }
        try {
            validateTapConfig(this.schema);
            this._next({
                message: 'Starting configuration...',
                type: 'START'
            });
            this._configure();
        }
        catch (err) {
            this._writeSubject.error(err);
        }
        return this._writeSubject;
    }
    _configure() {
        return __awaiter(this, void 0, void 0, function* () {
            try {
                // TODO why we do that here ? 
                if (this._useScram()) {
                    yield this.tap.enableEncryption(true, true);
                }
                (yield this.tap.service.device.factoryReset()).successful();
                yield this.setupConfigState();
                yield this._writeConfig();
                yield this.applyConfig(this._schema.config.version);
                yield this.tap.logout();
                this._writeSubject.complete();
            }
            catch (err) {
                this._writeSubject.error(err);
            }
        });
    }
    read(tap) {
        this.tap = tap;
        this._readSubject = new rxjs.Subject();
        this._next({
            message: 'Starting reading...',
            type: 'START'
        });
        this._read();
        return this._readSubject;
    }
    _read() {
        return __awaiter(this, void 0, void 0, function* () {
            try {
                let config = yield this.readSchema();
                this._schema = config;
                this._readSubject.next({
                    type: 'END',
                    payload: config,
                    message: 'Configuration loaded!'
                });
                this._readSubject.complete();
            }
            catch (err) {
                this._readSubject.error(err);
            }
        });
    }
    setupConfigState() {
        return __awaiter(this, void 0, void 0, function* () {
            this._next({
                message: 'Setup firmware state...',
                type: 'SETUP_FIRMWARE_STATE'
            });
            let state = (yield this.tap.service.firmware.getState()).body();
            if (state != deviceClient.FirmwareState.NORMAL) {
                throw definitions.TapConfigurationError.unexpectedFirmwareState(state);
            }
            // (await this.tap.login("admin", "admin"));
            (yield this.tap.service.interface.putConfigVersion(-1)).successful();
        });
    }
    isConfigured() {
        return __awaiter(this, void 0, void 0, function* () {
            let configVersionCalled = yield this.tap.service.interface.getConfigVersion();
            configVersionCalled.successful();
            let intValue = configVersionCalled.body(impl.NumberConverter.uint32());
            return intValue !== 0;
        });
    }
    applyConfig(newVersion = "1.0.0") {
        return __awaiter(this, void 0, void 0, function* () {
            this._next({
                message: 'Applying configuration...',
                type: 'APPLY_CONFIG'
            });
            yield this.sleep(this.options.delayMs);
            (yield this.tap.service.interface.putConfigVersion(semanticVersionStringToInt(newVersion))).successful();
            yield this.sleep(this.options.delayMs);
            (yield this.tap.service.interface.reloadConfig()).successful();
            yield this.sleep(this.options.delayMs);
        });
    }
    sleep(delayMs) {
        return common.sleep(delayMs);
    }
    _useScram() {
        return (Boolean(this._schema.config) &&
            Boolean(this._schema.config.tap) &&
            Boolean(this._schema.config.tap.security) &&
            Boolean(this._schema.config.tap.security.scramActivated));
    }
    _writeConfig() {
        return __awaiter(this, void 0, void 0, function* () {
            if (this._useScram()) {
                // To prevent scram on this one... TODO find a better way
                (yield this.tap.lwm2m.put('/1024/65535/6', Uint8Array.from([0x10]))).successful();
                // (await this.tap.service.interface.putLock({
                //     factoryReset: false,
                //     hashPassword: false,
                //     resourceFactory: false,
                //     resourceLogUid: false,
                //     scramActivated: true
                // })).successful();
            }
            yield this._writeApplication(this._schema.config.application);
            if (this._schema.config.tap) {
                yield this._writeTap(this._schema.config.tap);
            }
            if (this._schema.config.target) {
                yield this._writeTarget(this._schema.config.target);
            }
            if (this._schema.config.data) {
                yield this._writeData(this._schema.config.data);
            }
            if (this._schema.config.tap && this._schema.config.tap.security) {
                (yield this.tap.service.interface.putLock(this._schema.config.tap.security)).successful();
            }
        });
    }
    _writeData(data) {
        return __awaiter(this, void 0, void 0, function* () {
            let profiles = this.profiles;
            for (let specialUser of util.SPECIAL_USERS) {
                if (!profiles.find(profile => profile.username == specialUser.username)) {
                    profiles.push(specialUser);
                }
            }
            if (this._schema.config.data) {
                if (profiles) {
                    for (let profile of profiles) {
                        yield this._writeProfile(profile);
                    }
                }
                if (this._schema.config.data.bundles) {
                    for (let bundle of this._schema.config.data.bundles) {
                        yield this._writeBundle(bundle);
                    }
                }
            }
        });
    }
    _readData() {
        return __awaiter(this, void 0, void 0, function* () {
            if (!this._tmpConfig.data) {
                this._tmpConfig.data = {};
            }
            this._tmpConfig.data.profiles = yield this._readProfiles();
            this._tmpConfig.data.bundles = yield this._readBundles();
            yield this._readSpecialFeatures();
            return this._tmpConfig.data;
        });
    }
    _readProfiles() {
        return __awaiter(this, void 0, void 0, function* () {
            let profiles = [];
            let profileIds = yield this._listProfiles();
            for (let profileId of profileIds) {
                let alias = (yield this.tap.service.group.getAlias(profileId)).body();
                let profile = yield this._readUserProfile(profileId);
                if (alias != profileId) {
                    let parentProfile = profiles.find(v => v.id == alias);
                    if (!parentProfile) {
                        throw new Error(`Parent profile with id ${alias} not found`);
                    }
                    logger.debug(`Group ${profileId} has alias ${alias}`);
                    if (!parentProfile.users) {
                        parentProfile.users = [];
                    }
                    parentProfile.users.push(profile);
                }
                else {
                    profiles.push(profile);
                }
            }
            return profiles;
        });
    }
    _getGroupName(profileId) {
        return __awaiter(this, void 0, void 0, function* () {
            let specialUsers = util.SPECIAL_USERS.filter(v => v.id == profileId);
            if (specialUsers.length == 0) {
                return (yield this.tap.service.group.getName(profileId)).body();
            }
            else {
                return specialUsers[0].username;
            }
        });
    }
    readSchema() {
        return __awaiter(this, void 0, void 0, function* () {
            this._tmpConfig = {
                application: {},
                tap: {},
                target: {},
                data: {}
            };
            let versionObject = (yield this.tap.service.interface.getConfigVersion()).body();
            this._tmpConfig.version = `${versionObject.major}.${versionObject.minor}.${versionObject.patch}`;
            this._tmpConfig.application = yield this._readApplication();
            this._tmpConfig.tap = yield this._readTap();
            this._tmpConfig.target = yield this._readTarget();
            this._tmpConfig.data = yield this._readData();
            let schema = {
                meta: {
                    version: '1.0.0'
                },
                config: this._tmpConfig
            };
            return schema;
        });
    }
    _readTarget() {
        return __awaiter(this, void 0, void 0, function* () {
            let data = {
                firmwareVersion: tapVersionToString((yield this.tap.service.target.getFirmwareVersion()).body()),
                protocol: toEnumString((yield this.tap.service.target.getProtocol()).body(), deviceClient.TargetProtocol),
                type: 'MCU'
            };
            let protocolConf = (yield this.tap.service.target.getProtocolConfiguration()).body();
            let protocolConfConfig = this.protocolConfDataConverter.decode(protocolConf);
            data.modbus = protocolConfConfig.modbus;
            data.serial = protocolConfConfig.serial;
            data.LEDs = protocolConfConfig.LEDs;
            data.usb = protocolConfConfig.usb;
            return data;
        });
    }
    _writeTarget(target) {
        return __awaiter(this, void 0, void 0, function* () {
            this._next({
                type: 'WRITE_TARGET_CONFIG',
                message: `Writing target config`
            });
            let type = target.type;
            logger.debug('target.type', type);
            switch (type) {
                case 'MCU':
                    break;
                case 'TAPNPASS':
                    if (target.serial) {
                        let protocolConf = this.protocolConfDataConverter.encode(target);
                        yield this._successfullCall(this.tap.service.target.putProtocolConfiguration(protocolConf));
                        // await this._successfullCall(this.tap.lwm2m.put(
                        //     '/1027//21',
                        //     protocolConfData
                        // ));
                    }
                    break;
                default:
                    throw new definitions.InvalidTapConfig(`Invalid target type: ${type}`);
            }
            if (target.protocol) {
                yield this._successfullCall(this.tap.service.target.putProtocol(util.toEnum(target.protocol, deviceClient.TargetProtocol)));
            }
            if (target.firmwareVersion) {
                let versionParts = target.firmwareVersion.split('.');
                (yield this.tap.service.target.putFirmwareVersion({
                    major: parseInt(versionParts[0]),
                    minor: parseInt(versionParts[1]),
                    patch: parseInt(versionParts[2])
                })).successful();
            }
        });
    }
    _successfullCall(call) {
        return __awaiter(this, void 0, void 0, function* () {
            return (yield call).successful();
        });
    }
    _apiCall(call) {
        return __awaiter(this, void 0, void 0, function* () {
            let response = yield call;
            // if (!response.isSuccessful()){
            //     console.warn(new )
            // }
            return response;
        });
    }
    _readTap() {
        return __awaiter(this, void 0, void 0, function* () {
            let tapOptions = {};
            // this._next({
            //     "type": "READ_TAP_CONFIG",
            //     "message": `Writing tap config`
            // });
            // TODO enable when it's fixed
            if (!tapOptions.wifi) {
                tapOptions.wifi = {
                    mode: toEnumString(_bodyOrDefault(yield this.tap.service.interface.getNetworkMode(), deviceClient.NetworkMode.PEER_TO_PEER), deviceClient.NetworkMode),
                    ssid: (yield this.tap.service.interface.getWifiSSID()).body(),
                    wepKey: (yield this.tap.service.interface.getWepKey()).body()
                };
            }
            let response = yield this.tap.service.interface.getLock();
            // console.info(response);
            tapOptions.security = response.body();
            tapOptions.availableHostProtocols = (yield this.tap.service.interface.getAuthorizedHostProtocol())
                .body()
                .map(hostProtocol => toEnumString(hostProtocol, deviceClient.HostProtocol));
            tapOptions.powerOptimization = toEnumString((yield this.tap.service.interface.getPowerOptimisationLevel()).body(), deviceClient.LowPowerOptimisationLevel);
            tapOptions.hostInactivityPeriod = (yield this.tap.service.interface.getHostInactivityPeriod()).body();
            tapOptions.hashIterationCount = (yield this.tap.service.scram.getHashIteration()).body();
            tapOptions.nfcPairingMode = toEnumString((yield this.tap.service.interface.getNfcPairingMode()).body(), deviceClient.NfcPairingMode);
            tapOptions.mqttRelay = yield this._readMqttRelay();
            tapOptions.cloud = yield this._readCloudOptions();
            return tapOptions;
        });
    }
    _writeTap(tapOptions) {
        return __awaiter(this, void 0, void 0, function* () {
            this._next({
                type: 'WRITE_TAP_CONFIG',
                message: `Writing tap config`
            });
            if (tapOptions.hashIterationCount) {
                yield this._writeCall(this.tap.service.scram.putHashIteration(tapOptions.hashIterationCount));
            }
            if (tapOptions.wifi) {
                if (tapOptions.wifi.mode) {
                    (yield this.tap.service.interface.putNetworkMode(util.toEnum(tapOptions.wifi.mode, deviceClient.NetworkMode))).successful();
                }
                if (tapOptions.wifi.ssid) {
                    (yield this.tap.service.interface.putWifiSSID(tapOptions.wifi.ssid)).successful();
                }
                if (tapOptions.wifi.wepKey) {
                    (yield this.tap.service.interface.putWepKey(tapOptions.wifi.wepKey)).successful();
                }
            }
            if (tapOptions.availableHostProtocols) {
                if (tapOptions.availableHostProtocols === 'ALL') {
                    let callOptions = this.tap.service.interface.resources
                        .putAuthorizedHostProtocol;
                    callOptions.body = Uint8Array.from([0, 0]);
                    callOptions.bodyEncoder = undefined;
                    yield this.tap.service.interface._call(callOptions);
                }
                else {
                    let protocols = tapOptions.availableHostProtocols.map(v => util.toEnum(v, deviceClient.HostProtocol));
                    (yield this.tap.service.interface.putAuthorizedHostProtocol(protocols)).successful();
                }
            }
            if (tapOptions.powerOptimization !== undefined) {
                let powerLevel = util.toEnum(tapOptions.powerOptimization, deviceClient.LowPowerOptimisationLevel);
                (yield this.tap.service.interface.putPowerOptimisationLevel(powerLevel)).successful();
            }
            if (tapOptions.hostInactivityPeriod !== undefined) {
                (yield this.tap.service.interface.putHostInactivityPeriod(tapOptions.hostInactivityPeriod)).successful();
            }
            if (tapOptions.nfcPairingMode !== undefined) {
                (yield this.tap.service.interface.putNfcPairingMode(util.toEnum(tapOptions.nfcPairingMode, deviceClient.NfcPairingMode))).successful();
            }
            // TODO
            // : Put  /1024/65535/30 [00000000] : 0xFF
            // : Put  /1024/65535/31 [00000000] : 0xFF
            // : Put  /1024/65535/32 [00000000] : 0xFF
            if (tapOptions.mqttRelay) {
                yield this._writeMqttRelay(tapOptions.mqttRelay);
            }
            if (tapOptions.cloud) {
                yield this._writeCloudOptions(tapOptions.cloud);
            }
        });
    }
    _readMqttRelay() {
        return __awaiter(this, void 0, void 0, function* () {
            let url = (yield this.tap.service.interface.getMqttRelayUrl()).body();
            let serviceName = (yield this.tap.service.interface.getMqttRelayPort()).body();
            if (serviceName && serviceName != '0') {
                url = `mqtt://${url}:${serviceName}`;
            }
            let options = {
                url: url,
                netKey: (yield this.tap.service.interface.getMqttRelayNetKey()).body(),
                key: (yield this.tap.service.interface.getMqttRelayPassword()).body()
            };
            return options;
        });
    }
    _writeMqttRelay(options) {
        return __awaiter(this, void 0, void 0, function* () {
            this._next({
                type: 'WRITE_RELAY_CONFIG',
                message: `\tWriting relay config`
            });
            yield this._writeCall(this.tap.service.interface.putMqttRelayUrl(options.url));
            if (options.netKey) {
                yield this._writeCall(this.tap.service.interface.putMqttRelayNetKey(options.netKey));
            }
            yield this._writeCall(this.tap.service.interface.putMqttRelayPort('0'));
            yield this._writeCall(this.tap.service.interface.putMqttRelayPassword(options.key));
        });
    }
    _writeCall(call, description) {
        return __awaiter(this, void 0, void 0, function* () {
            (yield call).successful();
        });
    }
    _readCloudOptions() {
        return __awaiter(this, void 0, void 0, function* () {
            let url = (yield this.tap.service.interface.getCloudGatewayUrl()).body();
            let serviceName = (yield this.tap.service.interface.getCloudServiceName()).body();
            if (serviceName && serviceName != '0') {
                url = `mqtt://${url}:${serviceName}`;
            }
            let options = {
                url: url,
                password: (yield this.tap.service.interface.getCloudPassword()).body(),
                uploadPeriod: (yield this.tap.service.interface.getUploadPeriod()).body()
            };
            return options;
        });
    }
    _writeCloudOptions(options) {
        return __awaiter(this, void 0, void 0, function* () {
            this._next({
                type: 'WRITE_CLOUD_CONFIG',
                message: `\tWriting cloud config`
            });
            if (options.password) {
                yield this._writeCall(this.tap.service.interface.putCloudPassword(options.password));
            }
            yield this._writeCallOptional(this.tap.service.interface.putCloudServiceName('0'));
            yield this._writeCall(this.tap.service.interface.putCloudGatewayUrl(options.url));
            yield this._writeCall(this.tap.service.interface.putUploadPeriod(options.uploadPeriod));
        });
    }
    _writeCallOptional(call) {
        return call;
    }
    _readApplication() {
        return __awaiter(this, void 0, void 0, function* () {
            let data = {
                name: (yield this.tap.service.interface.getAppName()).body(),
                uri: (yield this.tap.service.interface.getAppPath()).body()
            };
            if (data.uri) {
                let pathInfo = this.appPathConverter.decode(data.uri);
                if (pathInfo.type === tapAppPathConverter.TapPathType.AAR) {
                    data.android = {
                        aar: pathInfo.value
                    };
                    data.uri = undefined;
                }
            }
            return data;
        });
    }
    _writeApplication(application) {
        return __awaiter(this, void 0, void 0, function* () {
            this._next({
                type: 'WRITE_APPLICATION',
                message: `Writing application data`
            });
            if (application.name !== undefined) {
                yield this._writeCall(this.tap.service.interface.putAppName(application.name));
            }
            if (application.uri) {
                yield this._writeCall(this.tap.service.interface.putAppPath(application.uri));
            }
            if (application.android !== undefined) {
                if (application.android.aar !== undefined) {
                    if (application.uri) {
                        throw new Error(`Cannot specified android.aar and uri in same config. It will be overwritten`);
                    }
                    let encodedAppPath = this.appPathConverter.encode({
                        type: tapAppPathConverter.TapPathType.AAR,
                        value: application.android.aar
                    });
                    yield this._writeCall(this.tap.service.interface.putAppPath(encodedAppPath));
                }
            }
            if (application.universalLink !== undefined) {
                yield this._writeCall(this.tap.service.interface.putUniversalLink(application.universalLink));
            }
        });
    }
    _writeUser(user, parentGroup) {
        return __awaiter(this, void 0, void 0, function* () {
            this._next({
                type: 'WRITE_USER',
                message: `\t Writing user ${user.username}`,
                payload: user
            });
            // user.username = user.username || username;
            let userId = getProfileId(user);
            if (userId === 0) {
                return;
            }
            if (!util.isSpecialUser(user.username)) {
                (yield this.tap.service.group.create(userId)).successful();
                if (user.username) {
                    (yield this.tap.service.group.putName(userId, user.username)).successful();
                }
            }
            if (parentGroup) {
                let parentGroupId = getProfileId(parentGroup);
                (yield this.tap.service.group.putAlias(userId, parentGroupId)).successful();
            }
            if (user.key && user.password) {
                throw new Error(`You can either specify user.key or user.password but not both`);
            }
            if (user.key) {
                yield this._writeUserKey(userId, user.key);
                // if (user.salt){
                //     (await this.tap.service.group.putScramUserSalt(userId, user.salt)).successful();
                // }
                // (await this.tap.service.group.putScramUserIteration(userId, )).successful()
            }
            else {
                if (!user.password) {
                    user.password = user.username; // TODO check ?
                }
                // TODO check if hash password
                let password = user.password;
                yield this._writeUserPassword(userId, password, user.salt);
            }
            if (user.lifeTime) {
                (yield this.tap.service.group.putSessionLifetime(userId, user.lifeTime)).successful();
            }
        });
    }
    /**
     * TODO inprove with user key check...
     * @param userId
     * @param hexString
     */
    _writeUserKey(userId, hexString) {
        return __awaiter(this, void 0, void 0, function* () {
            if (hexString.startsWith('0x')) {
                hexString = hexString.substring(2);
            }
            let hexData = byteConverter.hexStringToBuffer(hexString);
            // if (hexData.length != 16) {
            //     throw new Error(`Profile key must be 32 bytes`);
            // }
            return (yield this.tap.service.group.changePasswordKey(userId, hexData)).successful();
        });
    }
    _writeUserPassword(userId, password, salt) {
        return __awaiter(this, void 0, void 0, function* () {
            if (!this._schema.config.tap || !this._schema.config.tap.security) {
                throw new Error(`Security options are required`); // TODO custom error
            }
            if (this._schema.config.tap.security.scramActivated) {
                if (!this._schema.config.tap.hashIterationCount) {
                    // this._schema.config.tap.hashIterationCount = DEFAULT_HASH_ITERATION_COUNT;
                    throw new Error(`config.tap.security.scramActivated not set but required when activating SCRAM`);
                }
                let saltArray;
                if (!salt) {
                    saltArray = this.scramSaltGenerator();
                }
                else {
                    saltArray = impl.NumberConverter.uint32(false).encode(salt);
                }
                let passwordBytes = deviceClient.ScramAuth.createScramPasswordKey(password, {
                    iterationNumber: this._schema.config.tap.hashIterationCount,
                    salt: saltArray
                });
                logger.debug(`Writing SCRAM password for user id ${userId} with hash: ${byteConverter.bufferToHexString(passwordBytes)}`);
                (yield this.tap.service.group.changePasswordKey(userId, passwordBytes)).successful();
            }
            else if (this._schema.config.tap.security.hashPassword) {
                let passwordBytes = byteConverter.hexStringToBuffer(deviceClient.CryptoHelper.passwordHasher.hash(password)).slice(0, 16);
                logger.debug(`Writing password for user id ${userId} with hash: ${byteConverter.bufferToHexString(passwordBytes)}`);
                (yield this.tap.service.group.changePasswordKey(userId, passwordBytes)).successful();
            }
            else {
                logger.debug(`Writing password for user id ${userId} without hash: ${password}`);
                (yield this.tap.service.group.changePassword(userId, password)).successful();
            }
        });
    }
    _writeProfile(group) {
        return __awaiter(this, void 0, void 0, function* () {
            this._next({
                type: 'WRITE_PROFILE',
                message: `Writing profile ${group.username}`
            });
            yield this._writeUser(group);
            if (group.users) {
                for (let username in group.users) {
                    let userConfig = group.users[username];
                    userConfig.username = userConfig.username || username;
                    yield this._writeUser(userConfig, group);
                }
            }
        });
    }
    _writeBundle(bundle) {
        return __awaiter(this, void 0, void 0, function* () {
            this._next({
                type: 'WRITE_BUNDLE',
                message: `Writing bundle ${bundle.name || bundle.id}`
            });
            yield this.bundleConfigurator.write(this.tap, bundle, {
                bundleId: bundle.id
            });
            if (bundle.acls) {
                for (let profileName in bundle.acls) {
                    let profile = this.getProfileByName(profileName);
                    let acl = bundle.acls[profileName];
                    let profileId = getProfileId(profile);
                    // TODO may error when profile
                    if (profileId > 0xff) {
                        throw new Error(`ACL with profile id ${0xff} is not implemented yet`);
                    }
                    let fixedGroupId = profileId + 0x4100; // TODO change
                    let aclData = {
                        groupId: fixedGroupId,
                        read: acl.read,
                        write: acl.write
                    };
                    this._next({
                        type: 'WRITE_ACL',
                        message: `\tWriting acl for profile ${profileName} read=${acl.read} write=${acl.write}`,
                        payload: aclData
                    });
                    (yield this.tap.service.bundle.putAcl(bundle.id, aclData)).successful();
                }
            }
            if (bundle.features) {
                for (let feature of bundle.features) {
                    this._next({
                        type: 'WRITE_SPECIAL_FEATURE',
                        message: `\tAdded feature ${feature} for bundle ${bundle.name ||
                            bundle.id}`
                    });
                    let specialFeatureId = util.toEnum(feature, deviceClient.SpecialFeature);
                    (yield this.tap.service.interface.putSpecialFeatureProfile(specialFeatureId, bundle.id)).successful();
                }
            }
        });
    }
    get profiles() {
        if (this._schema.config.data) {
            return this._schema.config.data.profiles || [];
        }
        return [];
    }
    getProfileByName(profileName) {
        let profile = this.profiles.find(p => p.username == profileName);
        if (!profile) {
            profile = util.SPECIAL_USERS.find(p => p.username == profileName);
        }
        if (!profile) {
            throw new definitions.InvalidTapConfig(`Missing profile with name "${profileName}"`);
        }
        return profile;
    }
    _readBundles() {
        return __awaiter(this, void 0, void 0, function* () {
            let bundles = [];
            let bundleIds = yield this._listBundles();
            for (let bundleId of bundleIds) {
                try {
                    let bundle = yield this._readBundle(bundleId);
                    bundles.push(bundle);
                }
                catch (err) {
                    if (err && err.response) {
                        if (err.response.codeRet() ==
                            api.ResultCode.IOTIZE_404_NOT_FOUND) {
                            console.warn(`Skip bundle with id ${bundleId}`);
                            continue;
                        }
                    }
                    throw err;
                }
            }
            return bundles;
        });
    }
    _listBundles() {
        return __awaiter(this, void 0, void 0, function* () {
            // TODO smarter:
            // Read profile + dedudece bundels
            return Promise.resolve([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]);
        });
    }
    _listBundlesFromProfile() {
        return __awaiter(this, void 0, void 0, function* () {
            (yield this.tap.service.target.connect()).successful();
            let data = (yield this.tap.service.variable.readProfile()).body();
            let converter = new deviceClient.TlvBundleConverter();
            let mappings = converter.decode(data);
            let ids = Object.keys(mappings).map(key => parseInt(key));
            if (ids.length == 0) {
                console.warn(`No variable found`);
            }
            let bundles = [];
            for (let id of ids) {
                let bundleId = (yield this.tap.service.variable.getBundleId(id)).body();
                if (bundles.indexOf(bundleId) == -1) {
                    bundles.push(bundleId);
                }
            }
            return bundles;
        });
    }
    _readUserProfile(groupId) {
        return __awaiter(this, void 0, void 0, function* () {
            let profile = {
                id: groupId,
                username: yield this._getGroupName(groupId),
                lifeTime: (yield this.tap.service.group.getSessionLifetime(groupId)).body()
            };
            if (util.isSpecialUser(profile.username)) {
                delete profile['id'];
            }
            return profile;
        });
    }
    _readBundle(bundleId) {
        return __awaiter(this, void 0, void 0, function* () {
            let config = yield this.bundleConfigurator.read(this.tap, {
                bundleId: bundleId,
                readVariables: true
            });
            config.acl = yield this._readAcls(bundleId);
            return config;
        });
    }
    _readAcls(bundleId) {
        return __awaiter(this, void 0, void 0, function* () {
            let acls = {};
            if (!this._tmpConfig.data) {
                return acls;
            }
            let profiles = this._tmpConfig.data.profiles || [];
            for (let profileName in profiles) {
                if (profileName == 'admin')
                    continue;
                let acl = yield this._readAcl(profileName, bundleId);
                if (acl.read || acl.write) {
                    acls[profileName] = acl;
                }
            }
            return acls;
        });
    }
    _readAcl(profileName, bundleId) {
        return __awaiter(this, void 0, void 0, function* () {
            // let profile: RecursivePartial<ProfileConfig> = this._tmpConfig.data.profiles[profileName] as ProfileConfig;
            // let response = (await this.tap.service.bundle.getAcl(bundleId, 0x4100 + profile.id));//.body()!;  // TODO not implemented yet
            return {
                read: false,
                write: false
            };
        });
    }
    _readSpecialFeatures() {
        return __awaiter(this, void 0, void 0, function* () {
            let result = [];
            let specialFeaturesIds = enumValues(deviceClient.SpecialFeature);
            for (let featureId of specialFeaturesIds) {
                let specialFeatureValue = (yield this.tap.service.interface.getSpecialFeatureProfile(featureId)).body();
                if (!isSpecialFeatureSet(featureId, specialFeatureValue)) {
                    continue;
                }
                if (this._tmpConfig.data && this._tmpConfig.data.bundles) {
                    for (let group of this._tmpConfig.data.bundles) {
                        if (specialFeatureValue == group.id) {
                            let features = group.features || [];
                            let feature = toEnumString(featureId, deviceClient.SpecialFeature);
                            features.push(feature);
                            result[group.id] = features;
                            group.features = features;
                        }
                    }
                }
            }
            return result;
        });
    }
    _listProfiles() {
        return __awaiter(this, void 0, void 0, function* () {
            yield this.sleep(10);
            let extraProfiles = [];
            let profileId = 1;
            let response;
            do {
                response = yield this.tap.service.group.getSessionLifetime(profileId);
                if (response.isSuccessful()) {
                    extraProfiles.push(profileId);
                }
                profileId++;
            } while (response.isSuccessful() && profileId < 0xfffe);
            return extraProfiles.concat([0, 0xffff, 0xfffe]);
        });
    }
    // READ VAR
    errorHandler(errorCode, err) {
        let error = new SchemaTapConfigurator.MyError(errorCode, err);
        // TODO
    }
    _next(event) {
        if (this._writeSubject) {
            this._writeSubject.next(event);
        }
    }
}
exports.SchemaTapConfigurator = SchemaTapConfigurator;
(function (SchemaTapConfigurator) {
    class MyError extends Error {
        constructor(code, cause) {
            super(cause.message);
            this.code = code;
            this.cause = cause;
        }
    }
    SchemaTapConfigurator.MyError = MyError;
    let ErrorCode;
    (function (ErrorCode) {
        ErrorCode[ErrorCode["ReadVariableType"] = 0] = "ReadVariableType";
    })(ErrorCode = SchemaTapConfigurator.ErrorCode || (SchemaTapConfigurator.ErrorCode = {}));
})(SchemaTapConfigurator = exports.SchemaTapConfigurator || (exports.SchemaTapConfigurator = {}));
// class VariableMetaConfiguratorV1_60 implements SchemaTapConfigurator.ConfiguratorPart<VariableMeta, { variableId: number }>{
//     async read(tap: Tap, readOptions: { variableId: number }): Promise<VariableMeta> {
//         let variableId = readOptions.variableId;
//         let name = _bodyOrDefault(await tap.service.variable.getName(variableId), `variable_${variableId}`);
//         let unit = _bodyOrDefault(await tap.service.variable.getName(variableId), undefined);
//         return {
//             name: name,
//             unit: unit,
//             id: variableId
//         }
//     }
//     async write(tap: Tap, variable: VariableMeta): Promise<void> {
//         if (variable.name || variable.unit) {
//             variable.name = variable.name.substr(0, 12); // TODO change
//             await tap.service.variable.putName(variable.id, variable.name);
//         }
//         if (variable.unit) {
//             variable.unit = variable.unit.substr(0, 4); // TODO change
//             await tap.service.variable.putUnit(variable.id, variable.unit);
//         }
//     }
// }
class MemoryBundleConfigurator {
    constructor(variableConfigurator = new VariableConfigurator()) {
        this.variableConfigurator = variableConfigurator;
    }
    get writeEvents() {
        if (!this._writeSubject) {
            this._writeSubject = new rxjs.Subject();
        }
        return this._writeSubject;
    }
    read(tap, readOptions) {
        return __awaiter(this, void 0, void 0, function* () {
            let bundleId = readOptions.bundleId;
            let bundle = {
                id: bundleId,
                name: _bodyOrDefault(yield tap.service.bundle.getName(bundleId), `bundle_${bundleId}`),
                dataLog: {
                    period: (yield tap.service.bundle.getDataLogPeriod(bundleId)).body()
                },
                features: []
            };
            if (readOptions.readVariables) {
                bundle.variables = yield this._readVariables(tap, bundleId);
            }
            return bundle;
        });
    }
    _readVariables(tap, bundleId) {
        return __awaiter(this, void 0, void 0, function* () {
            let variablesForBundle = [];
            try {
                variablesForBundle = yield this._listVariablesForBundle(tap, bundleId);
            }
            catch (err) {
                // this._readSubject.next({
                //     message: 'Cannot read list of variables',
                //     type: 'ERROR',
                //     payload: err
                // })
                // TODO
                console.warn(`Cannot read variables for bundleId: ${bundleId}`, err);
            }
            let variables = [];
            for (let variableId of variablesForBundle) {
                variables.push(yield this._readVariable(tap, variableId));
            }
            return variables;
        });
    }
    _readVariable(tap, variableId) {
        return this.variableConfigurator.read(tap, {
            variableId: variableId
        });
    }
    _listVariablesForBundle(tap, bundleId) {
        return __awaiter(this, void 0, void 0, function* () {
            (yield tap.service.target.connect()).successful();
            let response = yield tap.service.bundle.getValues(bundleId);
            response.successful();
            let data = response.rawBody();
            let converter = new deviceClient.TlvBundleConverter();
            let mappings = converter.decode(data);
            return Object.keys(mappings).map(key => parseInt(key));
        });
    }
    write(tap, bundle, writeOptions) {
        return __awaiter(this, void 0, void 0, function* () {
            this._next({
                type: 'WRITE_BUNDLE',
                message: `\tWriting bundle ${bundle.name || bundle.id} (id=${bundle.id})`,
                payload: {
                    bundle
                }
            });
            (yield tap.service.bundle.create(bundle.id)).successful();
            if (bundle.dataLog) {
                (yield tap.service.bundle.putDataLogPeriod(bundle.id, bundle.dataLog.period || 0)).successful();
            }
            if (bundle.name) {
                yield tap.service.bundle.putName(bundle.id, bundle.name.substr(0, 12)); // TODO max length ?
            }
            if (bundle.variables) {
                for (let variable of bundle.variables) {
                    yield this._writeVariable(tap, bundle, variable);
                }
            }
        });
    }
    _writeVariable(tap, bundle, variable) {
        this._next({
            type: 'WRITE_VARIABLE',
            message: `\tWriting variable ${variable.type} ${variable.name || variable.id} (id=${variable.id}) (bundle: ${bundle.name || bundle.id} - id=${bundle.id})`,
            payload: {
                variable,
                bundle
            }
        });
        return this.variableConfigurator.write(tap, variable, {
            bundleId: bundle.id
        });
    }
    _next(event) {
        if (this._writeSubject) {
            this._writeSubject.next(event);
        }
    }
}
exports.MemoryBundleConfigurator = MemoryBundleConfigurator;
class VariableConfigurator {
    constructor(stringConverter = utf8Converter) {
        this.stringConverter = stringConverter;
    }
    read(tap, readOptions) {
        return __awaiter(this, void 0, void 0, function* () {
            let id = readOptions.variableId;
            // Todo handle modbus variable
            let data = {
                id: id,
                type: yield this.readVariableType(tap, id),
                address: yield this.readVariableAddress(tap, id),
                // format: toEnumString((await tap.service.variable.getFormat(id)).body()!, VariableFormat),
                length: (yield tap.service.variable.getNumberOfElements(id)).body(),
                bundleId: (yield tap.service.variable.getBundleId(id)).body()
            };
            let metaDataResponse = yield tap.service.variable.getMetaData(id);
            if (metaDataResponse.isSuccessful()) {
                let metaData = this._decodeMetaData(metaDataResponse.rawBody());
                data = Object.assign(Object.assign({}, data), metaData);
            }
            return data;
        });
    }
    write(tap, variable, writeOptions) {
        return __awaiter(this, void 0, void 0, function* () {
            let id = variable.id;
            let metaData = this._encodeVariableMetaData(variable);
            let metaDataImplemented = true;
            if (metaData.length > 0) {
                let response = yield tap.service.variable.create(id, metaData.length);
                if (response.codeRet() == api.ResultCode.IOTIZE_501_NOT_IMPLEMENTED) {
                    metaDataImplemented = false;
                    console.warn(`This tap does not support extra variable size`);
                    yield this._writeCall(tap.lwm2m.post(`/1029/${id}/65535`));
                }
            }
            else {
                yield this._writeCall(tap.lwm2m.post(`/1029/${id}/65535`));
            }
            yield this._writeCall(tap.service.variable.putAddress(id, parseAddress(variable.address)));
            yield this._writeCall(tap.service.variable.putBundleId(id, writeOptions.bundleId));
            yield this._writeCall(tap.service.variable.putFormat(id, util.toEnum(variable.type, deviceClient.VariableType)));
            yield this._writeCall(tap.service.variable.putNumberOfElements(id, variable.length));
            // Optional call
            if (metaDataImplemented) {
                yield tap.service.variable.putMetaData(id, metaData);
            }
        });
    }
    _encodeVariableMetaData(variable) {
        if (!variable.name && !variable.unit) {
            return new Uint8Array(0);
        }
        return byteConverter.asciiStringToByteBuffer(`${variable.name}:${variable.unit || ''}`);
    }
    _decodeMetaData(data) {
        let dataString = this.stringConverter.decode(data);
        let meta = {};
        let parts = dataString.split(':');
        if (parts.length >= 1) {
            meta.name = parts[0];
        }
        if (parts.length >= 2) {
            meta.unit = parts[1];
        }
        return meta;
    }
    readVariableAddress(tap, id) {
        return __awaiter(this, void 0, void 0, function* () {
            try {
                return `0x${(yield tap.service.variable.getAddress(id))
                    .body()
                    .toString(16)}`;
            }
            catch (err) {
                return `0x0`;
            }
        });
    }
    readVariableType(tap, id) {
        return __awaiter(this, void 0, void 0, function* () {
            try {
                let response = yield tap.service.variable.getType(id);
                return toEnumString(response.body(), deviceClient.VariableType);
            }
            catch (err) {
                this.errorHandler(SchemaTapConfigurator.ErrorCode.ReadVariableType, err);
                return 'INT8';
            }
        });
    }
    errorHandler(errorCode, err) {
        let error = new SchemaTapConfigurator.MyError(errorCode, err);
        // TODO
    }
    _writeCall(call, description) {
        return __awaiter(this, void 0, void 0, function* () {
            (yield call).successful();
        });
    }
}
exports.VariableConfigurator = VariableConfigurator;

});

var schemaTapConfigurator$1 = unwrapExports(schemaTapConfigurator);
var schemaTapConfigurator_1 = schemaTapConfigurator.bytesToUtf8;
var schemaTapConfigurator_2 = schemaTapConfigurator.InvalidTapSchemaConfig;
var schemaTapConfigurator_3 = schemaTapConfigurator.semanticVersionStringToInt;
var schemaTapConfigurator_4 = schemaTapConfigurator.parseTapConfig;
var schemaTapConfigurator_5 = schemaTapConfigurator.isJsonString;
var schemaTapConfigurator_6 = schemaTapConfigurator.fromXml;
var schemaTapConfigurator_7 = schemaTapConfigurator.validateTapConfig;
var schemaTapConfigurator_8 = schemaTapConfigurator.checkErrors;
var schemaTapConfigurator_9 = schemaTapConfigurator.SchemaTapConfigurator;
var schemaTapConfigurator_10 = schemaTapConfigurator.MemoryBundleConfigurator;
var schemaTapConfigurator_11 = schemaTapConfigurator.VariableConfigurator;

var tapConfigSchemaBuilder = createCommonjsModule(function (module, exports) {
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
class TapConfigSchemaBuilder {
    constructor(_schema) {
        this._schema = _schema;
    }
    static default() {
        return new TapConfigSchemaBuilder({
            meta: {},
            config: {
                application: {
                    name: "TAP"
                },
                version: "1.0.0",
                tap: {
                    security: {
                        factoryReset: false,
                        hashPassword: true,
                        resourceFactory: false,
                        resourceLogUid: false,
                        scramActivated: false
                    },
                    hashIterationCount: 5000
                },
                data: {
                    profiles: [
                        // {
                        //     username: "anonymous",
                        //     lifeTime: 300
                        // },
                        {
                            username: "admin",
                            password: "admin",
                            lifeTime: 300
                        }
                        // {
                        //     username: "supervisor",
                        //     password: "supervisor",
                        //     lifeTime: 300
                        // }
                    ]
                }
            }
        });
    }
    appName(appName) {
        this._schema.config.application.name = appName;
        return this;
    }
    user(username, editor) {
        let userDataIndex = this._schema.config.data.profiles.findIndex(p => p.username === username);
        let userData = userDataIndex >= 0 ? this._schema.config.data.profiles[userDataIndex] : undefined;
        if (!userData) {
            // TODO check SpecialProfileConfig
            userData = {
                username: username,
                lifeTime: 300,
                password: username,
            };
        }
        userData = editor(userData);
        this._schema.config.data.profiles[userDataIndex] = userData;
        return this;
    }
    scram(enabled) {
        this._schema.config.tap.security.scramActivated = enabled;
        if (enabled) {
            this._schema.config.tap.security.hashPassword = true;
        }
        return this;
    }
    build() {
        return this._schema;
    }
}
exports.TapConfigSchemaBuilder = TapConfigSchemaBuilder;

});

var tapConfigSchemaBuilder$1 = unwrapExports(tapConfigSchemaBuilder);
var tapConfigSchemaBuilder_1 = tapConfigSchemaBuilder.TapConfigSchemaBuilder;

var lib = createCommonjsModule(function (module, exports) {
"use strict";
function __export(m) {
    for (var p in m) if (!exports.hasOwnProperty(p)) exports[p] = m[p];
}
Object.defineProperty(exports, "__esModule", { value: true });
__export(schemaTapConfigurator);
__export(tapConfigConverter);
__export(tapAppPathConverter);
__export(tapConfigSchemaBuilder);

});

var index = unwrapExports(lib);

var public_api = createCommonjsModule(function (module, exports) {
"use strict";
function __export(m) {
    for (var p in m) if (!exports.hasOwnProperty(p)) exports[p] = m[p];
}
Object.defineProperty(exports, "__esModule", { value: true });
__export(lib);

});

var public_api$1 = unwrapExports(public_api);

var iotizeDeviceClient_jsConfigurator = createCommonjsModule(function (module, exports) {
"use strict";
/**
 * Generated bundle index. Do not edit.
 */
function __export(m) {
    for (var p in m) if (!exports.hasOwnProperty(p)) exports[p] = m[p];
}
Object.defineProperty(exports, "__esModule", { value: true });
__export(public_api);

exports.ɵb = definitions.InvalidTapConfig;

});

var iotizeDeviceClient_jsConfigurator$1 = unwrapExports(iotizeDeviceClient_jsConfigurator);

export default iotizeDeviceClient_jsConfigurator$1;
//# sourceMappingURL=iotize-device-client.js-configurator.js.map
