"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
var __importStar = (this && this.__importStar) || function (mod) {
    if (mod && mod.__esModule) return mod;
    var result = {};
    if (mod != null) for (var k in mod) if (Object.hasOwnProperty.call(mod, k)) result[k] = mod[k];
    result["default"] = mod;
    return result;
};
Object.defineProperty(exports, "__esModule", { value: true });
/// <reference path="../types/colorthief.ts"/>
const strings_1 = require("@angular-devkit/core/src/utils/strings");
const data_accessor_1 = require("@iotize/common/data-accessor");
const iotize_studio_1 = require("@iotize/device-client.js/config/iotize-studio");
const colorthief_1 = __importDefault(require("colorthief"));
const path_1 = require("path");
const icons_1 = require("../assets/icons");
const debug_1 = require("../debug");
const schemaTemplateReadOnly = __importStar(require("../schema.json"));
const meta_1 = require("./angular-utility/meta");
const definitions_1 = require("./definitions");
const generator_1 = require("./generator");
const config_adapter_1 = require("./iotize-studio/config-adapter");
const show_in_card_adapter_1 = __importDefault(require("./iotize-studio/extra-adapters/show-in-card-adapter"));
const tap_config_parser_1 = require("./tap-config-parser");
const util_1 = require("./util");
const schemaTemplate = Object.assign({}, schemaTemplateReadOnly);
function sanitizeFilename(input) {
    return input.replace(/[^a-zA-Z_\-]/g, '-');
}
function capitalize(s) {
    if (typeof s !== 'string' || s.length == 0)
        return '';
    return s.charAt(0).toUpperCase() + s.slice(1);
}
const defaultPageIcon = {
    "TapMonitoringPage": "analytics",
    "AboutPage": "help",
    "ProtocolConfigurationPage": "cog",
    "TapInfoPage": "help"
};
var MonitoringPageMode;
(function (MonitoringPageMode) {
    MonitoringPageMode["OnePagePerBundle"] = "One page per bundle";
    MonitoringPageMode["OnePagePerProfile"] = "One page per profile";
})(MonitoringPageMode || (MonitoringPageMode = {}));
function createDefinitionsSchema(tapConfigSchema) {
    const icons = Object.keys(icons_1.ICONS);
    const data = tapConfigSchema.config.data || {};
    const bundles = data.bundles || [];
    const profiles = data.profiles || [];
    schemaTemplate.definitions.icon.enum = icons;
    schemaTemplate.definitions.TapProfileName.enum = profiles.map(profile => profile.username);
    const variablesList = bundles.reduce((acc, curr) => {
        curr.variables.forEach((variable) => {
            acc.push(variable.name || (variable.id).toString());
        });
        return acc;
    }, []);
    schemaTemplate.definitions['variable-list'].enum = variablesList;
    return schemaTemplate.definitions;
}
exports.createDefinitionsSchema = createDefinitionsSchema;
function createSchema(tapConfigSchema) {
    schemaTemplate.definitions = createDefinitionsSchema(tapConfigSchema);
    return schemaTemplate;
}
exports.createSchema = createSchema;
function createDefaultConfigGenerator(tapConfigSchema, iotizeStudioConfigXml) {
    let profiles = [];
    if (tapConfigSchema.config && tapConfigSchema.config.data) {
        profiles = (tapConfigSchema.config.data.profiles || []).map((profile) => profile.username);
        if (!profiles.find(p => p === "anonymous")) {
            profiles.push('anonymous');
        }
    }
    const generator = ConfigGenerator.fromTapConfig(tapConfigSchema, iotizeStudioConfigXml);
    const tapDevicePage = generator.tapDevicePage();
    generator.about();
    generator.connectionPage();
    generator.notFoundPage();
    if (profiles.length > 0) {
        const monitoringPage = generator.monitoringPage(undefined, profiles);
        tapDevicePage.defaultPage = monitoringPage.path;
    }
    generator.infoPage();
    generator.protocolConfigurationPage();
    generator.notFoundPage();
    generator.tapLoginPage();
    generator.createDefaultMenu();
    return generator;
}
exports.createDefaultConfigGenerator = createDefaultConfigGenerator;
class ConfigGenerator {
    constructor(schema, tapConfig, iotizeStudioConfigXml, _logger = { warn: console.warn, info: console.log, debug: debug_1.debug }) {
        this.schema = schema;
        this.tapConfig = tapConfig;
        this.iotizeStudioConfigXml = iotizeStudioConfigXml;
        this._logger = _logger;
        // TODO validate with ajv
        // console.info('ConfigGenerator', 'new instance SCHEMA', schema);
        if (typeof schema === "string") {
            throw new Error(`Invalid schema. Given a string but expecting an object`);
        }
        if (!this.schema.app) {
            throw new Error(`Invalid schema error. It does not contain property "app"`);
        }
        if (!this.schema.app.menu) {
            this.schema.app.menu = [];
        }
        if (!this.schema.app.pages) {
            this.schema.app.pages = [];
        }
        if (!this.iotizeStudioConfigXml) {
            console.warn('ConfigGenerator created without iotize studio config');
            // throw new Error('Cannot find iotize studio config xml');
        }
    }
    get androidOptions() {
        if (!this.schema.app.platforms) {
            this.schema.app.platforms = {};
        }
        if (!this.schema.app.platforms.android) {
            this.schema.app.platforms.android = {};
        }
        return this.schema.app.platforms.android;
    }
    ;
    get iotizeStudioConfig() {
        if (!this._iotizeStudioConfig) {
            if (!this.iotizeStudioConfigXml) {
                return undefined;
            }
            this._iotizeStudioConfig = iotize_studio_1.IotizeConfigFactory.fromXML(this.iotizeStudioConfigXml);
        }
        return this._iotizeStudioConfig;
    }
    get iotizeStudioConfigDotAccessor() {
        if (!this._iotizeStudioConfigDotAccessor) {
            if (!this.iotizeStudioConfigXml) {
                return undefined;
            }
            this._iotizeStudioConfigDotAccessor = new data_accessor_1.DotAccessor(tap_config_parser_1.iotizeStudioConfigToJson(this.iotizeStudioConfigXml));
        }
        return this._iotizeStudioConfigDotAccessor.get("IOTizeConfiguration", undefined);
    }
    get appTheme() {
        if (!this.schema.app.theme) {
            this.schema.app.theme = {
                colors: []
            };
        }
        return new AppThemeGenerator(this.schema.app.theme);
    }
    get pages() {
        return this.schema.app.pages;
    }
    get rootPageList() {
        return Object.values(this.schema.app.pages);
    }
    get pageList() {
        const routePages = this.rootPageList;
        const pages = [];
        for (const page of routePages) {
            this._pageList(page, pages);
        }
        return pages;
    }
    get logger() {
        return this._logger;
    }
    static fromTapConfig(tapConfig, iotizeStudioConfigXML) {
        var _a, _b, _c, _d;
        const protocols = [
            definitions_1.AppProtocol.NFC
        ];
        if (tapConfig.config && tapConfig.config.tap && tapConfig.config.tap.availableHostProtocols) {
            const availableHostProtocols = tapConfig.config.tap.availableHostProtocols;
            if (Array.isArray(availableHostProtocols)) {
                availableHostProtocols.forEach(element => {
                    switch (element) {
                        case "BLE":
                            protocols.push(definitions_1.AppProtocol.BLE);
                            break;
                        case "NFC":
                            break;
                        case "LORA":
                            break;
                        case "WIFI":
                            protocols.push(definitions_1.AppProtocol.SOCKET);
                            protocols.push(definitions_1.AppProtocol.WIFI);
                            // protocols.push(AppProtocol.WEBSOCKET);
                            break;
                    }
                });
            }
            else if (availableHostProtocols === 'ALL') {
                protocols.push(definitions_1.AppProtocol.BLE);
                protocols.push(definitions_1.AppProtocol.SOCKET);
                protocols.push(definitions_1.AppProtocol.WIFI);
            }
        }
        const appName = ((_a = tapConfig.config.application) === null || _a === void 0 ? void 0 : _a.name) || 'TAP';
        let appId;
        if ((_c = (_b = tapConfig.config.application) === null || _b === void 0 ? void 0 : _b.android) === null || _c === void 0 ? void 0 : _c.aar) {
            appId = tapConfig.config.application.android.aar;
        }
        else {
            appId = `com.iotize.generatedapp.${appName.toLowerCase()}`;
        }
        appId = appId.replace(/[^a-zA-Z\.]/g, '').toLocaleLowerCase();
        const domain = ((_d = tapConfig.config.application) === null || _d === void 0 ? void 0 : _d.universalLink) || `www.iotize.com`;
        const appVersion = tapConfig.config.version.toString() || '0.0.1';
        const appConfigSchema = {
            version: {
                generator: meta_1.meta.version()
            },
            app: {
                id: appId,
                version: appVersion,
                domain: domain,
                name: appName,
                platforms: {
                    android: {
                        enabled: true
                    },
                    browser: {
                        enabled: true,
                        PWA: true
                    },
                    ios: {
                        enabled: true
                    }
                },
                menu: [],
                pages: {},
                defaultPage: undefined
            }
        };
        const configGenerator = new ConfigGenerator(appConfigSchema, tapConfig, iotizeStudioConfigXML, undefined);
        return configGenerator;
    }
    build() {
        if (this.schema.app.pages.TapConnectionPage && !this.schema.app.pages.TapDevicePage) {
            throw new Error(`Invalid schema: cannot have a connection page wihtout a tap device page`);
        }
        return this.schema;
    }
    setAppId(id) {
        // this.schema.app.domain = id;
        this.schema.app.id = id;
        return this;
    }
    addFastlaneIntegration(fastlaneIntegartion) {
        this.addIntegration(fastlaneIntegartion);
        return fastlaneIntegartion;
    }
    addFirebaseIntegration(firebaseIntegration) {
        this.addIntegration(firebaseIntegration);
        return firebaseIntegration;
    }
    addIntegration(integration) {
        if (!this.schema.app.integration) {
            this.schema.app.integration = [];
        }
        const existingIntegrationIndex = this.schema.app.integration.findIndex(info => info.type === integration.type);
        if (existingIntegrationIndex >= 0) {
            this.schema.app.integration.splice(existingIntegrationIndex, 1);
        }
        this.schema.app.integration.push(integration);
        return integration;
    }
    createDefaultMenu() {
        const pages = this.pageList;
        for (const page of pages) {
            this.addDefaultPageMenu(page);
        }
    }
    addDefaultPageMenu(page) {
        let menu;
        switch (page.type) {
            case 'MonitoringPage':
                menu = this.addMenuForSubPage(page, 'analytics', "TapDevicePage");
                break;
            case 'TapInfoPage':
                menu = this.addMenuForSubPage(page, 'information-circle', "TapDevicePage");
                break;
            case 'ProtocolConfigurationPage':
                menu = this.addMenuForSubPage(page, 'barcode', "TapDevicePage");
                break;
            case 'TapConfigPage':
                menu = this.addMenuForSubPage(page, 'construct', "TapDevicePage");
                break;
            case 'AboutPage':
                menu = this.addMenuForPage(page, 'help');
                break;
        }
        return menu;
    }
    _pageList(page, extraPages) {
        extraPages.push(page);
        if (page.children) {
            const subPageArray = Array.isArray(page.children) ? page.children : Object.values(page.children);
            subPageArray.forEach((subPage) => this._pageList(subPage, extraPages));
        }
    }
    notFoundPage(relativePath) {
        return this.getOrCreateAppPage({
            type: 'NotFoundPage',
            enabled: true,
            pageTitle: 'Not found',
            path: '**',
            projectPath: relativePath || 'pages/not-found'
        });
    }
    connectionPage(relativePath, override = {}) {
        const defaultOptions = {
            type: 'TapConnectionPage',
            enabled: true,
            pageTitle: 'Connection',
            path: 'connect',
            projectPath: relativePath || 'pages/tap-connect',
            protocols: {
                ble: {
                    enabled: true,
                },
                nfc: {
                    enabled: true
                },
                socket: {
                    enabled: true,
                    enableNetworkScan: true,
                    enableSoket: true,
                    enableWebsocket: true,
                    enableWifiAccessPointScan: true
                }
            }
        };
        return this.getOrCreateAppPage(Object.assign(Object.assign({}, defaultOptions), override));
    }
    infoPage(projectPath, override) {
        const infoPage = {
            type: "TapInfoPage",
            enabled: true,
            pageTitle: 'Device information',
            path: 'info',
            projectPath: projectPath,
            // loadChildren: `./${relativePath}/${sanitizeFilename(name)}.module#${classify(name)}Module`,
            items: [
                {
                    key: "appName"
                },
                {
                    key: "serialNumber"
                },
                {
                    key: "tapFirmwareVersion"
                },
                {
                    key: "configVersion"
                },
                {
                    key: "TargetProtocol"
                }
            ]
        };
        const parentPage = "TapDevicePage";
        if (!this.hasPageType(parentPage)) {
            this.tapDevicePage();
        }
        return this.getOrCreateSubPage(Object.assign(Object.assign({}, infoPage), override), parentPage, "tap-info-page");
    }
    // public refreshMenu() {
    //     for (let page of this.pageList) {
    //         this.addMenuForPage(page);
    //     }
    // }
    /**
     * Create a dashboard configuration according to Tap Schema.
     */
    addDashboard(profile, path, projectPath, moduleName) {
        var _a;
        const profileName = typeof profile === "string" ? profile : profile.name();
        const profileModel = typeof profile === "string" ? undefined : profile;
        this.logger.info(`Creating dashboard from config for profile "${profileName}"`);
        const iotizeStudioConfig = this.iotizeStudioConfig;
        const tapConfig = this.tapConfig;
        let columnNumber = 3;
        let monitoringPageMode = MonitoringPageMode.OnePagePerBundle;
        const xappConfig = (_a = this.iotizeStudioConfigDotAccessor) === null || _a === void 0 ? void 0 : _a.get("GUI_XAppConfig", undefined);
        if (xappConfig) {
            const layoutOptions = xappConfig.get("GUI_layout", undefined);
            if (layoutOptions) {
                columnNumber = layoutOptions.value("col", "number", columnNumber);
                monitoringPageMode = layoutOptions.value("mode", "string", monitoringPageMode);
            }
        }
        if (monitoringPageMode !== MonitoringPageMode.OnePagePerBundle && monitoringPageMode !== MonitoringPageMode.OnePagePerProfile) {
            monitoringPageMode = MonitoringPageMode.OnePagePerBundle;
        }
        if (columnNumber < 1) {
            columnNumber = 1;
        }
        let rootLayout;
        let pageTitle;
        if (profileModel && profileModel.data.$["GUI_AppPageTitle"]) {
            pageTitle = profileModel.data.$["GUI_AppPageTitle"];
        }
        else {
            pageTitle = profileName === 'anonymous'
                ? 'Public dashboard'
                : `${capitalize(profileName)} dashboard`;
        }
        let pageRoot;
        if (monitoringPageMode === MonitoringPageMode.OnePagePerProfile) {
            pageRoot = {
                type: "MonitoringDashboardPage",
                pageTitle: pageTitle,
                enabled: true,
                path: path,
                projectPath: projectPath,
                profileName: profileName,
                moduleName: moduleName,
                children: []
            };
        }
        else if (monitoringPageMode === MonitoringPageMode.OnePagePerBundle) {
            pageRoot = {
                type: "MonitoringDashboardPage",
                pageTitle: pageTitle,
                enabled: true,
                path: path,
                projectPath: projectPath,
                profileName: profileName,
                moduleName: moduleName,
                children: [],
                content: {
                    type: "TabLayout",
                    tabs: []
                }
            };
        }
        else {
            throw new Error(`Illegal monitoring page mode: ${monitoringPageMode}`);
        }
        const bundles = tapConfig.config.data.bundles || [];
        let currentPage = pageRoot;
        for (const bundle of bundles) {
            const variables = bundle.variables || [];
            if (monitoringPageMode === MonitoringPageMode.OnePagePerBundle) {
                const bundleName = bundle.name || `bundle ${bundle.id}`;
                const sanitizedBundleName = strings_1.dasherize(bundleName);
                currentPage = {
                    type: "MonitoringDashboardPage",
                    pageTitle: util_1.userfriendlystring(bundleName),
                    enabled: true,
                    path: sanitizedBundleName,
                    projectPath: `${projectPath}/${sanitizedBundleName}`,
                    profileName: profileName,
                    moduleName: strings_1.classify(`${profileName}${bundleName}Module`),
                    header: false,
                    footer: false
                };
                rootLayout = undefined;
                pageRoot.content.tabs.push({
                    label: util_1.userfriendlystring(bundleName),
                    icon: undefined,
                    path: sanitizedBundleName
                });
                pageRoot.children.push(currentPage);
                if (!pageRoot.defaultPage) {
                    pageRoot.defaultPage = currentPage.path;
                }
            }
            if (rootLayout === undefined) {
                if (columnNumber > 1) {
                    rootLayout = {
                        type: 'GridLayout',
                        cols: columnNumber,
                        elements: []
                    };
                }
                else {
                    rootLayout = {
                        type: 'ListLayout',
                        elements: []
                    };
                }
            }
            currentPage.content = rootLayout;
            if (generator_1.hasReadOrWriteRights(bundle, profileName)) {
                for (const variable of variables) {
                    const component = this._createVariableMonitorComponent(iotizeStudioConfig, bundle, variable, profileName);
                    if (component) {
                        // if (rootLayout.type === "ListLayout"){
                        //     let itemLayout: ItemLayout = {
                        //         type: 'ItemLayout',
                        //         elements: [
                        //             component
                        //         ]
                        //     }
                        //     component = itemLayout;
                        // }
                        rootLayout.elements.push(component);
                    }
                }
            }
        }
        return pageRoot;
    }
    _createVariableMonitorComponent(iotizeStudioConfig, bundle, variable, profileName) {
        var _a;
        const elementsTypeToAddLabelWhenNotInCard = [
            "VariableText",
            "VariableButtons",
            "VariableToggle",
            "VariableRange",
            "VariableSelect"
        ];
        this.logger.debug(`Rendering variable ${variable.id.toString()} (iotize studio config ? ${Boolean(iotizeStudioConfig)})`);
        const iotizeStudioVariableConfig = iotizeStudioConfig ? iotizeStudioConfig.variable(variable.id.toString()) : undefined;
        if (iotizeStudioVariableConfig) {
            if (iotizeStudioVariableConfig.attribute('GUI_Display') === 'No') {
                this.logger.info(`Skip variable in dashboard: "${variable.name}"`);
                return undefined;
            }
        }
        const cardTitle = iotizeStudioVariableConfig ? (iotizeStudioVariableConfig.alias() || iotizeStudioVariableConfig.name()) : variable.name;
        const isVariableEditable = generator_1.isEditable(bundle, variable, profileName, iotizeStudioVariableConfig);
        let cardContainer;
        // console.log('iotizeStudioVariableConfig.data', iotizeStudioVariableConfig!.data);
        if (iotizeStudioVariableConfig && iotizeStudioVariableConfig.data.XAppRender && iotizeStudioVariableConfig.data.XAppRender.length > 0) {
            const xappRender = iotizeStudioVariableConfig.data.XAppRender[0];
            // console.log('xappRender', xappRender);
            this.logger.debug(`XAppRender found for variable ${variable.id.toString()}`);
            const renderOptions = Object.assign(Object.assign({}, xappRender), xappRender.$);
            const variableTitle = iotizeStudioVariableConfig.alias() || iotizeStudioVariableConfig.name() || `Variable ${iotizeStudioVariableConfig.id()}`;
            // console.info('RENDER OPTIONS', renderOptions);
            const accessor = new data_accessor_1.DotAccessor(renderOptions);
            const input = {
                accessor,
                variable,
                variableConfig: iotizeStudioVariableConfig,
                extraOptions: {
                    editable: isVariableEditable,
                    title: variableTitle
                }
            };
            // TODO move from here
            // It should be in iotize-studio directory
            if (xappRender.$['GUI_showInCard'] === "Yes") {
                cardContainer = show_in_card_adapter_1.default(input);
            }
            input.extraOptions.showInCard = cardContainer !== undefined;
            let component = config_adapter_1.IoTizeStudioConfigAdapter.renderOptionsToComponent(input);
            if (component) {
                if (cardContainer !== undefined) {
                    (_a = cardContainer.content) === null || _a === void 0 ? void 0 : _a.push(component);
                    component = cardContainer;
                }
                else if (elementsTypeToAddLabelWhenNotInCard.indexOf(component.type) >= 0) {
                    component = {
                        type: "KeyValueLayout",
                        key: {
                            content: [
                                {
                                    type: "Text",
                                    content: `<ion-item lines="none" class="ion-no-padding ion-text-center"><ion-label class="ion-text-wrap">${variableTitle}</ion-label></ion-item>`
                                }
                            ]
                        },
                        value: {
                            content: [
                                component
                            ]
                        }
                    };
                }
            }
            return component;
        }
        else {
            const component = {
                type: 'CardLayout',
                description: undefined,
                icon: undefined,
                title: cardTitle,
                content: []
            };
            // let cardVariable: TapVariableCardComponent = {
            //     type: 'VariableCard',
            //     editable: isVariableEditable,
            //     unit: variable.unit,
            //     variable: `${variable.name}`
            // };
            const variableLength = variable.length || 1;
            if (variableLength > 1) {
                const layoutItemList = {
                    type: 'ListLayout',
                    items: []
                };
                component.content.push(layoutItemList); // TODO fix
                for (let index = 0; index < variableLength; index++) {
                    const textVariable = {
                        type: 'VariableText',
                        editable: isVariableEditable,
                        variableIndex: index,
                        variable: `${variable.name}`
                    };
                    layoutItemList.items.push({
                        title: index.toString(),
                        value: textVariable
                    });
                }
            }
            else {
                component.content.push({
                    type: 'VariableText',
                    variable: `${variable.name}`,
                    editable: isVariableEditable,
                    unit: variable.unit
                });
            }
            return component;
        }
    }
    tapConnectivitySettings(relativePath, override) {
        const defaultOptions = {
            type: "TapConnectivitySettingsPage",
            enabled: true,
            path: 'connectivity',
            pageTitle: 'Connectivity settings',
            projectPath: relativePath,
            authorizedProfiles: [
                "admin",
                "supervisor"
            ]
        };
        return this.getOrCreateSubPage(Object.assign(Object.assign({}, defaultOptions), override), "TapDevicePage", "tap-connectivity-settings-page");
    }
    about(relativePath, override) {
        const defaultOptions = {
            type: "AboutPage",
            enabled: true,
            projectPath: relativePath || 'pages/about',
            items: [
                {
                    title: 'Developer',
                    value: 'IoTize SAS'
                },
                {
                    title: 'About us',
                    value: 'https://iotize.com'
                },
                {
                    title: 'Version',
                    value: '{{ environment.version }}'
                }
            ],
            pageTitle: 'About',
            path: 'about',
        };
        const newPage = Object.assign(Object.assign({}, defaultOptions), override);
        return this.getOrCreateAppPage(newPage);
    }
    monitoringPage(projectPath, dashboardProfiles = []) {
        const DEFAULT_MONITORING_PAGE = {
            type: 'MonitoringPage',
            enabled: true,
            pageTitle: 'Monitoring',
            projectPath: projectPath,
            path: 'monitoring',
        };
        const parentPage = "TapDevicePage";
        if (!this.hasPageType(parentPage)) {
            this.tapDevicePage();
        }
        const monitoringPage = this.getOrCreateSubPage(DEFAULT_MONITORING_PAGE, parentPage, 'monitoring-page');
        dashboardProfiles.forEach((profile) => {
            this.getOrCreateDashboard(profile, {
                reconfigure: false
            });
        });
        return monitoringPage;
    }
    tapDevicePage(projectPath) {
        const devicePage = this.getOrCreateAppPage({
            enabled: true,
            children: {},
            pageTitle: 'Device',
            path: 'device',
            projectPath: projectPath || 'pages/tap-device-page',
            type: 'TapDevicePage'
        });
        return devicePage;
    }
    tapConfigPage(projectPath, override) {
        const parentPage = "TapDevicePage";
        if (!this.hasPageType(parentPage)) {
            this.tapDevicePage();
        }
        const defaultPageParameters = {
            enabled: true,
            type: 'TapConfigPage',
            path: 'settings',
            pageTitle: 'Settings',
            projectPath: projectPath,
            authorizedProfiles: [
                "admin",
                "supervisor"
            ]
        };
        return this.getOrCreateSubPage(Object.assign(Object.assign({}, defaultPageParameters), override), parentPage, "tap-settings-page");
    }
    protocolConfigurationPage(projectPath, override) {
        const targetProtocolConfig = this.tapConfig.config.target ? this.tapConfig.config.target.protocol : undefined;
        const protocolConfigurationList = [
            "MODBUS",
            "SERIAL_STANDARD",
            "SERIAL_VIA_TAPNPASS"
        ];
        const enabled = targetProtocolConfig ? protocolConfigurationList.indexOf(targetProtocolConfig) >= 0 : false;
        const protocolConfigurationPage = {
            enabled: enabled,
            type: 'ProtocolConfigurationPage',
            path: 'protocol-configuration',
            pageTitle: 'Protocol configuration',
            projectPath: projectPath,
            serialSettings: true,
            modbusSettings: targetProtocolConfig === "MODBUS" || targetProtocolConfig === "SERIAL_VIA_TAPNPASS",
            tapnpassSettings: targetProtocolConfig === "SERIAL_VIA_TAPNPASS"
        };
        const parentPage = "TapDevicePage";
        if (!this.hasPageType(parentPage)) {
            this.tapDevicePage();
        }
        return this.getOrCreateSubPage(Object.assign(Object.assign({}, protocolConfigurationPage), override), parentPage, "protocol-configuration");
    }
    tapLoginPage(projectPath) {
        return this.getOrCreateSubPage({
            enabled: true,
            children: {},
            pageTitle: 'Tap login',
            path: 'login',
            projectPath: projectPath,
            type: 'TapLoginPage'
        }, "TapDevicePage", "tap-login-page");
    }
    getOrCreateDashboard(profile, _options) {
        const monitoringPage = this.getPageByType("MonitoringPage");
        if (!monitoringPage) {
            throw new Error(`Cannot create dashboard page. Monitoring page is not enabled. Available pages: ${this.pageList.map(page => page.type).join(',')}`);
        }
        const profileName = typeof profile === "string" ? profile : profile.name();
        const dasherizeProfileName = strings_1.dasherize(profileName);
        const relativePath = _options.name || `${monitoringPage.projectPath}/${dasherizeProfileName}-dashboard`;
        const moduleName = strings_1.classify(`${dasherizeProfileName}-dashboard-module`);
        if (!monitoringPage.children) {
            monitoringPage.children = [];
        }
        const defaultDashboardPath = `${dasherizeProfileName}`;
        const existingingDashboardIndex = monitoringPage.children.findIndex(item => item.path === defaultDashboardPath);
        let dashboard = undefined;
        if (existingingDashboardIndex >= 0) {
            if (_options.reconfigure) {
                const existingDashboard = monitoringPage.children[existingingDashboardIndex];
                this.logger.warn(`Deleting dashboard page "${existingDashboard.pageTitle}" (path="${existingDashboard.path}") at index ${existingingDashboardIndex}`);
                monitoringPage.children.splice(existingingDashboardIndex, 1);
                dashboard = this.addDashboard(profile, defaultDashboardPath, relativePath, moduleName);
                monitoringPage.children.push(dashboard);
            }
            else {
                this.logger.info(`Using existing dashboard configuration`);
                dashboard = monitoringPage.children[existingingDashboardIndex];
            }
        }
        if (!dashboard) {
            dashboard = this.addDashboard(profile, defaultDashboardPath, relativePath, moduleName);
            monitoringPage.children.push(dashboard);
        }
        return dashboard;
    }
    addMenu(item) {
        if (!item.url.startsWith('/')) {
            item.url = `/${item.url}`;
        }
        if (!this.schema.app.menu.find(m => m.url === item.url)) {
            this.logger.info(`Add menu for page: ${item.title} (url: ${item.url})`);
            this.schema.app.menu.push(item);
        }
        else {
            this.logger.info(`Menu item ${item.title} (url: ${item.url}) already exists`);
        }
    }
    addMenuForPage(page, icon) {
        if (page.enabled) {
            this.logger.info(`Add menu for page: ${page.type}`);
            const menuItem = {
                title: page.pageTitle,
                icon: icon ? icon : defaultPageIcon[page.type],
                url: page.path
            };
            this.addMenu(menuItem);
            return menuItem;
        }
        else {
            // this.logger.warn(`Page ${page.pageTitle} (url: ${page.path}) disabled. Skip from menu`);
        }
    }
    addMenuForSubPage(page, icon, parentPageString) {
        if (page.enabled) {
            this.logger.info(`Add menu for page: ${page.type}`);
            const parentPage = this.getPageByType(parentPageString);
            if (!parentPage) {
                throw new Error(`Cannot add menu for sub page ${page.pageTitle} as parent page ${parentPageString} does not exist yet`);
            }
            const menuItem = {
                title: page.pageTitle,
                icon: icon,
                url: `${parentPage.path}/${page.path}`
            };
            this.addMenu(menuItem);
            return menuItem;
        }
        else {
            // this.logger.warn(`Page ${page.pageTitle} (url: ${page.path}) disabled. Skip from menu`);
        }
    }
    getPageByPath(path, pages = this.rootPageList) {
        // path = !Array.isArray(path) ? path.split('/') : path;
        // this._logger.info(`Search page "${path}"  in pages ${pages.map(p => `${p.type}:${p.path}`).join(', ')} `)
        if (path.startsWith('/')) {
            path = path.substring(1);
        }
        const page = pages.find((page) => {
            return path.startsWith(page.path);
        });
        if (!page) {
            this._logger.debug(`Page NOT_FOUND "${path}"`);
            return undefined;
        }
        const subPath = path.substr(page.path.length);
        if (subPath.length === 0) {
            // this._logger.info(`Page FOUND "${path}"  ${page.type}:${page.path} `)
            return page;
        }
        else {
            return this.getPageByPath(subPath, Object.values(page.children) || []);
        }
    }
    getOrCreatePageByPath(path, defaultValue) {
        if (!this.hasPageByPath(path)) {
            this.addPage(defaultValue);
        }
        return this.getPageByPath(path);
    }
    getPageByType(type) {
        return this.pageList.find(page => page.type === type);
    }
    hasPageType(type) {
        return Boolean(this.rootPageList.find(page => page.type === type));
    }
    addPage(page) {
        this.logger.info(`Creating page ${page.path} (type=${page.type})`);
        if (!page.type) {
            throw new Error(`Cannot add page without a type attribute`);
        }
        this.schema.app.pages[page.type] = page;
        return this;
    }
    getDefaultPage() {
        let defaultPage = this.schema.app.defaultPage;
        if (!defaultPage) {
            if (this.schema.app.menu.length === 0) {
                defaultPage = '';
            }
            else {
                defaultPage = this.schema.app.menu[0].url;
            }
        }
        return defaultPage;
    }
    /**
     * TODO
     * @param pageName
     */
    hasPageByPath(path) {
        return this.getPageByPath(path) !== undefined; // TODO check undefined
    }
    getOrCreateAppPage(appPage) {
        // this.logger.info(`Search page ${appPage.path} (type=${appPage.type})`);
        return this.getOrCreatePageByPath(appPage.path, appPage);
    }
    getOrCreateSubPage(appPage, parentPageOrString, defaultSubProjectPath = strings_1.dasherize(appPage.type || ''), subPageKey = "children") {
        let parentPage;
        if (typeof parentPageOrString === "string") {
            parentPage = this.pages[parentPageOrString];
            if (!parentPage) {
                throw new Error(`Page "${parentPageOrString}" is required to create "${appPage.type}"`);
            }
        }
        else {
            parentPage = parentPageOrString;
        }
        // this.logger.debug(`Search sub page ${appPage.path} (type=${appPage.type})`);
        if (!parentPage[subPageKey]) {
            parentPage[subPageKey] = {};
        }
        const childrens = parentPage[subPageKey];
        if (!(appPage.type in childrens)) {
            if (!appPage.projectPath) {
                appPage.projectPath = `${parentPage.projectPath}/${defaultSubProjectPath}`;
            }
            this.logger.info(`\t- Creating sub page url ${appPage.path} (path: ${appPage.projectPath}, type=${appPage.type})`);
            childrens[appPage.type] = appPage;
        }
        else {
            this.logger.info(`\t- Sub page with url ${appPage.path} already exists (path: ${appPage.projectPath}, type=${appPage.type})`);
        }
        // appPage['__parent'] = parentPage;
        return childrens[appPage.type];
    }
}
exports.ConfigGenerator = ConfigGenerator;
function asHexString(input) {
    return typeof input === "number" ? `0x${input.toString(16)}` : input;
}
class AppThemeGenerator {
    constructor(theme) {
        this.options = {
            shadeFactor: 0.1,
            tintFactor: 0.1
        };
        if (!theme) {
            theme = {
                colors: []
            };
        }
        this.theme = theme;
        this.initTheme();
    }
    get colors() {
        return this.theme.colors;
    }
    async fromImage(fromImage) {
        const colorCount = 3;
        const img = path_1.resolve(fromImage);
        const colors = await colorthief_1.default.getPalette(img, colorCount);
        this.setColor('primary', colors[0]);
        this.setColor('secondary', colors[1]);
        this.setColor('tertiary', colors[2]);
    }
    /**
     * set color theme from name
     * @param themeName
     */
    initTheme(themeName) {
        for (const color of DEFAULT_APP_THEME_COLORS) {
            if (this.colors.findIndex(c => c.name === color.name) == -1) {
                this.setColor(color.name, color.value);
            }
        }
    }
    /**
     * Set a new color in theme
     * @param name
     * @param value
     */
    setColor(name, value) {
        if (Array.isArray(value)) {
            if (value.length != 3) {
                throw new Error(`Illegal argument. Color must be an array of 3 elements representing red, green and blue colors`);
            }
            const [red, green, blue] = value;
            value = (red << 16) + (green << 8) + blue;
            // console.log(`RGB color: 0x${value.toString(16)}`);
        }
        value = typeof value === "string" ? parseInt(value, 16) : value;
        const colorIndex = this.theme.colors.findIndex(c => c.name === name);
        const color = {
            name: name,
            value: asHexString(value),
            contrast: asHexString(this.computeVariant(value, 'contrast')),
            shade: asHexString(this.computeVariant(value, 'shade')),
            tint: asHexString(this.computeVariant(value, 'tint'))
        };
        if (colorIndex >= 0) {
            this.theme.colors[colorIndex] = color;
        }
        else {
            this.theme.colors.push(color);
        }
    }
    /**
     * Inspired by https://maketintsandshades.com/about
     * @param color
     * @param variant
     */
    computeVariant(color, variant) {
        switch (variant) {
            case 'contrast':
                return (isDarkColor(color) ? 0xffffff : 0x000000);
            case 'shade':
                return this._applyFactor(color, (color) => Math.round(Math.max(color * this.options.shadeFactor, 255)));
            case 'tint':
                return this._applyFactor(color, (color) => Math.round(color + ((255 - color) * this.options.tintFactor)));
        }
        return color;
    }
    _applyFactor(color, fct) {
        let result = 0;
        for (const i of [16, 8, 0]) {
            const tmp = fct((color >> i) & 0xFF);
            result = result | (tmp << i);
            // console.info(i, color.toString(16), tmp.toString(16), (tmp << i).toString(16), result.toString(16));
        }
        return result;
    }
}
exports.AppThemeGenerator = AppThemeGenerator;
const DEFAULT_APP_THEME_COLORS = [
    {
        name: "primary",
        value: "0x3880ff"
    },
    {
        name: "secondary",
        value: "0x0cd1e8"
    },
    {
        name: "tertiary",
        value: "0x7044ff"
    },
    {
        name: "success",
        value: "0x10dc60"
    },
    {
        name: "warning",
        value: "0xffce00"
    },
    {
        name: "danger",
        value: "0xf04141"
    },
    {
        name: "dark",
        value: "0x222428"
    },
    {
        name: "medium",
        value: "0x989aa2"
    },
    {
        name: "light",
        value: "0xf4f5f8"
    },
    {
        name: "monitoring-background",
        value: "0xeeeeee"
    }
];
function isDarkColor(hexColor) {
    const { r, g, b } = hexToRgb(hexColor);
    const colorArray = [r / 255, g / 255, b / 255].map(v => {
        if (v <= 0.03928) {
            return v / 12.92;
        }
        return Math.pow((v + 0.055) / 1.055, 2.4);
    });
    const luminance = 0.2126 * colorArray[0] + 0.7152 * colorArray[1] + 0.0722 * colorArray[2];
    return luminance <= 0.179;
}
exports.isDarkColor = isDarkColor;
function hexToRgb(c) {
    return {
        r: (c << 16) & 0xFF,
        g: (c << 8) & 0xFF,
        b: c & 0xFF
    };
}
exports.hexToRgb = hexToRgb;
//# sourceMappingURL=config-generator.js.map