'use strict';

/**
 * Pass the data of a pass under a technique and organize it into a tree structure
 * @param passData
 */
exports.buildEffect = function(index, passData) {

    const props = passData.props;
    const defs = passData.defines;

    const tree = {
        name: `Pass ${index}${passData.phase ? ' - ' + passData.phase : ''}`,
        type: 'cc.Object',
        childMap: {},
    };

    const hideAttrs = ['USE_INSTANCING'];

    function encode(item) {
        let current = tree;

        /**
         * USE_INSTANCING is common to every child in passes
         * To make editing easier, they are referred to the outside of the passes
         * At this point, you need to set each of the passes to be non-editable and invisible
         */

        if (hideAttrs.includes(item.name)) {
            item.visible = false;
        }

        if (item.defines && item.defines.length) {
            item.defines.forEach((name) => {

                // The defines starting with ! are reverse dependencies, the data position will not change
                if (name.startsWith('!')) {
                    return;
                }

                let child = current.childMap[name];
                if (!child) {
                    child = current.childMap[name] = {
                        name,
                        // type: 'ui.Depend',
                        type: 'Boolean',
                        childMap: {},
                    };
                }
                current = child;
            });
        }

        if (current.childMap[item.name]) {
            const tempChildMap = current.childMap[item.name].childMap;
            current.childMap[item.name] = item;
            item.childMap = tempChildMap;
        } else {
            current.childMap[item.name] = item;
            item.childMap = {};
        }
        current.childMap[item.name].name = item.name;
    }

    defs.forEach((item) => {
        switch (item.type) {
            case 'Number':
                item.type = 'Enum';
                item.enumList = [];
                for (let i = item.range[0]; i <= item.range[1]; i++) {
                    item.enumList.push({
                        name: i,
                        value: i,
                    });
                }
                break;
            case 'String':
                item.type = 'Enum';
                item.enumList = item.options.map((str) => {
                    return {
                        name: str,
                        value: str,
                    };
                });
                break;
            case 'Enum': break; // Fix the problem that item.type === 'Enum' is reset to 'ui.Depend'
            default:
                // item.type = 'ui.Depend';
                item.type = 'Boolean';
        }
        encode(item);
    });
    props.forEach(encode);

    function encodeStates(item) {
        let current = tree;
        if (current.childMap[item.name]) {
            const tempChildMap = current.childMap[item.name].childMap;
            current.childMap[item.name] = item;
            item.childMap = tempChildMap;
        } else {
            current.childMap[item.name] = item;
            item.childMap = {};
        }

        if (item.isObject && item.value) {
            current = current.childMap[item.name];
            Object.keys(item.value).forEach((name) => {
                let child = current.childMap[name];
                if (!child) {
                    child = current.childMap[name] = {
                        name,
                        type: 'cc.Object',
                        childMap: {},
                    };
                }
            });
        }
    }

    function modifyType(item) {
        if (!item) {
            return;
        }

        if (item.isObject) {
            Object.keys(item.value).forEach((key) => {
                modifyType(item.value[key]);
            });
        } else if (item.isArray) {
            item.value.forEach((data) => {
                modifyType(data);
            });

            modifyType(item.elementTypeData);
        } else {
            switch (item.type) {
                case 'Number':
                    if (item.isEnum) {
                        item.type = 'Enum';
                        item.enumList = [];
                        Object.keys(item.enumData).forEach((key) => {
                            item.enumList.push({
                                name: key,
                                value: item.enumData[key],
                            });
                        });
                    }
                    break;
            }
        }
    }

    modifyType(passData.states);
    encodeStates(passData.states);

    function translate(item) {
        const children = Object.keys(item.childMap).map((name) => {
            const child = item.childMap[name];
            translate(child);
            return child;
        });
        if (item) {
            item.children = children;
        }
        return item;
    }
    const dump = translate(tree);
    dump.value = tree.childMap;
    return dump;
};

exports.materialTechniquePolyfill = function(origin) {
    let useInstancing;
    const passes = origin.passes.map((data, index) => {
        // Merge data.defines and data.props
        const pass = exports.buildEffect(index, data);
        pass.switch = data.switch;
        pass.propertyIndex = data.propertyIndex;

        if (!useInstancing && pass.childMap.USE_INSTANCING) {
            useInstancing = JSON.parse(JSON.stringify(pass.childMap.USE_INSTANCING));
            useInstancing.visible = true;
        }
        return pass;
    });

    /**
     * USE_INSTANCING is common to every child in passes
     * For ease of editing, they are referred to outside of the passes
     * Two external variables useInstancing, useBatching are provided to dock
     * The value of the first pass takes precedence
     */
    const technique = {
        name: origin.name,
        passes,
        useInstancing,
    };

    return technique;
};
