import { BaseError, CodeError } from '@iotize/common/error';
import { VariableType } from '@iotize/tap/service/impl/variable';
import { hexStringToBuffer, bufferToHexString } from '@iotize/common/byte-converter';
import { KaitaiStreamWriter } from '@iotize/common/byte-stream';
import { DotAccessor } from '@iotize/common/data-accessor';
import { HostProtocol, SpecialFeature } from '@iotize/tap';
import { ANONYMOUS_USER, isSpecialUser, ADMIN_USER } from '@iotize/tap/configurator';
import { TapnpassProtocolConfigurationConverter, SecurityOptionsConverter, reversedTargetSerialModbusConfiguration, SerialSettings, VariableType as VariableType$1 } from '@iotize/tap/service/all';
import { converters } from '@iotize/tap/service/core';
import { parse } from 'fast-xml-parser';

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 __());
    };
})();
var InvalidConfigurationError = /** @class */ (function (_super) {
    __extends(InvalidConfigurationError, _super);
    function InvalidConfigurationError(msg) {
        return _super.call(this, msg) || this;
    }
    return InvalidConfigurationError;
}(BaseError));

var ByteOrder32Bits;
(function (ByteOrder32Bits) {
    ByteOrder32Bits[ByteOrder32Bits["B3_B2_B1_B0"] = 0] = "B3_B2_B1_B0";
    ByteOrder32Bits[ByteOrder32Bits["B1_B0_B3_B2"] = 1] = "B1_B0_B3_B2";
    ByteOrder32Bits[ByteOrder32Bits["B2_B3_B0_B1"] = 2] = "B2_B3_B0_B1";
    ByteOrder32Bits[ByteOrder32Bits["B0_B1_B2_B3"] = 3] = "B0_B1_B2_B3";
})(ByteOrder32Bits || (ByteOrder32Bits = {}));
var ByteOrder16Bits;
(function (ByteOrder16Bits) {
    ByteOrder16Bits[ByteOrder16Bits["B1_B0"] = 0] = "B1_B0";
    ByteOrder16Bits[ByteOrder16Bits["B0_B1"] = 1] = "B0_B1";
})(ByteOrder16Bits || (ByteOrder16Bits = {}));

function toEnumFromValue(mapping, value) {
    var values = Object.keys(mapping).map(function (key) { return mapping[key]; });
    if (values.find(function (v) { return v.toString() === value; }) ===
        undefined) {
        throw new Error("Missing value \"" + value + "\" in enum. Available keys are " + values.join(', '));
    }
    if (value in mapping) {
        // For string to number convertion
        return mapping[mapping[value]];
    }
    return value;
}
function toEnumType(value, mapping) {
    if (!(value in mapping)) {
        throw new Error("Missing mapping for " + value + ". Available: " + Object.keys(mapping).join(', '));
    }
    return mapping[value];
}
// export function toEnumFromKey<T>(mapping: any, key: string): T {
//     if (!(key in mapping)) {
//         throw new Error(`Missing key "${key}" in enum. Available keys are ${Object.keys(mapping).join(', ')}`);
//     }
//     return mapping[mapping[key]];
// }

var IotizeVariableModel = /** @class */ (function () {
    function IotizeVariableModel(data, iotizeModule, bundle) {
        this.data = data;
        this.iotizeModule = iotizeModule;
        if (bundle) {
            this._bundle = bundle;
        }
    }
    IotizeVariableModel.prototype.toJson = function () {
        return this.data;
    };
    IotizeVariableModel.prototype.module = function () {
        return this.iotizeModule;
    };
    /**
     * Size (int bits) of one element
     */
    IotizeVariableModel.prototype.valueSize = function () {
        return IotizeVariableModel.unitSizeToBitSize(parseInt(this.data.$.Unit));
    };
    IotizeVariableModel.prototype.valueQuantity = function () {
        return Number(this.data.$.Quantity);
    };
    IotizeVariableModel.prototype.name = function () {
        return this.data.$.GUI_Name;
    };
    IotizeVariableModel.prototype.id = function () {
        return this.data.$.Id;
    };
    IotizeVariableModel.prototype.alias = function () {
        return this.data.$.GUI_Alias;
    };
    IotizeVariableModel.prototype.description = function () {
        return this.data.$.GUI_Description;
    };
    IotizeVariableModel.prototype.unit = function () {
        return this.data.$.GUI_DispUnit;
    };
    IotizeVariableModel.prototype.bundle = function () {
        return this._bundle;
    };
    // public type(): VariableType.Data {
    //     if (this.data.$.GUI_float && this.data.$.GUI_float == 1) {
    //         return VariableType.Data.FLOAT32;
    //     } else if (this.data.$.GUI_unsigned && this.data.$.GUI_unsigned == 1) {
    //         return VariableType.Data.UINT32;
    //     } else {
    //         return VariableType.Data.INT32;
    //     }
    // }
    IotizeVariableModel.prototype.dataType = function () {
        var unit = parseInt(this.data.$.Unit, 10);
        if (this.data.$.GUI_float && this.data.$.GUI_float == 1) {
            return VariableType.Data.FLOAT32;
        }
        else if (this.data.$.GUI_unsigned && this.data.$.GUI_unsigned == 1) {
            switch (unit) {
                case 0:
                    return VariableType.Data.BOOLEAN;
                case 1:
                    return VariableType.Data.UINT8;
                case 2:
                    return VariableType.Data.UINT16;
                case 3:
                    return VariableType.Data.UINT32;
                default:
                    throw new Error("Invalid data size \"" + unit + "\" for variable " + this.name());
            }
        }
        else {
            switch (unit) {
                case 0:
                    return VariableType.Data.BOOLEAN;
                case 1:
                    return VariableType.Data.INT8;
                case 2:
                    return VariableType.Data.INT16;
                case 3:
                    return VariableType.Data.INT32;
                default:
                    throw new Error("Invalid data size \"" + unit + "\" for variable " + this.name());
            }
        }
    };
    /**
     * Return attribute by name
     */
    IotizeVariableModel.prototype.attribute = function (name) {
        return this.data.$[name];
    };
    IotizeVariableModel.prototype.byteOrder = function () {
        var guiCoding = this.attribute('GUI_coding');
        if (guiCoding) {
            if (this.valueSize() === 16) {
                return toEnumFromValue(ByteOrder16Bits, guiCoding);
            }
            else if (this.valueSize() === 32) {
                return toEnumFromValue(ByteOrder32Bits, guiCoding);
            }
        }
        return undefined;
    };
    /**
     * Convert unit size found in configuration file into a number of bits
     */
    IotizeVariableModel.unitSizeToBitSize = function (unitSize) {
        switch (unitSize) {
            case 0:
                return 1;
            case 1:
                return 8;
            case 2:
                return 16;
            case 3:
                return 32;
            default:
                throw new Error("Invalid unit size: " + unitSize);
        }
    };
    return IotizeVariableModel;
}());

var IotizeProfileModel = /** @class */ (function () {
    function IotizeProfileModel(data) {
        this.data = data;
    }
    IotizeProfileModel.prototype.toJson = function () {
        return this.data;
    };
    IotizeProfileModel.prototype.description = function () {
        return this.data.$.GUI_Description;
    };
    IotizeProfileModel.prototype.name = function () {
        return this.data.$.Name;
    };
    IotizeProfileModel.prototype.id = function () {
        return this.data.$.Id;
    };
    return IotizeProfileModel;
}());

var IotizeACL = /** @class */ (function () {
    function IotizeACL(data, aclGroup) {
        this.aclGroup = aclGroup;
        this.data = data;
    }
    IotizeACL.create = function (profileName, aclGroup) {
        return new IotizeACL({
            $: {
                ProfileName: profileName,
                Rights: ''
            }
        }, aclGroup);
    };
    IotizeACL.prototype.toJson = function () {
        return this.data;
    };
    IotizeACL.prototype.listRights = function () {
        var rights = this._getProperty('Rights', '');
        if (rights.length === 0) {
            return [];
        }
        return rights.split(',').map(function (entry) {
            return entry.trim();
        });
    };
    IotizeACL.prototype.username = function () {
        return this._getProperty('ProfileName');
    };
    IotizeACL.prototype.hasRight = function (name) {
        if (this.username() === 'admin') {
            return true;
        }
        var rights = this.listRights();
        return rights.indexOf(name) !== -1;
    };
    IotizeACL.prototype._getProperty = function (name, defaultValue) {
        if (!(name in this.data.$)) {
            if (defaultValue === undefined) {
                throw new InvalidConfigurationError('Missing key ' + name + ' in IoTize ACL');
            }
            return defaultValue;
        }
        return this.data.$[name];
    };
    return IotizeACL;
}());

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 ANONYMOUS_PROFILE = 'anonymous';
/**
 *
 */
var IotizeACLGroup = /** @class */ (function () {
    function IotizeACLGroup(data, bundle) {
        this.bundle = bundle;
        this.data = data;
    }
    IotizeACLGroup.prototype.toJson = function () {
        return this.data;
    };
    /**
     * Get acl for a profile
     * @param profileName
     */
    IotizeACLGroup.prototype.getByProfileName = function (profileName) {
        var e_1, _a;
        var profiles = this.listProfiles();
        try {
            for (var profiles_1 = __values(profiles), profiles_1_1 = profiles_1.next(); !profiles_1_1.done; profiles_1_1 = profiles_1.next()) {
                var profileAcl = profiles_1_1.value;
                if (profileAcl.username() === profileName) {
                    return profileAcl;
                }
            }
        }
        catch (e_1_1) { e_1 = { error: e_1_1 }; }
        finally {
            try {
                if (profiles_1_1 && !profiles_1_1.done && (_a = profiles_1.return)) _a.call(profiles_1);
            }
            finally { if (e_1) throw e_1.error; }
        }
        return IotizeACL.create(profileName, this);
    };
    IotizeACLGroup.prototype.hasRight = function (profileName, right) {
        var anonymousProfile = this.getByProfileName(ANONYMOUS_PROFILE);
        if (anonymousProfile && anonymousProfile.hasRight(right)) {
            return true;
        }
        if (profileName !== ANONYMOUS_PROFILE) {
            var profile = this.getByProfileName(profileName);
            if (profile) {
                return profile.hasRight(right);
            }
        }
        return false;
    };
    /**
     * List acl entries for each profile
     */
    IotizeACLGroup.prototype.listProfiles = function () {
        var e_2, _a;
        if (!this.data.IOTizeACL) {
            return [];
        }
        var entries = [];
        try {
            for (var _b = __values(this.data.IOTizeACL), _c = _b.next(); !_c.done; _c = _b.next()) {
                var data = _c.value;
                entries.push(new IotizeACL(data, this));
            }
        }
        catch (e_2_1) { e_2 = { error: e_2_1 }; }
        finally {
            try {
                if (_c && !_c.done && (_a = _b.return)) _a.call(_b);
            }
            finally { if (e_2) throw e_2.error; }
        }
        return entries;
    };
    return IotizeACLGroup;
}());

var __values$1 = (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 IotizeBundleModel = /** @class */ (function () {
    function IotizeBundleModel(data, iotizeModule) {
        this.data = data;
        this.iotizeModule = iotizeModule;
    }
    IotizeBundleModel.prototype.toJson = function () {
        return this.data;
    };
    IotizeBundleModel.prototype.module = function () {
        return this.iotizeModule;
    };
    IotizeBundleModel.prototype.id = function () {
        if (!this.data.$.Id) {
            throw new InvalidConfigurationError('This bundle does not have an id');
        }
        return this.data.$.Id;
    };
    IotizeBundleModel.prototype.name = function () {
        return this.data.$.GUI_Name || '#' + this.id();
    };
    IotizeBundleModel.prototype.dataLogPeriod = function () {
        if (!this.data.$.DataLogPeriod) {
            return 0;
        }
        return Number(this.data.$.DataLogPeriod);
    };
    IotizeBundleModel.prototype.hasDataLog = function () {
        return this.dataLogPeriod() > 0;
    };
    IotizeBundleModel.prototype.description = function () {
        return this.data.$.GUI_Description;
    };
    IotizeBundleModel.prototype.variable = function (variableId) {
        var e_1, _a;
        try {
            for (var _b = __values$1(this.variables()), _c = _b.next(); !_c.done; _c = _b.next()) {
                var variable = _c.value;
                if (variable.id() === variableId) {
                    return variable;
                }
            }
        }
        catch (e_1_1) { e_1 = { error: e_1_1 }; }
        finally {
            try {
                if (_c && !_c.done && (_a = _b.return)) _a.call(_b);
            }
            finally { if (e_1) throw e_1.error; }
        }
        throw new VariableDoesNotExistError(variableId, this.iotizeModule);
    };
    IotizeBundleModel.prototype.hasVariable = function (variableId) {
        var e_2, _a;
        try {
            for (var _b = __values$1(this.variables()), _c = _b.next(); !_c.done; _c = _b.next()) {
                var variable = _c.value;
                if (variable.id() === variableId) {
                    return variable;
                }
            }
        }
        catch (e_2_1) { e_2 = { error: e_2_1 }; }
        finally {
            try {
                if (_c && !_c.done && (_a = _b.return)) _a.call(_b);
            }
            finally { if (e_2) throw e_2.error; }
        }
        return false;
    };
    IotizeBundleModel.prototype.profiles = function () {
        var e_3, _a;
        if (!this.data.IOTizeAccessControl) {
            return [];
        }
        var profiles = [];
        try {
            for (var _b = __values$1(this.data.IOTizeAccessControl), _c = _b.next(); !_c.done; _c = _b.next()) {
                var profile = _c.value;
                profiles.push(new IotizeProfileModel(profile));
            }
        }
        catch (e_3_1) { e_3 = { error: e_3_1 }; }
        finally {
            try {
                if (_c && !_c.done && (_a = _b.return)) _a.call(_b);
            }
            finally { if (e_3) throw e_3.error; }
        }
        return profiles;
    };
    IotizeBundleModel.prototype.variables = function () {
        var e_4, _a;
        if (!this.data.IOTizeVariable) {
            return [];
        }
        var variables = [];
        try {
            for (var _b = __values$1(this.data.IOTizeVariable), _c = _b.next(); !_c.done; _c = _b.next()) {
                var variable = _c.value;
                variables.push(new IotizeVariableModel(variable, this.iotizeModule, this));
            }
        }
        catch (e_4_1) { e_4 = { error: e_4_1 }; }
        finally {
            try {
                if (_c && !_c.done && (_a = _b.return)) _a.call(_b);
            }
            finally { if (e_4) throw e_4.error; }
        }
        return variables;
    };
    IotizeBundleModel.prototype.getACLGroup = function () {
        var data = {};
        if (!this.data.IOTizeAccessControl) {
            this.data.IOTizeAccessControl = [];
        }
        if (this.data.IOTizeAccessControl.length > 0) {
            data = this.data.IOTizeAccessControl[0];
        }
        return new IotizeACLGroup(data, this);
    };
    return IotizeBundleModel;
}());

var __extends$1 = (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 __());
    };
})();
var __values$2 = (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 BundleDoesNotExistError = /** @class */ (function (_super) {
    __extends$1(BundleDoesNotExistError, _super);
    // static CODE = 'BundleDoesNotExistError';
    function BundleDoesNotExistError(bundleId, config) {
        var _this = _super.call(this, 'Bundle "' + bundleId + '" does not exist.') || this;
        _this.bundleId = bundleId;
        _this.config = config;
        return _this;
    }
    return BundleDoesNotExistError;
}(BaseError));
/**
 *
 */
var VariableDoesNotExistError = /** @class */ (function (_super) {
    __extends$1(VariableDoesNotExistError, _super);
    // static CODE = 'VariableDoesNotExistError';
    function VariableDoesNotExistError(variableId, config) {
        var _this = _super.call(this, "Variable \"" + variableId + "\" does not exist.") || this;
        _this.variableId = variableId;
        _this.config = config;
        return _this;
    }
    return VariableDoesNotExistError;
}(BaseError));
/**
 * Class representing an iotize studio configuration for a module
 */
var IotizeConfigModel = /** @class */ (function () {
    function IotizeConfigModel(data) {
        if (!data.IOTizeConfiguration) {
            //console.error('Trying to create an IotizeConfigModel with invalid data: ', data);
            throw new InvalidConfigurationError('Key IOTizeConfiguration must exists');
        }
        if (!data.IOTizeConfiguration.IOTizeApp) {
            throw new InvalidConfigurationError('Key IOTizeConfiguration.IOTizeApp must exists');
        }
        if (data.IOTizeConfiguration.IOTizeApp.length === 0 ||
            !data.IOTizeConfiguration.IOTizeApp[0]['$']) {
            throw new InvalidConfigurationError('No IOTizeConfiguration.IOTizeApp defined in this configuration file');
        }
        this.data = data;
    }
    IotizeConfigModel.prototype.toJson = function () {
        return this.data;
    };
    IotizeConfigModel.prototype.getAppConfig = function () {
        return this.data.IOTizeConfiguration.IOTizeApp[0];
    };
    /**
     * Get application name
     */
    IotizeConfigModel.prototype.appName = function () {
        if (!this.getAppConfig().$.Name) {
            throw new InvalidConfigurationError('Application Name is not set');
        }
        return this.getAppConfig().$.Name;
    };
    /**
     * Get config version
     */
    IotizeConfigModel.prototype.version = function () {
        if (!this.getAppConfig().$.Config) {
            throw new InvalidConfigurationError('App config version is not set');
        }
        return this.getAppConfig().$.Config;
    };
    /**
     * Get module serial number
     */
    IotizeConfigModel.prototype.id = function () {
        if (!this.getAppConfig().$.DevIoTizeSN) {
            throw new InvalidConfigurationError('Serial number is not set');
        }
        return this.getAppConfig().$.DevIoTizeSN;
    };
    /**
     * Set module serial number
     * @param id
     */
    IotizeConfigModel.prototype.setId = function (id) {
        this.getAppConfig().$.DevIoTizeSN = id;
        return this;
    };
    /**
     * Alias for this.id();
     */
    IotizeConfigModel.prototype.serialNumber = function () {
        return this.id();
    };
    /**
     * Get an array of all bundles
     */
    IotizeConfigModel.prototype.bundles = function () {
        var e_1, _a;
        if (!this.data.IOTizeConfiguration.IOTizeBundle) {
            return [];
        }
        var bundles = [];
        try {
            for (var _b = __values$2(this.data.IOTizeConfiguration.IOTizeBundle), _c = _b.next(); !_c.done; _c = _b.next()) { // TODO bug if only one bundle
                var bundle = _c.value;
                bundles.push(new IotizeBundleModel(bundle, this));
            }
        }
        catch (e_1_1) { e_1 = { error: e_1_1 }; }
        finally {
            try {
                if (_c && !_c.done && (_a = _b.return)) _a.call(_b);
            }
            finally { if (e_1) throw e_1.error; }
        }
        return bundles;
    };
    /**
     * Array of bundles available for a specific profile
     */
    IotizeConfigModel.prototype.bundleAvailableForProfileName = function (profileName) {
        return this.bundles().reduce(function (list, currentValue) {
            if (currentValue.getACLGroup().hasRight(profileName, 'read')) {
                list.push(currentValue);
            }
            return list;
        }, []);
    };
    /**
     * Get an array of all variables
     */
    IotizeConfigModel.prototype.variables = function () {
        var e_2, _a, e_3, _b;
        if (!this.data.IOTizeConfiguration.IOTizeBundle) {
            return [];
        }
        var variables = {};
        try {
            for (var _c = __values$2(this.data.IOTizeConfiguration.IOTizeBundle), _d = _c.next(); !_d.done; _d = _c.next()) {
                var bundle = _d.value;
                bundle = new IotizeBundleModel(bundle, this);
                try {
                    //            console.log('VAR: ', bundle.variables());
                    for (var _e = (e_3 = void 0, __values$2(bundle.variables())), _f = _e.next(); !_f.done; _f = _e.next()) {
                        var variable = _f.value;
                        variables[variable.name()] = variable;
                    }
                }
                catch (e_3_1) { e_3 = { error: e_3_1 }; }
                finally {
                    try {
                        if (_f && !_f.done && (_b = _e.return)) _b.call(_e);
                    }
                    finally { if (e_3) throw e_3.error; }
                }
            }
        }
        catch (e_2_1) { e_2 = { error: e_2_1 }; }
        finally {
            try {
                if (_d && !_d.done && (_a = _c.return)) _a.call(_c);
            }
            finally { if (e_2) throw e_2.error; }
        }
        var res = [];
        for (var key in variables) {
            res.push(variables[key]);
        }
        return res;
    };
    /**
     * Get bundle by id, if it does not exist, throw BundleDoesNotExistError
     * @throws BundleDoesNotExistError
     * @param id
     */
    IotizeConfigModel.prototype.bundle = function (id) {
        var e_4, _a;
        try {
            for (var _b = __values$2(this.bundles()), _c = _b.next(); !_c.done; _c = _b.next()) {
                var bundle = _c.value;
                if (bundle.id() === id) {
                    return bundle;
                }
            }
        }
        catch (e_4_1) { e_4 = { error: e_4_1 }; }
        finally {
            try {
                if (_c && !_c.done && (_a = _b.return)) _a.call(_b);
            }
            finally { if (e_4) throw e_4.error; }
        }
        throw new BundleDoesNotExistError(id, this);
    };
    IotizeConfigModel.prototype.hasBundle = function (id) {
        var e_5, _a;
        try {
            for (var _b = __values$2(this.bundles()), _c = _b.next(); !_c.done; _c = _b.next()) {
                var bundle = _c.value;
                if (bundle.id() === id) {
                    return bundle;
                }
            }
        }
        catch (e_5_1) { e_5 = { error: e_5_1 }; }
        finally {
            try {
                if (_c && !_c.done && (_a = _b.return)) _a.call(_b);
            }
            finally { if (e_5) throw e_5.error; }
        }
        return false;
    };
    /**
     * Get variable by id, if it does not exist, throw VariableDoesNotExistError
     * @throws VariableDoesNotExistError
     * @param id
     */
    IotizeConfigModel.prototype.variable = function (variableId) {
        var e_6, _a;
        try {
            for (var _b = __values$2(this.variables()), _c = _b.next(); !_c.done; _c = _b.next()) {
                var variable = _c.value;
                if (variable.id() == variableId) {
                    return variable;
                }
            }
        }
        catch (e_6_1) { e_6 = { error: e_6_1 }; }
        finally {
            try {
                if (_c && !_c.done && (_a = _b.return)) _a.call(_b);
            }
            finally { if (e_6) throw e_6.error; }
        }
        throw new VariableDoesNotExistError(variableId, this);
    };
    /**
     * Get an array of all profiles
     */
    IotizeConfigModel.prototype.profiles = function () {
        var e_7, _a;
        if (!this.data.IOTizeConfiguration.IOTizeProfile) {
            return [];
        }
        var profiles = [];
        try {
            for (var _b = __values$2(this.data.IOTizeConfiguration.IOTizeProfile), _c = _b.next(); !_c.done; _c = _b.next()) { // TODO bug if there is only one profile...
                var profile = _c.value;
                profiles.push(new IotizeProfileModel(profile));
            }
        }
        catch (e_7_1) { e_7 = { error: e_7_1 }; }
        finally {
            try {
                if (_c && !_c.done && (_a = _b.return)) _a.call(_b);
            }
            finally { if (e_7) throw e_7.error; }
        }
        return profiles;
    };
    IotizeConfigModel.prototype.description = function () {
        return this.getAppConfig().$.GUI_Description;
    };
    IotizeConfigModel.prototype.name = function () {
        return this.getAppConfig().$.GUI_Name;
    };
    return IotizeConfigModel;
}());

/**
 * Utility class to create iotize config model from XML or JSON file
 */
var IotizeConfigFactory = /** @class */ (function () {
    function IotizeConfigFactory() {
    }
    /**
     * Create IotizeConfigModel from a XML string
     * @param xml
     */
    IotizeConfigFactory.fromXML = function (xml) {
        throw new Error("Add import to @iotize/tap/config/iotize-studio/parser/xml to create configuration model from XML file");
    };
    /**
     * Create IotizeConfigModel from a JSON string
     * @param json
     */
    IotizeConfigFactory.fromJson = function (json) {
        return new IotizeConfigModel(JSON.parse(json));
    };
    /**
     * Build and IotizeConfigModel
     * Input can either be
     * - A json string
     * - A XML string
     * - A JSON Object
     *
     * TODO create test
     * @param data
     */
    IotizeConfigFactory.create = function (data) {
        if (typeof data === 'string') {
            data.trim();
            if (data.length === 0) {
                throw new Error('Cannot create configuration from empty data');
            }
            if (data[0] === '{') {
                return IotizeConfigFactory.fromJson(data);
            }
            else {
                return IotizeConfigFactory.fromXML(data);
            }
        }
        else {
            return new IotizeConfigModel(data);
        }
    };
    return IotizeConfigFactory;
}());

var __extends$2 = (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 __());
    };
})();
var IoTizeStudioError = /** @class */ (function (_super) {
    __extends$2(IoTizeStudioError, _super);
    function IoTizeStudioError() {
        return _super !== null && _super.apply(this, arguments) || this;
    }
    IoTizeStudioError.invalidKeyValue = function (key, msg) {
        return new IoTizeStudioError("Invalid IoTize studio value for configuration key \"" + key + "\". " + msg, IoTizeStudioError.Code.InvalidConfigKeyValue);
    };
    return IoTizeStudioError;
}(CodeError));
(function (IoTizeStudioError) {
    var Code;
    (function (Code) {
        Code["InvalidConfigKeyValue"] = "IoTizeStudioErrorInvalidConfigKeyValue";
    })(Code = IoTizeStudioError.Code || (IoTizeStudioError.Code = {}));
})(IoTizeStudioError || (IoTizeStudioError = {}));

var NetworkModeMapping = {
    P2P: 'PEER_TO_PEER',
    NETWORK: 'NETWORK',
    INFRA: 'NETWORK',
    INFRA_CLOUD: 'NETWORK' // TODO check
};
var LowPowerOptimizationLevelMapping = {
    0: 'NO',
    1: 'SHUTDOWN',
    2: 'STANDBY' // TODO CHECK
};
var WIFI_KEY_VISIBILITY_MAPPING = {
    0: 'VISIBLE',
    1: 'HIDDEN'
};
var S3P_CONFIG_MODE_MAPPING = {
    0: 'EMULATION',
    1: 'INDEXED'
};
var WIFI_SSID_VISIBILITY_MAPPING = {
    0: 'VISIBLE',
    1: 'HIDDEN',
    2: 'HIDDEN_FROM_SCAN'
};
var WIFI_PROTOCOL_MAPPING = {
    1: ['B'],
    3: ['B', 'G'],
    7: ['B', 'G', 'N']
};
// export const WIFI_TX_POWER_MAPPING: Record<string, number> {
// };
var TargetProtocolMapping = {
    "JTAG": 'JTAG',
    "MODBUS": 'MODBUS',
    "NONE": 'NONE',
    "S3P": 'S3P',
    "SERIAL": 'SERIAL',
    "SERIAL_STANDARD": 'SERIAL',
    "SERIAL_VIA_TAPNPASS": 'SERIAL',
    "CUSTOM": 'CUSTOM',
    "SWD": 'SWD',
    "GPIO": 'DIRECT_IO',
    "DIRECT_IO": 'DIRECT_IO',
    "DIRECTIO": 'DIRECT_IO'
};
var NFCParingModeMapping = {
    "NO": 'NO',
    "PAIRING": 'MANDATORY',
    'PAIRING+LOGIN': 'MANDATORY_FOR_LOGIN'
};
var NFC_CONNECTION_PRIORITY_MAPPING = {
    0: 'NFC_NON_PRIORITY',
    1: 'NFC_PRIORITY'
};
// const MAPPING: Record<WriteConfigKey, any> = {
//     // App
//     'application.name': 'IOTizeConfiguration.IOTizeApp.#.Name',
//     'application.universalLink': 'IOTizeConfiguration.IOTizeApp.#.WebLink',
//     'application.uri': 'IOTizeConfiguration.IOTizeApp.#.GUI_Path',
//     'application.data': 'TODO',
//     // CLOUD 
//     'cloud.dataLog.uploadPeriod': 'IOTizeConfiguration.IOTizeApp.CloudServices.#.CloudMqttPeriod',
//     'cloud.dataLog.mqttPublishTopic': 'IOTizeConfiguration.IOTizeApp.WIFI_INFO.#.Cloud_Publish_Topic',
//     'cloud.dataLog.mqttSubscribeTopic': 'IOTizeConfiguration.IOTizeApp.WIFI_INFO.#.Cloud_Subscribe_Topic',
//     'cloud.platform': '',
//     // Target
//     'target.protocol.type': 'IOTizeConfiguration.IOTizeApp.#.TargetProtocol',
//     // Tap
//     'tap.certificate.privateKey': 'IOTizeConfiguration.IOTizeApp.GUI_MQTT_Info.GUI_DevMQTT_PrivateKey',
//     'tap.certificate.publicKey': 'IOTizeConfiguration.IOTizeApp.GUI_MQTT_Info.GUI_DevMQTT_Certificate',
//     // JVM
//     'jvm.code': 'IOTizeConfiguration.IOTizeApp.#.JVM_Custom_Code',
//     'jvm.profileId': 'IOTizeConfiguration.IOTizeApp.#.',
//     // LORA
//     'wireless.protocols.lora.config': 'IOTizeConfiguration.IOTizeApp.LoRa_Info.#.LoRaMode',
//     'wireless.protocols.lora.ids': 'IOTizeConfiguration.IOTizeApp.LoRa_Info.#.TODO',
//     // MQTT
//     'wireless.protocols.mqtt.clientId': 'IOTizeConfiguration.IOTizeApp.WIFI_INFO.#.MQTTRelay_Client_ID',
//     'wireless.protocols.mqtt.password': 'IOTizeConfiguration.IOTizeApp.WIFI_INFO.#.MQTTRelay_Passwd',
//     'wireless.protocols.mqtt.username': 'IOTizeConfiguration.IOTizeApp.WIFI_INFO.#.MQTTRelay_Broker_Login',
//     'wireless.protocols.mqtt.brokerPort': 'IOTizeConfiguration.IOTizeApp.WIFI_INFO.#.MQTTRelay_Service',
//     'wireless.protocols.mqtt.brokerHostname': 'IOTizeConfiguration.IOTizeApp.WIFI_INFO.#.MQTTRelay_URL',
//     'wireless.protocols.mqtt.brokerCertificate': 'IOTizeConfiguration.IOTizeApp.WIFI_INFO.#.Broker_Certificate',
//     'wireless.protocols.mqtt.topicPrefix': 'IOTizeConfiguration.IOTizeApp.WIFI_INFO.#.TODO',
//     // NFC
//     'wireless.protocols.nfc.autoLogProfileId': 'IOTizeConfiguration.IOTizeApp.#.NFCLogin',
//     'wireless.protocols.nfc.connectionPriority': 'IOTizeConfiguration.IOTizeApp.#.NFCInterruptionLock',
//     'wireless.protocols.nfc.pairingMode': 'IOTizeConfiguration.IOTizeApp.#.NFCPairingMandatory',
//     // WIFI
//     'wireless.protocols.wifi.ssid': 'IOTizeConfiguration.IOTizeApp.WIFI_Info.ModuleSSID ||',
//     'wireless.protocols.wifi.ssidVisibility': 'IOTizeConfiguration.IOTizeApp.WIFI_Info.Wifi_SSID_Hidden',
//     'wireless.protocols.wifi.keyVisibility': 'IOTizeConfiguration.IOTizeApp.WIFI_Info.#.Wifi_WEPKey_Hidden',
//     'wireless.protocols.wifi.gatewayIp': 'IOTizeConfiguration.IOTizeApp.WIFI_Info.#.',
//     'wireless.protocols.wifi.mask': 'IOTizeConfiguration.IOTizeApp.WIFI_Info.#.',
//     'wireless.protocols.wifi.mode': 'IOTizeConfiguration.IOTizeApp.WIFI_Info.#.',
//     'wireless.protocols.wifi.protocol': 'IOTizeConfiguration.IOTizeApp.WIFI_Info.#.',
//     'wireless.protocols.wifi.region': 'IOTizeConfiguration.IOTizeApp.WIFI_Info.#.',
//     // Wireless options
//     'wireless.options.inactivityTimeout': 'IOTizeConfiguration.IOTizeApp.WIFI_Info.#.',
//     'wireless.options.authorizedProtocols': 'IOTizeConfiguration.IOTizeApp.WIFI_Info.#.',
//     'data.profiles[]': 'IOTizeConfiguration.IOTizeApp.IOTizeProfile',
//     'data.profiles[].username': '#.Name',
//     'data.profiles[].alias': '#.GroupId',
//     'data.profiles[].lifeTime': '#.LifeTime',
//     'data.profiles[].passwordKey': '#.Pwd',
//     'data.profiles[].password': false,
//     'data.bundles[]': 'IOTizeConfiguration.IOTizeApp.IOTizeBundle',
//     'data.bundles[].name': '#.Name',
//     'data.bundles[].dataLog.period': '#.DataLogPeriod',
//     'data.bundles[].acl[].$': '#.',
//     'data.variables[].address': '',
//     'data.variables[].bundleId': '',
//     'data.variables[].type': '',
//     'data.variables[].value': '',
// }

function isTruthy(v, falsyValue) {
    if (falsyValue === void 0) { falsyValue = ["No", "no", "false", "0"]; }
    if (v === undefined) {
        return false;
    }
    if (falsyValue.indexOf(v.toString()) > 0) {
        return false;
    }
    return !!v;
}
function convertToVariableType(accessor) {
    var unsigned = accessor.value('GUI_unsigned', 'boolean', false);
    var float = accessor.value('GUI_float', 'boolean', false);
    var unitDataType = accessor.value('UnitType', 'string', '');
    var size = accessor.value('Unit', 'number', 1);
    if (unitDataType) {
        if (unitDataType === 'boolean') {
            return VariableType.Data.BOOLEAN;
        }
        else if (unitDataType === 'ASCII') {
            return VariableType.Data.ASCII;
        }
        else {
            unsigned = unitDataType === 'unsigned integer';
            float = unitDataType === 'float';
        }
    }
    if (float) {
        return VariableType.Data.FLOAT32;
    }
    switch (size) {
        case 0:
            return unsigned ? VariableType.Data.UINT8 : VariableType.Data.INT8;
        case 1:
            return unsigned ? VariableType.Data.UINT8 : VariableType.Data.INT8;
        case 2:
            return unsigned ? VariableType.Data.UINT16 : VariableType.Data.INT16;
        case 3:
            return unsigned ? VariableType.Data.UINT32 : VariableType.Data.INT32;
        default:
            throw new Error("Unknown unit size: \"" + size + "\" (type=" + typeof size + ")");
    }
}
function fromMapping(data, attributeName, mapping, defaultValue) {
    var value = data.value(attributeName, 'string', defaultValue);
    if (!value) {
        return undefined;
    }
    value = value.replace(/ /g, '_').toUpperCase();
    if (!(value in mapping)) {
        throw new Error("Invalid value \"" + value + "\" for key \"" + attributeName + "\"");
    }
    return mapping[value];
}
/**
 * TODO add to common lib (toEnumFromValue)
 * @param mapping
 * @param value
 */
function stringToEnum(mapping, value) {
    var values = Object.keys(mapping).map(function (key) { return mapping[key]; });
    if (values.find(function (v) { return v.toString() === value; }) ===
        undefined) {
        throw new Error("Missing value \"" + value + "\" in enum. Available keys are " + values.join(', '));
    }
    if (value in mapping) {
        // For string to number convertion
        return mapping[mapping[value]];
    }
    return value;
}
function mapKey(object, key, value) {
    if (value) {
        object[key] = 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;
//         }
// }

function convertIoTizeStudioConfigToTapConfig(fileContent) {
    // console.log('File content', fileContent);
    var xmlConfig = parse(fileContent, {
        attributeNamePrefix: '',
        ignoreAttributes: false
    });
    var config = new IoTizeStudioToTapConfigConverter().convert(new DotAccessor(xmlConfig));
    return config;
}
function isHexString(input) {
    if (typeof input !== 'string') {
        return false;
    }
    return input.match(/^(0x)?([a-fA-F0-9])*$/);
}
function decodeInitialVariableValue(value) {
    var values = value.split(';')
        .reduce(function (prev, v) {
        if (v.length === 0) {
            return prev;
        }
        if (v.startsWith('0x')) {
            prev.push(v);
        }
        else {
            prev.push(parseFloat(v));
        }
        return prev;
    }, []);
    return values.length > 0 ?
        (values.length === 1 ? values[0] : values) : undefined;
}
/**
 * Convert an IoTize studio configuration file to TapConfiguratorConfig
 *
 * TODO it's a partial implementation, some keys need to be implemented.
 * Not exaustive list of not supported keys:
 *  - JVM code
 *  - Lora config
 *
 */
var IoTizeStudioToTapConfigConverter = /** @class */ (function () {
    function IoTizeStudioToTapConfigConverter(options, host) {
        if (options === void 0) { options = {
            strict: false
        }; }
        this.options = options;
        this.host = host;
        /**
         * Errors reported during convertion when strict mode is disabled
         */
        this.warnings = [];
        this.modbusSerialConfigConverter = new TapnpassProtocolConfigurationConverter();
    }
    IoTizeStudioToTapConfigConverter.prototype.convert = function (root) {
        var config = root.get('IOTizeConfiguration');
        var appConfigNode = config.get('IOTizeApp');
        var wifiConfigNode = appConfigNode.get('WIFI_Info', undefined);
        var bundles = config.list('IOTizeBundle', []);
        var profiles = config.list('IOTizeProfile', []);
        var versionString = config.value('Version', 'string', '1.0.0');
        var result = {
            version: versionString,
            application: this.readApplicationConfig(appConfigNode),
            data: {
                bundles: this.readBundlesConfig(bundles),
                profiles: this.readProfilesConfig(profiles)
            },
            cloud: this.readCloudConfig(appConfigNode),
            jvm: this.readJvmConfig(config),
            wireless: {
                options: this.readWirelessOptionsConfig(appConfigNode),
                protocols: {
                    wifi: this.readWifiConfig(wifiConfigNode),
                    mqtt: this.readMqttConfig(wifiConfigNode),
                    ble: this.readBleConfig(appConfigNode),
                    lora: this.readLoraConfig(appConfigNode),
                    nfc: this.readNfcConfig(appConfigNode)
                }
            },
            tap: this.readTapConfig(appConfigNode),
            target: this.readTargetConfig(appConfigNode),
        };
        if (this.warnings.length > 0) {
        }
        return result;
    };
    IoTizeStudioToTapConfigConverter.prototype.reportError = function (error, message) {
        if (message === void 0) { message = error.message; }
        if (this.options.strict) {
            throw error;
        }
        this.warnings.push({
            error: error,
            message: message
        });
    };
    IoTizeStudioToTapConfigConverter.prototype.readMqttConfig = function (appConfig) {
        var _a, _b, _c, _d, _e, _f, _g;
        if (!appConfig) {
            return undefined;
        }
        return {
            brokerHostname: (_a = appConfig.get('CloudHostName', undefined)) === null || _a === void 0 ? void 0 : _a.value('Name', 'string', undefined),
            brokerPort: (_b = appConfig.get('CloudServiceName', undefined)) === null || _b === void 0 ? void 0 : _b.value('Name', 'string', undefined),
            password: (_c = appConfig.get('CloudMQTT_UID', undefined)) === null || _c === void 0 ? void 0 : _c.value('Name', 'string', undefined),
            netKey: (_d = appConfig.get('GUI_DevNetkey', undefined)) === null || _d === void 0 ? void 0 : _d.value('Name', 'string', undefined),
            clientId: (_e = appConfig.get('GUI_DevMQTT_ClientID', undefined)) === null || _e === void 0 ? void 0 : _e.value('Name', 'string', undefined),
            brokerCertificate: this.readFileContentAsHexString((_f = appConfig.get('AWSRootCertificate', undefined)) === null || _f === void 0 ? void 0 : _f.value('Name', 'string', undefined)),
            relayNetKey: undefined,
            topicPrefix: undefined,
            username: (_g = appConfig.get('CloudMQTT_USERNAME', undefined)) === null || _g === void 0 ? void 0 : _g.value('Name', 'string', undefined)
        };
    };
    IoTizeStudioToTapConfigConverter.prototype.readLoraConfig = function (appConfigNode) {
        return {
            config: undefined,
            ids: undefined
        };
    };
    IoTizeStudioToTapConfigConverter.prototype.readBleConfig = function (appConfigNode) {
        return {
            pairing: appConfigNode.value('BLEPairingRequest', 'boolean', undefined)
        };
    };
    IoTizeStudioToTapConfigConverter.prototype.readNfcConfig = function (appConfigNode) {
        return {
            autoLogProfileId: appConfigNode.value('NFCLogin', 'number', undefined),
            connectionPriority: fromMapping(appConfigNode, 'NFCInterruptionLock', NFC_CONNECTION_PRIORITY_MAPPING, undefined),
            pairingMode: fromMapping(appConfigNode, 'NFCPairingMandatory', NFCParingModeMapping, undefined)
        };
    };
    IoTizeStudioToTapConfigConverter.prototype.readWifiConfig = function (wifiConfigNode) {
        if (!wifiConfigNode) {
            return undefined;
        }
        var mode = fromMapping(wifiConfigNode, 'Network', NetworkModeMapping, 'P2P');
        var isAP = mode === 'PEER_TO_PEER';
        return {
            mode: mode,
            ssid: wifiConfigNode.value(isAP ? 'Module_AP_SSID' : 'ModuleSSID', 'string', undefined),
            key: wifiConfigNode.value(isAP ? 'Module_AP_WEPKey' : 'ModuleWEPKey', 'string', undefined),
            mask: wifiConfigNode.value('ModuleIPMask', 'string', undefined),
            ipGateway: wifiConfigNode.value('ModuleIPGateway', 'string', undefined),
            ip: wifiConfigNode.value('ModuleIPAddress', 'string', undefined),
            keyVisibility: fromMapping(wifiConfigNode, 'Wifi_WEPKey_Hidden', WIFI_KEY_VISIBILITY_MAPPING, undefined),
            protocol: fromMapping(wifiConfigNode, 'Wifi_Protocol', WIFI_PROTOCOL_MAPPING, undefined),
            region: wifiConfigNode.value('region', 'string', undefined),
            ssidVisibility: fromMapping(wifiConfigNode, 'Wifi_SSID_Hidden', WIFI_SSID_VISIBILITY_MAPPING, undefined),
            txPower: wifiConfigNode.value('Wifi_TxLevel', 'number', undefined)
        };
    };
    IoTizeStudioToTapConfigConverter.prototype.toAuthorizedHostProtocols = function (value) {
        var _this = this;
        if (!value || value === 'ALL' || value === 'FFFF') {
            return undefined;
        }
        if (isHexString(value)) {
            var encodedValue = hexStringToBuffer(value);
            return converters.hostProtocolArray
                .decode(encodedValue)
                .map(function (p) {
                return HostProtocol[p];
            });
        }
        else {
            return value.split(',')
                .reduce(function (prev, value) {
                value = value.trim();
                try {
                    // TODO check it's a read HostProtocolConfig...
                    prev.push(value); //stringToEnum<HostProtocolConfig>(HostProtocolConfig, value));
                }
                catch (err) {
                    _this.reportError(err);
                }
                return prev;
            }, []);
        }
    };
    IoTizeStudioToTapConfigConverter.prototype.readWirelessOptionsConfig = function (appConfigNode) {
        var availableHostProtocols = undefined;
        var hostProtocolString = appConfigNode.value('HostProtocol', 'string', undefined);
        try {
            availableHostProtocols = this.toAuthorizedHostProtocols(hostProtocolString);
        }
        catch (err) {
            this.reportError(err, "Cannot convert HostProtocol value \"" + hostProtocolString + "\"");
        }
        return {
            inactivityTimeout: appConfigNode.value('LowPowerHostTimeout', 'number', 0),
            authorizedProtocols: availableHostProtocols && availableHostProtocols.length > 0 ? availableHostProtocols : undefined,
        };
    };
    IoTizeStudioToTapConfigConverter.prototype.readJvmConfig = function (config) {
        var appConfigNode = config.get('IOTizeApp');
        var envNode = config.get('GUI_Environment', undefined);
        var bcbFile = appConfigNode.value('JVM_Custom_Code', 'string', undefined);
        var useJvm = envNode === null || envNode === void 0 ? void 0 : envNode.value('UseInternalJVM', 'boolean', false);
        if (!useJvm) {
            return undefined;
        }
        return {
            code: bcbFile ? this.readFileContentAsHexString(bcbFile) : undefined,
        };
    };
    IoTizeStudioToTapConfigConverter.prototype.readFileContentAsHexString = function (path) {
        if (!path) {
            return undefined;
        }
        if (!this.host) {
            this.reportError(new Error("External file reference \"" + path + "\" cannot be resolved"));
        }
        if (this.host) {
            try {
                var fileContent = this.host.getFileContent(path);
                return '0x' + bufferToHexString(fileContent);
            }
            catch (err) {
                this.reportError(err);
            }
        }
        return undefined;
    };
    IoTizeStudioToTapConfigConverter.prototype.readCloudConfig = function (appConfigNode) {
        var cloudService = appConfigNode.get('CloudServices', undefined);
        // if (cloudService?.has('CloudMQTT_UID')) {
        //     let url = '';
        //     const cloudHostName = cloudService.get('CloudHostName', undefined);
        //     const cloudServiceName = cloudService.get('CloudServiceName', undefined);
        //     if (cloudHostName) {
        //         url = `mqtt://${cloudHostName.value('Name', 'string', '')}:${cloudServiceName?.value('Name', 'string', '') || ''}`;
        //     }
        //     // cloud = {
        //     //     // password: cloudService
        //     //     //     .get('CloudMQTT_UID', undefined)
        //     //     //     ?.value('Name', 'string', '') || '',
        //     //     uploadPeriod: ,
        //     //     url
        //     // };
        // }
        var wifiNode = appConfigNode.get('WIFI_Info', undefined);
        return {
            dataLog: {
                mqttPublishTopic: wifiNode === null || wifiNode === void 0 ? void 0 : wifiNode.value('Cloud_Publish_Topic', 'string', undefined),
                mqttSubscribeTopic: wifiNode === null || wifiNode === void 0 ? void 0 : wifiNode.value('Cloud_Subscribe_Topic', 'string', undefined),
                uploadPeriod: cloudService === null || cloudService === void 0 ? void 0 : cloudService.value('CloudMqttPeriod', 'number', 0),
            }
            // platform: '' // TODO
        };
    };
    IoTizeStudioToTapConfigConverter.prototype.readApplicationConfig = function (appConfigNode) {
        var appPath = appConfigNode.has('GUI_Path')
            ? appConfigNode.value('GUI_Path', 'string', '')
            : appConfigNode.value('Path', 'string', '');
        // const appPathInfo = this.appPathConverter.decode(appPath);
        return {
            name: appConfigNode.value('Name', 'string', 'Tap'),
            universalLink: appConfigNode.value('WebLink', 'string', ''),
            uri: appPath
        };
    };
    IoTizeStudioToTapConfigConverter.prototype.readTapConfig = function (appConfigNode) {
        var _a, _b;
        var lockSettingsEncoded = appConfigNode.value('InterfaceLock', 'number', 0);
        var lockOptions = new SecurityOptionsConverter()
            .decode(Uint8Array.from([0, lockSettingsEncoded])); // TODO pad instead
        return {
            security: lockOptions,
            powerOptimization: fromMapping(appConfigNode, 'LowPowerLevel', LowPowerOptimizationLevelMapping, undefined),
            scram: {
                hashIteration: appConfigNode.value('Scram_Hash_It', 'number', 5000)
            },
            certificate: {
                publicKey: this.readFileContentAsHexString((_a = appConfigNode.get('DeviceCertificate', undefined)) === null || _a === void 0 ? void 0 : _a.value('Name', 'string', undefined)),
                privateKey: this.readFileContentAsHexString((_b = appConfigNode.get('DevicePrivateKey', undefined)) === null || _b === void 0 ? void 0 : _b.value('Name', 'string', undefined))
            }
        };
    };
    IoTizeStudioToTapConfigConverter.prototype.readTargetConfig = function (appConfig) {
        var firmwareVersion = appConfig.value('Firmware', 'string', undefined);
        if (appConfig.value('GUI_TargetType', 'string', '0') === '1') {
            return {
                type: 'TAPNPASS',
                protocol: {
                    config: this.readTapnpassTargetProtocolConfig(appConfig)
                },
                firmwareVersion: firmwareVersion
            };
        }
        else {
            var protocol = this.readMCUTargetProtocol(appConfig);
            if (!protocol) {
                return undefined;
            }
            return {
                type: 'MCU',
                protocol: protocol,
                firmwareVersion: firmwareVersion
            };
        }
        // if (appConfig.has('USB_Aliases')) {
        //   data.type = "TAPNPASS";
        //   (data as TapNPassTargetConfig).usb = this._convertUsbData(appConfig);
        // }
    };
    IoTizeStudioToTapConfigConverter.prototype.readTargetProtocol = function (appConfig) {
        // const targetProtocolString = appConfig.value('TargetProtocol', 'string', 'SWD');
        return fromMapping(appConfig, 'TargetProtocol', TargetProtocolMapping, 'NONE');
    };
    IoTizeStudioToTapConfigConverter.prototype.readTapnpassTargetProtocol = function (appConfig) {
        var targetProtocol = this.readTargetProtocol(appConfig);
        switch (targetProtocol) {
            case 'SERIAL':
                return {
                    type: 'SERIAL',
                    config: this.readTapnpassTargetProtocolConfig(appConfig)
                };
            case 'MODBUS':
                return {
                    type: 'MODBUS',
                    config: this.readTapnpassTargetProtocolConfig(appConfig)
                };
            default:
                throw new Error("Illegal target protocol \"" + targetProtocol + "\" for TapNPass target type.");
        }
    };
    IoTizeStudioToTapConfigConverter.prototype.readTapnpassTargetProtocolConfig = function (appConfig) {
        var targetProtocolConfDataLen = appConfig.value('TargetProtocolConfDataLen', 'number', 0);
        if (targetProtocolConfDataLen === 0) {
            return {
                modbus: {
                    offsetFirstRegister: false,
                    slave: 0
                },
                serial: {
                    baudRate: 115200,
                    bitParity: "EVEN",
                    dataBits: "_8",
                    handshakeDelimiter: "NONE",
                    hardwareFlowControl: "NONE",
                    identification: false,
                    ledStatus: true,
                    physicalPort: "AUTO",
                    stopBits: "ONE",
                    timeout: 255
                },
                usb: {
                    aliases: []
                }
            };
        }
        var protocolData = hexStringToBuffer(appConfig.value('TargetProtocolConfData', 'string'));
        if (protocolData.length < 8) {
            throw IoTizeStudioError.invalidKeyValue('TargetProtocolConfData', 'Protocol configuration data should be at least 8 bytes long');
        }
        var ctr1 = protocolData.slice(0, 4);
        var ctr2 = protocolData.slice(4, 8);
        var remaining = protocolData.slice(8);
        //  KaitaiStreamWriter.mergeArrays(ctr2, ctr1).reverse(),
        protocolData = KaitaiStreamWriter.mergeArrays(ctr1.reverse(), ctr2.reverse(), remaining);
        var protocolConf = reversedTargetSerialModbusConfiguration.decode(protocolData);
        var serial = protocolConf.serial;
        // TODO usb settings
        return {
            modbus: protocolConf.modbus,
            serial: {
                baudRate: serial.baudRate,
                bitParity: this.toEnumKey(serial.bitParity, SerialSettings.BitParity),
                dataBits: this.toEnumKey(serial.dataBits, SerialSettings.DataBits),
                handshakeDelimiter: this.toEnumKey(serial.handshakeDelimiter, SerialSettings.HandshakeDelimiter),
                hardwareFlowControl: this.toEnumKey(serial.hardwareFlowControl, SerialSettings.HardwareFlowControl),
                physicalPort: this.toEnumKey(serial.physicalPort, SerialSettings.PhysicalPort),
                stopBits: this.toEnumKey(serial.stopBits, SerialSettings.StopBits),
                timeout: serial.timeout,
                identification: false,
                ledStatus: false
            },
            usb: {
                aliases: [
                // TODO 
                ]
            }
        };
    };
    IoTizeStudioToTapConfigConverter.prototype.readMCUTargetProtocol = function (appConfig) {
        var targetProtocol = this.readTargetProtocol(appConfig);
        switch (targetProtocol) {
            case 'SWD':
                return {
                    type: targetProtocol
                };
            case 'CUSTOM':
                return {
                    type: 'CUSTOM'
                    // config: '0xTODO'
                };
            case 'MODBUS':
                return {
                    type: 'MODBUS',
                    config: this.readModbusProtocolConfig(appConfig)
                };
            case 'S3P':
                return {
                    type: 'S3P',
                    config: this.readS3PProtocolConfig(appConfig)
                };
            case 'SERIAL':
                return {
                    type: 'SERIAL',
                    config: this.readSerialProtocolConfig(appConfig)
                };
            case 'NONE':
                return undefined;
            case 'DIRECT_IO':
                return {
                    type: 'DIRECT_IO'
                };
            case 'JTAG':
            default:
                throw new Error("Target protocol convertion from \"" + targetProtocol + "\" is not supported yet.");
        }
    };
    IoTizeStudioToTapConfigConverter.prototype.readSerialProtocolConfig = function (appConfig) {
        var config = this.readModbusProtocolConfig(appConfig);
        if (!config) {
            return undefined;
        }
        return config === null || config === void 0 ? void 0 : config.serial;
    };
    IoTizeStudioToTapConfigConverter.prototype.readS3PProtocolConfig = function (appConfig) {
        return {
            mode: fromMapping(appConfig, 'S3Pmode', S3P_CONFIG_MODE_MAPPING) || 'EMULATION',
            delay: appConfig.value('S3PDelay', 'number', 1)
        };
    };
    IoTizeStudioToTapConfigConverter.prototype.readModbusProtocolConfig = function (appConfig) {
        var config = this.readTapnpassTargetProtocolConfig(appConfig);
        var partialSerial = config.serial;
        delete partialSerial.identification;
        delete partialSerial.physicalPort;
        delete partialSerial.hardwareFlowControl;
        delete partialSerial.ledStatus;
        return {
            modbus: config.modbus,
            serial: config.serial
        };
    };
    IoTizeStudioToTapConfigConverter.prototype.toEnumKey = function (value, mapping
    // , defaultValue: (keyof MappingType)
    ) {
        // if (value === undefined) {
        //     return defaultValue;
        // }
        if (!(value in mapping)) {
            this.reportError(new Error("Cannot find corresponding key for value \"" + value + "\". Available keys: " + Object.keys(mapping).join(', ')));
        }
        return mapping[value];
    };
    // _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 = hexStringToBuffer(usbAliasesAcccessor.value(aliasName));
    //     aliases.push(this._convertUsbAlias(usbAliasRawData));
    //   }
    //   return {
    //     aliases: aliases
    //   };
    // }
    // private _convertUsbAlias(data: Uint8Array): UsbAlias {
    //   return this.usbAliasConverter.decode(data);
    // }
    IoTizeStudioToTapConfigConverter.prototype.readProfilesConfig = function (profiles) {
        var results = [];
        for (var i = 0; i < profiles.length; i++) {
            var xmlProfile = profiles[i];
            var name_1 = xmlProfile.value('Name');
            var profile = void 0;
            if (name_1 === ANONYMOUS_USER.username) {
                continue;
            }
            else if (isSpecialUser(name_1)) {
                var specialConfig = {
                    username: xmlProfile.value('Name', 'string'),
                    lifeTime: xmlProfile.value('LifeTime', 'number', 0),
                    key: this.readUserKey(xmlProfile)
                };
                profile = specialConfig;
            }
            else {
                var userConfig = {
                    username: xmlProfile.value('Name', 'string', ''),
                    id: xmlProfile.value('Id', 'number', 0),
                    lifeTime: xmlProfile.value('LifeTime', 'number', 0),
                    key: this.readUserKey(xmlProfile)
                };
                profile = userConfig;
            }
            var subProfilesConfig = xmlProfile.list('IOTizeUser', []);
            if (subProfilesConfig.length > 0) {
                var subProfiles = this.readUserProfile(subProfilesConfig);
                profile.users = subProfiles;
            }
            results.push(profile);
        }
        return results;
    };
    IoTizeStudioToTapConfigConverter.prototype.readUserKey = function (xmlProfile) {
        var scramPwd = xmlProfile.value('Scram_Pwd', 'string', undefined);
        if (scramPwd) {
            // let userSalt = xmlProfile.value('Scram_User_Salt', 'number');
            // specialConfig.salt = userSalt;
            return "0x" + scramPwd;
        }
        else {
            var key = xmlProfile.value('Pwd', 'string', undefined);
            if (key) {
                return "0x" + key;
            }
        }
        return undefined;
    };
    IoTizeStudioToTapConfigConverter.prototype.readUserProfile = function (subProfilesConfig) {
        var users = [];
        for (var i = 0; i < subProfilesConfig.length; i++) {
            var xmlUserConfig = subProfilesConfig[i];
            var user = {
                username: xmlUserConfig.value('Name', 'string', ''),
                id: xmlUserConfig.value('Id', 'number', 0),
                key: this.readUserKey(xmlUserConfig),
                lifeTime: 0 // There is no lifetime for users (only profile)
            };
            users.push(user);
        }
        return users;
    };
    IoTizeStudioToTapConfigConverter.prototype.readBundlesConfig = function (xmlBundles) {
        var result = [];
        for (var i = 0; i < xmlBundles.length; i++) {
            var xmlBundle = xmlBundles[i];
            var aclsXml = [];
            var aclContainer = xmlBundle.get('IOTizeAccessControl', undefined);
            if (aclContainer) {
                aclsXml = aclContainer.list('IOTizeACL', []);
            }
            var acls = {};
            for (var j = 0; j < aclsXml.length; j++) {
                var aclXml = aclsXml[j];
                var profileName = aclXml.value('ProfileName');
                if (profileName === ADMIN_USER.username) {
                    continue;
                }
                var rights = aclXml.value('Rights');
                acls[profileName] = {
                    read: rights.includes('read'),
                    write: rights.includes('write')
                };
            }
            var bundleConfig = {
                name: xmlBundle.value('GUI_Name'),
                id: parseInt(xmlBundle.value('Id')),
                variables: this.readVariablesConfig(xmlBundle.list('IOTizeVariable', [])),
                acls: acls,
                dataLog: {
                    period: xmlBundle.value('DataLogPeriod', 'number', 0)
                },
                features: this.readSpecialFeatures(xmlBundle.list('Feature', []))
            };
            result.push(bundleConfig);
        }
        return result;
    };
    IoTizeStudioToTapConfigConverter.prototype.readSpecialFeatures = function (featuresXml) {
        if (featuresXml.length > 0) {
            var result = [];
            for (var i = 0; i < featuresXml.length; i++) {
                var featureId = featuresXml[i].value('FeatureResId', 'number');
                if (featureId in SpecialFeature) {
                    var value = SpecialFeature[featureId]; // TODO check
                    result.push(value);
                }
                else {
                    throw new Error("Unexpected feature id: " + featureId);
                }
            }
            return result;
        }
        else {
            return undefined;
        }
    };
    IoTizeStudioToTapConfigConverter.prototype.readVariablesConfig = function (variables) {
        var result = [];
        for (var i = 0; i < variables.length; i++) {
            var xmlVariable = variables[i];
            var id = xmlVariable.value('Id', 'number');
            var length_1 = xmlVariable.value('Quantity', 'number', 1);
            var dataType = toEnumType(convertToVariableType(xmlVariable), VariableType$1.Data);
            var name_2 = xmlVariable.value('GUI_Name', 'string', '');
            var unit = xmlVariable.value('GUI_DispUnit', 'string', '') || undefined;
            var meta = void 0;
            if (name_2 || unit) {
                meta = {
                    name: name_2,
                    unit: unit
                };
            }
            var isInTapStatic = isTruthy(xmlVariable.value('IsInTap', 'string', '0'));
            var IsInTapVolatil = isTruthy(xmlVariable.value('IsInTapVolatil', 'string', '0'));
            if (IsInTapVolatil || isInTapStatic) {
                var initialValueString = xmlVariable.value('InTap_InitialValueString', 'string', '');
                var initialValue = decodeInitialVariableValue(initialValueString);
                var inTapVariable = {
                    domain: IsInTapVolatil ? 'IN_TAP_VOLATIL' : 'IN_TAP_STATIC',
                    id: id,
                    length: length_1,
                    dataType: dataType,
                    meta: meta,
                    value: initialValue
                };
                result.push(inTapVariable);
            }
            else {
                // if (isModbusTargetConfig){
                //     const modbusVariableConfig: ModbusVariableConfig = {
                //         domain: 'TARGET',
                //         modbusAddress: {
                //         },
                //         id,
                //         length,
                //         dataType,
                //         name,
                //         unit
                //     };
                //     result.push(variableConfig);
                // }
                // else {
                var variableConfig = {
                    domain: 'TARGET',
                    address: '0x' + xmlVariable.value('Address', 'number').toString(16),
                    id: id,
                    length: length_1,
                    dataType: dataType,
                    meta: meta
                };
                result.push(variableConfig);
                // }
            }
        }
        return result;
    };
    return IoTizeStudioToTapConfigConverter;
}());

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

export { BundleDoesNotExistError, ByteOrder16Bits, ByteOrder32Bits, InvalidConfigurationError, IoTizeStudioToTapConfigConverter, IotizeBundleModel, IotizeConfigFactory, IotizeConfigModel, IotizeProfileModel, IotizeVariableModel, VariableDoesNotExistError, convertIoTizeStudioConfigToTapConfig };
//# sourceMappingURL=iotize-tap-config-iotize-studio.js.map
