// Types.
import { isEventOrGesture } from '../../core/bindable';
import { getBindingOptions, bindingConstants } from '../binding-builder';
import { profile } from '../../../profiling';
import * as debugModule from '../../../utils/debug';
import { Device } from '../../../platform';
import { sanitizeModuleName } from '../../../utils/common';
import { resolveModuleName } from '../../../module-name-resolver';
const legacyShortBarrels = [
    'text/formatted-string',
    'text/span',
    'ui/text-base/formatted-string',
    'ui/text-base/span',
    'ui/action-bar',
    'ui/activity-indicator',
    'ui/button',
    'ui/content-view',
    'ui/date-picker',
    'ui/frame',
    'ui/html-view',
    'ui/image',
    'ui/label',
    'ui/layouts/absolute-layout',
    'ui/layouts/dock-layout',
    'ui/layouts/grid-layout',
    'ui/layouts/stack-layout',
    'ui/layouts/flexbox-layout',
    'ui/layouts/wrap-layout',
    'ui/list-picker',
    'ui/page',
    'ui/placeholder',
    'ui/progress',
    'ui/proxy-view-container',
    'ui/repeater',
    'ui/scroll-view',
    'ui/search-bar',
    'ui/segmented-bar',
    'ui/slider',
    'ui/switch',
    'ui/tab-view',
    'ui/web-view',
    'ui/text-field',
    'ui/text-view',
    'ui/time-picker',
    'ui/list-view',
];
const CORE_UI_BARREL = '@nativescript/core/ui';
const CODE_FILE = 'codeFile';
const CSS_FILE = 'cssFile';
const IMPORT = 'import';
const createComponentInstance = profile('createComponentInstance', (elementName, namespace) => {
    let instance;
    let instanceModule;
    // Get module id.
    let resolvedModuleName;
    try {
        if (typeof namespace === 'string') {
            if (legacyShortBarrels.includes(namespace)) {
                // console.log('CORE_UI_BARREL namespace:', namespace)
                resolvedModuleName = CORE_UI_BARREL;
            }
            else {
                // console.log('CUSTOM namespace:', namespace)
                resolvedModuleName = resolveModuleName(namespace, '');
            }
            instanceModule = global.loadModule(resolvedModuleName);
        }
        else {
            // load module from @nativescript/core/ui or mapped paths
            // resolvedModuleName =
            // 	MODULES[elementName] ||
            // 	UI_PATH +
            // 		(elementName.toLowerCase().indexOf('layout') !== -1 ? 'layouts/' : '') +
            // 		elementName
            // 			.split(/(?=[A-Z])/)
            // 			.join('-')
            // 			.toLowerCase();
            instanceModule = global.loadModule(CORE_UI_BARREL);
            // don't register core modules for HMR self-accept
            // instanceModule = global.loadModule(resolvedModuleName, false);
        }
        // Get the component type from module.
        const instanceType = instanceModule[elementName];
        // Create instance of the component.
        instance = new instanceType();
    }
    catch (ex) {
        throw new debugModule.ScopeError(ex, "Module '" + (resolvedModuleName || elementName) + "' not found for element '" + (namespace ? namespace + ':' : '') + elementName + "'.");
    }
    return { instance, instanceModule };
});
const getComponentModuleExports = profile('getComponentModuleExports', (instance, moduleExports, attributes) => {
    if (attributes) {
        const codeFileAttribute = attributes[CODE_FILE] || attributes[IMPORT];
        if (codeFileAttribute) {
            const resolvedCodeFileModule = resolveModuleName(sanitizeModuleName(codeFileAttribute), '');
            if (resolvedCodeFileModule) {
                moduleExports = global.loadModule(resolvedCodeFileModule);
                instance.exports = moduleExports;
            }
            else {
                throw new Error(`Code file with path "${codeFileAttribute}" cannot be found! Looking for webpack module with name "${resolvedCodeFileModule}"`);
            }
        }
    }
    return moduleExports;
});
const applyComponentCss = profile('applyComponentCss', (instance, moduleName, attributes) => {
    let cssApplied = false;
    if (attributes && attributes[CSS_FILE]) {
        const resolvedCssModuleName = resolveModuleName(sanitizeModuleName(attributes[CSS_FILE]), 'css');
        if (resolvedCssModuleName) {
            instance.addCssFile(resolvedCssModuleName);
            cssApplied = true;
        }
        else {
            throw new Error(`Css file with path "${attributes[CSS_FILE]}" cannot be found! Looking for webpack module with name "${resolvedCssModuleName}"`);
        }
    }
    if (moduleName && !cssApplied) {
        const resolvedCssModuleName = resolveModuleName(moduleName, 'css');
        if (resolvedCssModuleName) {
            instance.addCssFile(resolvedCssModuleName);
        }
    }
});
const applyComponentAttributes = profile('applyComponentAttributes', (instance, instanceModule, moduleExports, attributes) => {
    if (instance && instanceModule) {
        for (let attr in attributes) {
            const attrValue = attributes[attr];
            if (attr.indexOf(':') !== -1) {
                const platformName = attr.split(':')[0].trim();
                if (platformName.toLowerCase() === Device.os.toLowerCase()) {
                    attr = attr.split(':')[1].trim();
                }
                else {
                    continue;
                }
            }
            if (attr.indexOf('.') !== -1) {
                let subObj = instance;
                const properties = attr.split('.');
                const subPropName = properties[properties.length - 1];
                for (let i = 0; i < properties.length - 1; i++) {
                    if (subObj !== undefined && subObj !== null) {
                        subObj = subObj[properties[i]];
                    }
                }
                if (subObj !== undefined && subObj !== null) {
                    setPropertyValue(subObj, instanceModule, moduleExports, subPropName, attrValue);
                }
            }
            else {
                setPropertyValue(instance, instanceModule, moduleExports, attr, attrValue);
            }
        }
    }
});
export function getComponentModule(elementName, namespace, attributes, moduleExports, moduleNamePath, isRootComponent) {
    // Support lower-case-dashed component declaration in the XML (https://github.com/NativeScript/NativeScript/issues/309).
    elementName = elementName
        .split('-')
        .map((s) => s[0].toUpperCase() + s.substring(1))
        .join('');
    const { instance, instanceModule } = createComponentInstance(elementName, namespace, null);
    moduleExports = getComponentModuleExports(instance, moduleExports, attributes);
    if (isRootComponent) {
        applyComponentCss(instance, moduleNamePath, attributes);
    }
    applyComponentAttributes(instance, instanceModule, moduleExports, attributes);
    let componentModule;
    if (instance && instanceModule) {
        componentModule = { component: instance, exports: instanceModule };
    }
    return componentModule;
}
export function setPropertyValue(instance, instanceModule, exports, propertyName, propertyValue) {
    // Note: instanceModule can be null if we are loading custom component with no code-behind.
    if (isBinding(propertyValue) && instance.bind) {
        const bindOptions = getBindingOptions(propertyName, getBindingExpressionFromAttribute(propertyValue));
        instance.bind({
            sourceProperty: bindOptions[bindingConstants.sourceProperty],
            targetProperty: bindOptions[bindingConstants.targetProperty],
            expression: bindOptions[bindingConstants.expression],
            twoWay: bindOptions[bindingConstants.twoWay],
        }, bindOptions[bindingConstants.source]);
    }
    else if (isEventOrGesture(propertyName, instance)) {
        // Get the event handler from page module exports.
        const handler = exports && exports[propertyValue];
        // Check if the handler is function and add it to the instance for specified event name.
        if (typeof handler === 'function') {
            instance.on(propertyName, handler);
        }
    }
    else if (isKnownFunction(propertyName, instance) && exports && typeof exports[propertyValue] === 'function') {
        instance[propertyName] = exports[propertyValue];
    }
    else {
        instance[propertyName] = propertyValue;
    }
}
function getBindingExpressionFromAttribute(value) {
    return value.replace('{{', '').replace('}}', '').trim();
}
function isBinding(value) {
    let isBinding;
    if (typeof value === 'string') {
        const str = value.trim();
        isBinding = str.indexOf('{{') === 0 && str.lastIndexOf('}}') === str.length - 2;
    }
    return isBinding;
}
// For example, ListView.itemTemplateSelector
const KNOWN_FUNCTIONS = 'knownFunctions';
function isKnownFunction(name, instance) {
    return instance.constructor && KNOWN_FUNCTIONS in instance.constructor && instance.constructor[KNOWN_FUNCTIONS].indexOf(name) !== -1;
}
//# sourceMappingURL=index.js.map