var __assign = (this && this.__assign) || function () {
    __assign = Object.assign || function(t) {
        for (var s, i = 1, n = arguments.length; i < n; i++) {
            s = arguments[i];
            for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
                t[p] = s[p];
        }
        return t;
    };
    return __assign.apply(this, arguments);
};
import nunjucks from 'nunjucks';
import uniq from 'lodash.uniq';
import { resolveSoftwareType, resolve3DType } from './bloq-types';
import { resolveBoardClass, resolveComponentClass } from './hardware';
var arduinoTemplate = "\n#include <ArduinoEventsLib.h>\n\n{% for include in includes %}\n#include <{{include}}>\n{% endfor %}\n\nHeap heap;\n\n{{componentsDefinitionsCode}}\n\n{{declarationsCode}}\n\nvoid setup() {\n  {{setupCode}}\n}\n\nvoid loop() {\n  heap.eventloop();\n\n  {{loopCode}}\n}\n\n{{definitionsCode}}\n\n";
export function generateArduinoCode(bloqs, hardware) {
    var includes = [];
    var componentsDefinitions = [];
    var declarations = [];
    var definitions = [];
    var setup = [];
    var loop = [];
    var board = hardware.board, components = hardware.components;
    if (!board)
        return '';
    var boardClass = resolveBoardClass(board.className);
    var _a = boardClass.code, code = _a === void 0 ? {} : _a;
    if (code.includes) {
        includes = includes.concat(code.includes);
    }
    if (code.definitions) {
        componentsDefinitions.push(code.definitions);
    }
    if (code.setup) {
        setup.push(code.setup);
    }
    components.forEach(function (component) {
        var _a = generateComponentCode(component, boardClass), componentIncludes = _a[0], componentDefinitions = _a[1], componentSetup = _a[2];
        includes = includes.concat(componentIncludes);
        componentsDefinitions.push(componentDefinitions);
        setup.push(componentSetup);
    });
    bloqs.forEach(function (bloq) {
        var code = generateBloqCode(bloq, '', resolveSoftwareType);
        if (code.declarations) {
            declarations.push(code.declarations);
        }
        if (code.definitions) {
            definitions.push(code.definitions);
        }
        if (code.setup) {
            setup.push(code.setup);
        }
        if (code.statement) {
            loop.push(code.statement);
        }
    });
    var finalCode = nunjucks.renderString(arduinoTemplate, {
        componentsDefinitionsCode: componentsDefinitions.join('\n'),
        declarationsCode: declarations.join('\n'),
        definitionsCode: definitions.join('\n'),
        setupCode: setup.join('\n'),
        loopCode: loop.join('\n'),
        includes: uniq(includes),
    });
    return finalCode;
}
var oomlTemplate = "\n{{mainCode}}\n";
export function generateOOMLCode(bloqs) {
    var main = bloqs.map(function (bloq) {
        var code = generateBloqCode(bloq, '', resolve3DType);
        return code.statement || '';
    });
    return nunjucks.renderString(oomlTemplate, { mainCode: main.join('\n') });
}
export function generateBloqCode(bloq, parentFinally, resolveType) {
    if (parentFinally === void 0) { parentFinally = ''; }
    var bloqType = resolveType(bloq.type);
    var _a = bloq.children, children = _a === void 0 ? [] : _a;
    var codeTemplates = bloqType.code || {};
    var finallyCode = codeTemplates.finally
        ? nunjucks.renderString(codeTemplates.finally, __assign({}, bloq))
        : '';
    var childrenCode = '';
    if (children[0]) {
        childrenCode = generateBloqCode(children[0], '', resolveType);
    }
    var nextCode = bloq.next
        ? generateBloqCode(bloq.next, finallyCode + parentFinally, resolveType)
        : {};
    var data = __assign({}, bloq.data);
    bloqType.content.forEach(function (item) {
        var itemData = data[item.dataField];
        if (item.type === 'bloq' && itemData) {
            data[item.dataField] = __assign({}, itemData, { code: generateBloqCode(itemData, '', resolveType) });
        }
    });
    var params = __assign({}, bloq, { data: data, bloqId: parseInt(Math.random() * 10000), nextCode: nextCode,
        childrenCode: childrenCode, finallyCode: !bloq.next ? finallyCode + parentFinally : '', getComponentClass: function (component) {
            return component ? resolveComponentClass(component.className) : {};
        }, getComponentCode: function (component, section, params) {
            if (!component)
                return '';
            var componentClass = resolveComponentClass(component.className);
            var code = componentClass.getCode(section);
            var codeParams = __assign({}, params, { component: component,
                componentClass: componentClass });
            return nunjucks.renderString(code.join(''), codeParams);
        } });
    return ['declarations', 'definitions', 'setup', 'statement'].reduce(function (map, codeBlock) {
        var _a;
        var code;
        var template = codeTemplates[codeBlock] || '';
        if (codeBlock === 'statement') {
            if (!bloqType.async) {
                if (params.nextCode && params.nextCode.statement) {
                    template += "\n {{nextCode.statement}}";
                }
                if (params.finallyCode) {
                    template += "\n {{finallyCode}}";
                }
            }
        }
        else {
            template = "\n {{nextCode." + codeBlock + "}}" + template;
        }
        code = nunjucks.renderString(template, params);
        return __assign({}, map, (_a = {}, _a[codeBlock] = code, _a));
    }, {});
}
export function generateComponentCode(component, boardClass) {
    var componentClass = resolveComponentClass(component.className) || {};
    var _a = componentClass.code, code = _a === void 0 ? {} : _a, _b = componentClass.connectors, connectors = _b === void 0 ? [] : _b;
    var _c = boardClass.ports, ports = _c === void 0 ? [] : _c;
    var params = {
        getConnector: function (name) { return connectors.find(function (c) { return c.name === name; }); },
        getBoardPin: function (portName, pinName) {
            var port = ports.find(function (p) { return p.name === portName; });
            if (!port)
                return {};
            var _a = port.pins, pins = _a === void 0 ? [] : _a;
            return pins.find(function (p) { return p.name === pinName; });
        },
        component: component,
    };
    var definitions = componentClass.getCode('definitions');
    var setup = componentClass.getCode('setup');
    var includes = code.includes || [];
    var definitionsCode = nunjucks.renderString(definitions.join(''), params);
    var setupCode = nunjucks.renderString(setup.join(''), params);
    return [includes, definitionsCode, setupCode];
}
//# sourceMappingURL=code-generation.js.map