import * as React from "react";
import { Framework } from "./dev-tools";
import { get, isObject, set, uniqueId } from "lodash-es";
import SyntaxHighlighter, { registerLanguage } from "react-syntax-highlighter/prism-light";
import jsx from "react-syntax-highlighter/languages/prism/jsx";
import vs from "./dev-tools-code-preview-style";
registerLanguage("jsx", jsx);
export default class CodePreview extends React.Component {
    render() {
        return (React.createElement(SyntaxHighlighter, { language: "jsx", style: vs }, this.getCodePreview()));
    }
    getCodePreview() {
        let codePreview = "TBD";
        this.variables = "";
        switch (this.props.framework) {
            case Framework.react:
                codePreview = this.generateReactCodePreview(this.props.componentName, this.props.data, "", "");
                break;
        }
        return this.variables + codePreview;
    }
    /**
     * Generates the react code preview as a string
     */
    generateReactCodePreview(componentName, componentData, tab, location) {
        let renderedComponent = "";
        const currentTab = `${tab === "" ? "" : "\n"}${tab}`;
        const newTab = `    ${tab}`;
        const hasChildren = componentData && componentData.children;
        renderedComponent += `${currentTab}<${componentName}`;
        renderedComponent += this.generateReactAttributes(componentData, newTab, location);
        renderedComponent += this.generateReactComponent({
            hasChildren,
            componentName,
            componentData,
            tab,
            newTab,
            location
        });
        return renderedComponent;
    }
    /**
     * Generate the syntax for the react component code preview
     */
    generateReactComponent(componentConfig) {
        if (componentConfig.hasChildren) {
            return this.generateReactChildren(componentConfig.componentName, componentConfig.componentData, componentConfig.tab, componentConfig.newTab, componentConfig.location);
        }
        return isObject(componentConfig.componentData)
            ? `${this.hasOnlyChildrenOrNoProps(Object.keys(componentConfig.componentData), componentConfig.componentData)
                ? " "
                : componentConfig.tab}/>`
            : ` />`;
    }
    /**
     * Generates the string if the component contains children
     */
    generateReactChildren(componentName, componentData, tab, newTab, location) {
        let renderedComponentWithChild = `${Object.keys(componentData).length < 2 ? "" : tab}>`;
        const componentChildren = Array.isArray(componentData.children) ? componentData.children : [componentData.children];
        for (let i = 0, childrenLength = componentChildren.length; i < childrenLength; i++) {
            renderedComponentWithChild += this.getReactChildAsComponentOrString(componentChildren, i, componentName, location, newTab);
        }
        renderedComponentWithChild += `\n${tab}</${componentName}>`;
        return renderedComponentWithChild;
    }
    getReactChildAsComponentOrString(componentChildren, index, componentName, location, newTab) {
        if (typeof componentChildren[index] === "object") {
            return this.generateReactCodePreview(this.getComponentName(componentChildren[index]), componentChildren[index].props, newTab, location === ""
                ? componentName.charAt(0).toLowerCase() + componentName.slice(1) + index
                : location + componentName + index);
        }
        else if (typeof componentChildren[index] === "string") {
            return `\n${newTab}${componentChildren[index]}`;
        }
        return "";
    }
    /**
     * Generates the React attributes for the implementation
     * based on example data
     */
    generateReactAttributes(data, tab, location) {
        let attributes = "";
        if (data) {
            const propertyKeys = Object.keys(data);
            for (const property of propertyKeys) {
                attributes += this.getReactAttributesFromProperties(property, data, tab, location);
            }
            attributes += this.hasOnlyChildrenOrNoProps(propertyKeys, data)
                ? ``
                : `\n`;
        }
        return attributes;
    }
    hasOnlyChildrenOrNoProps(propertyKeys, data) {
        return (propertyKeys.length === 1 && data.children) || propertyKeys.length === 0;
    }
    getReactAttributesFromProperties(property, data, tab, location) {
        if (property !== "children") {
            if (isObject(data[property])) {
                return this.getReactAttributeObject(location, property, data, tab);
            }
            else if (typeof data[property] === "string") {
                return `\n${tab}${property}="${data[property]}"`;
            }
            else if (typeof data[property] !== "undefined") {
                return `\n${tab}${property}={${data[property]}}`;
            }
        }
        return "";
    }
    /**
     * Gets the attributes as a string
     */
    getReactAttributeObject(location, property, data, tab) {
        const propertyName = location === ""
            ? property
            : location + property.charAt(0).toUpperCase() + property.slice(1);
        const childrenLocations = this.findChildren(data[property], "", []);
        const propertyChildren = this.getReactChildrenFromProperties(data, property, propertyName, childrenLocations);
        const attributes = `\n${tab}${property}={${propertyName}}`;
        this.variables += `const ${propertyName} = `;
        this.variables += `${this.replaceReactPropertyChildren(data[property], propertyChildren)};\n\n`;
        this.setReactChildrenVariables(childrenLocations, propertyChildren, propertyName);
        return attributes;
    }
    getReactChildrenFromProperties(data, property, propertyName, childrenLocations) {
        const propertyChildren = [];
        if (childrenLocations.length > 0) {
            for (let i = childrenLocations.length - 1; i >= 0; i--) {
                propertyChildren.push({
                    id: uniqueId(),
                    propertyName,
                    location: childrenLocations[i],
                    data: get(data, `${property}.${childrenLocations[i]}.children`)
                });
            }
        }
        return propertyChildren;
    }
    /**
     * Sets the private variables to children objects
     */
    setReactChildrenVariables(childrenLocations, propertyChildren, propertyName) {
        if (childrenLocations.length > 0) {
            for (let i = childrenLocations.length - 1; i >= 0; i--) {
                let stringDataWithReactComponentNames = JSON.stringify(propertyChildren[i].data.props, null, 2);
                stringDataWithReactComponentNames = this.replaceReactChildrenVariableIdsWithComponent(childrenLocations, propertyChildren, stringDataWithReactComponentNames);
                this.variables += `const ${propertyName}${propertyChildren[i].location.replace(/children|props|\./g, "")} = `;
                this.variables += `${stringDataWithReactComponentNames};\n\n`;
            }
        }
    }
    replaceReactChildrenVariableIdsWithComponent(childrenLocations, propertyChildren, stringDataWithReactComponentNames) {
        let updatedStringDataWithReactComponentNames = stringDataWithReactComponentNames;
        for (let j = childrenLocations.length - 1; j >= 0; j--) {
            updatedStringDataWithReactComponentNames = updatedStringDataWithReactComponentNames.replace(`"${propertyChildren[j].id}"`, propertyChildren[j].component);
        }
        return updatedStringDataWithReactComponentNames;
    }
    /**
     * Replaces the React properties which accept children with a formatted React component
     */
    replaceReactPropertyChildren(propertyData, children) {
        let JSONStringWithReactChildComponents = "";
        const propertyDataWithIds = propertyData;
        for (const child of children) {
            const componentName = this.getComponentName(get(propertyData, child.location).children);
            child.component = `<${componentName} {...${child.propertyName}${child.location.replace(/children|props|\./g, "")}} />`;
            set(propertyDataWithIds, `${child.location}.children`, child.id);
        }
        JSONStringWithReactChildComponents = JSON.stringify(propertyDataWithIds, null, 2);
        for (const child of children) {
            JSONStringWithReactChildComponents = JSONStringWithReactChildComponents.replace(`"${child.id}"`, child.component);
        }
        return JSONStringWithReactChildComponents;
    }
    /**
     * Gets the component name form the component in data
     */
    getComponentName(component) {
        let name = "Undefined";
        this.props.childOptions.forEach((item) => {
            if (item.component === component.type) {
                name = item.name;
            }
        });
        return name;
    }
    /**
     * Find children in data
     */
    findChildren(data, location, childLocations) {
        const locations = childLocations;
        if (Array.isArray(data)) {
            locations.concat(this.findChildrenFromArray(data, location, childLocations));
        }
        else if (typeof data === "object" && data !== null) {
            locations.concat(this.findChildrenFromObject(data, location, childLocations));
        }
        return locations;
    }
    /**
     * Find children in an array
     */
    findChildrenFromArray(data, location, childLocations) {
        for (let i = 0, dataLength = data.length; i < dataLength; i++) {
            return this.findChildren(data[i], `${location === "" ? "" : `${location}.`}${i}`, childLocations);
        }
    }
    /**
     * Find children in an object
     */
    findChildrenFromObject(data, location, childLocations) {
        const locations = [];
        Object.keys(data).forEach((item) => {
            if (item === "children") {
                locations.push(location);
            }
            if (typeof data[item] === "object") {
                locations.concat(this.findChildren(data[item], `${location === "" ? "" : `${location}.`}${item}`, childLocations));
            }
        });
        return locations;
    }
}
//# sourceMappingURL=dev-tools-code-preview.js.map