import { ImperativeLanguageExporter, toSnakeCase, escapeString } from '../LanguageExporter.js';

/* ────────────────────────────────────────────────────────────────
   Lua Exporter — LÖVE2D / Defold / General Lua
   
   Exports visual scripts to Lua for game engines like
   LÖVE2D, Defold, and general Lua embedding.
──────────────────────────────────────────────────────────────── */
/* ── Lua Type Mapping (documentation only, Lua is dynamic) ──── */
const luaTypeMapping = {
    void: 'nil',
    bool: 'boolean',
    int: 'number',
    float: 'number',
    string: 'string',
    vector2: 'table',
    vector3: 'table',
    color: 'table',
    entity: 'table',
    array: 'table',
    dictionary: 'table',
    any: 'any',
};
/* ── Lua Exporter ─────────────────────────────────────────────── */
class LuaExporter extends ImperativeLanguageExporter {
    constructor(config = {}) {
        super({
            indent: '    ',
            ...config,
        });
        this.languageName = 'lua';
        this.fileExtension = 'lua';
        this.typeMapping = luaTypeMapping;
        this.statementTerminator = '';
        this.breakKeyword = 'break';
        this.continueKeyword = '-- continue (use goto in Lua 5.2+)';
        this.target = config.targetEngine || 'love2d';
    }
    /* ── Main Generation ────────────────────────────────────────── */
    generateScript(script) {
        const builder = this.createBuilder();
        // File header
        if (script.headerComment) {
            builder.comment(script.headerComment);
        }
        builder.line('--[[');
        builder.line(`  Generated by NiceToDev Visual Scripting`);
        builder.line(`  Target: ${this.target}`);
        builder.line(']]');
        builder.emptyLine();
        // Local requires for LÖVE2D
        if (this.target === 'love2d') {
            builder.comment('LÖVE2D requires');
            builder.emptyLine();
        }
        // Generate module table for class-like structure
        const mainClass = script.classes[0];
        if (mainClass) {
            this.generateClass(mainClass, builder);
        }
        // Top-level functions
        for (const func of script.functions || []) {
            this.generateMethod(func, builder);
            builder.emptyLine();
        }
        return builder.toString();
    }
    generateClass(cls, builder) {
        // Lua uses tables for OOP
        const className = cls.name;
        builder.comment(`Class: ${className}`);
        if (cls.comment) {
            builder.comment(cls.comment);
        }
        builder.emptyLine();
        builder.line(`local ${className} = {}`);
        builder.line(`${className}.__index = ${className}`);
        builder.emptyLine();
        // Enums as module constants
        for (const en of cls.enums || []) {
            this.generateEnum(en, builder);
            builder.emptyLine();
        }
        // Constructor
        builder.line(`function ${className}:new()`);
        builder.indent();
        builder.line('local self = setmetatable({}, self)');
        // Initialize fields
        for (const field of cls.fields) {
            const name = toSnakeCase(field.name);
            const init = field.initializer ? this.generateExpression(field.initializer) : 'nil';
            builder.line(`self.${name} = ${init}`);
        }
        builder.line('return self');
        builder.dedent();
        builder.line('end');
        builder.emptyLine();
        // Methods
        for (const method of cls.methods) {
            this.generateClassMethod(method, className, builder);
            builder.emptyLine();
        }
        // Export module
        builder.line(`return ${className}`);
    }
    generateEnum(en, builder) {
        builder.line(`-- Enum: ${en.name}`);
        builder.line(`local ${en.name} = {`);
        builder.indent();
        for (const member of en.members) {
            const value = member.value !== undefined ? member.value : `"${member.name}"`;
            builder.line(`${member.name} = ${value},`);
        }
        builder.dedent();
        builder.line('}');
    }
    generateClassMethod(method, className, builder) {
        const name = this.convertMethodName(method.name);
        const params = method.params.map((p) => toSnakeCase(p.name)).join(', ');
        // Use : for method syntax (self is implicit)
        builder.line(`function ${className}:${name}(${params})`);
        builder.indent();
        if (method.body.length === 0) {
            builder.line('-- empty');
        }
        else {
            for (const stmt of method.body) {
                this.generateStatement(stmt, builder);
            }
        }
        builder.dedent();
        builder.line('end');
    }
    generateMethod(method, builder) {
        const name = toSnakeCase(method.name);
        const params = method.params.map((p) => toSnakeCase(p.name)).join(', ');
        const localKeyword = method.isStatic ? 'local ' : '';
        builder.line(`${localKeyword}function ${name}(${params})`);
        builder.indent();
        if (method.body.length === 0) {
            builder.line('-- empty');
        }
        else {
            for (const stmt of method.body) {
                this.generateStatement(stmt, builder);
            }
        }
        builder.dedent();
        builder.line('end');
    }
    generateField(_field, _builder) {
        // Fields are generated in constructor
    }
    convertMethodName(name) {
        // Convert Unity/common lifecycle methods to LÖVE equivalents
        if (this.target === 'love2d') {
            const methodMap = {
                Start: 'load',
                Update: 'update',
                Draw: 'draw',
                OnDestroy: 'quit',
            };
            return methodMap[name] || toSnakeCase(name);
        }
        return toSnakeCase(name);
    }
    /* ── Statement Generation ───────────────────────────────────── */
    generateVarDecl(decl, builder) {
        const name = toSnakeCase(decl.name);
        const init = decl.initializer ? ` = ${this.generateExpression(decl.initializer)}` : '';
        builder.line(`local ${name}${init}`);
    }
    generateAssignment(assign, builder) {
        const target = this.generateExpression(assign.target);
        const value = this.generateExpression(assign.value);
        // Lua doesn't have compound assignment, expand it
        switch (assign.operator) {
            case '+=':
                builder.line(`${target} = ${target} + ${value}`);
                break;
            case '-=':
                builder.line(`${target} = ${target} - ${value}`);
                break;
            case '*=':
                builder.line(`${target} = ${target} * ${value}`);
                break;
            case '/=':
                builder.line(`${target} = ${target} / ${value}`);
                break;
            case '%=':
                builder.line(`${target} = ${target} % ${value}`);
                break;
            default:
                builder.line(`${target} = ${value}`);
        }
    }
    generateIf(stmt, builder) {
        var _a;
        const cond = this.generateExpression(stmt.condition);
        builder.line(`if ${cond} then`);
        builder.indent();
        for (const s of stmt.thenBranch) {
            this.generateStatement(s, builder);
        }
        builder.dedent();
        for (const elseIf of stmt.elseIfs || []) {
            builder.line(`elseif ${this.generateExpression(elseIf.condition)} then`);
            builder.indent();
            for (const s of elseIf.body) {
                this.generateStatement(s, builder);
            }
            builder.dedent();
        }
        if ((_a = stmt.elseBranch) === null || _a === void 0 ? void 0 : _a.length) {
            builder.line('else');
            builder.indent();
            for (const s of stmt.elseBranch) {
                this.generateStatement(s, builder);
            }
            builder.dedent();
        }
        builder.line('end');
    }
    generateWhile(stmt, builder) {
        const cond = this.generateExpression(stmt.condition);
        builder.line(`while ${cond} do`);
        builder.indent();
        for (const s of stmt.body) {
            this.generateStatement(s, builder);
        }
        builder.dedent();
        builder.line('end');
    }
    generateFor(stmt, builder) {
        // Convert to Lua's limited for or while
        if (stmt.init) {
            this.generateStatement(stmt.init, builder);
        }
        const cond = stmt.condition ? this.generateExpression(stmt.condition) : 'true';
        builder.line(`while ${cond} do`);
        builder.indent();
        for (const s of stmt.body) {
            this.generateStatement(s, builder);
        }
        if (stmt.update) {
            builder.line(this.generateExpression(stmt.update));
        }
        builder.dedent();
        builder.line('end');
    }
    generateForEach(stmt, builder) {
        const iterable = this.generateExpression(stmt.iterable);
        const variable = toSnakeCase(stmt.variable);
        // Use ipairs for arrays, pairs for tables
        builder.line(`for _, ${variable} in ipairs(${iterable}) do`);
        builder.indent();
        for (const s of stmt.body) {
            this.generateStatement(s, builder);
        }
        builder.dedent();
        builder.line('end');
    }
    generateReturn(stmt, builder) {
        if (stmt.value) {
            builder.line(`return ${this.generateExpression(stmt.value)}`);
        }
        else {
            builder.line('return');
        }
    }
    generateSwitch(stmt, builder) {
        var _a, _b;
        // Lua doesn't have switch, use if-elseif chain
        const expr = this.generateExpression(stmt.expression);
        const tempVar = '_switch_val';
        builder.line(`local ${tempVar} = ${expr}`);
        let first = true;
        for (const c of stmt.cases) {
            const caseVal = this.generateExpression(c.value);
            const keyword = first ? 'if' : 'elseif';
            builder.line(`${keyword} ${tempVar} == ${caseVal} then`);
            builder.indent();
            for (const s of c.body) {
                this.generateStatement(s, builder);
            }
            builder.dedent();
            first = false;
        }
        if ((_a = stmt.defaultCase) === null || _a === void 0 ? void 0 : _a.length) {
            builder.line('else');
            builder.indent();
            for (const s of stmt.defaultCase) {
                this.generateStatement(s, builder);
            }
            builder.dedent();
        }
        if (!first || ((_b = stmt.defaultCase) === null || _b === void 0 ? void 0 : _b.length)) {
            builder.line('end');
        }
    }
    generateTry(stmt, builder) {
        var _a;
        // Lua uses pcall for error handling
        builder.line('local ok, err = pcall(function()');
        builder.indent();
        for (const s of stmt.tryBlock) {
            this.generateStatement(s, builder);
        }
        builder.dedent();
        builder.line('end)');
        if ((_a = stmt.catchBlocks) === null || _a === void 0 ? void 0 : _a.length) {
            builder.line('if not ok then');
            builder.indent();
            for (const s of stmt.catchBlocks[0].body) {
                this.generateStatement(s, builder);
            }
            builder.dedent();
            builder.line('end');
        }
    }
    /* ── Expression Generation ──────────────────────────────────── */
    generateLiteral(lit) {
        var _a;
        switch (lit.valueType) {
            case 'bool':
                return lit.value ? 'true' : 'false';
            case 'string':
                return `"${escapeString(String(lit.value))}"`;
            case 'float':
            case 'int':
                return String(lit.value);
            case 'vector2': {
                const v = lit.value;
                return `{ x = ${v.x}, y = ${v.y} }`;
            }
            case 'vector3': {
                const v = lit.value;
                return `{ x = ${v.x}, y = ${v.y}, z = ${v.z} }`;
            }
            case 'color': {
                const c = lit.value;
                return `{ r = ${c.r}, g = ${c.g}, b = ${c.b}, a = ${(_a = c.a) !== null && _a !== void 0 ? _a : 1} }`;
            }
            default:
                return lit.value === null ? 'nil' : String(lit.value);
        }
    }
    generateBinaryOp(op) {
        const left = this.generateExpression(op.left);
        const right = this.generateExpression(op.right);
        // Lua operator mapping
        let operator = op.operator;
        switch (op.operator) {
            case '&&':
                operator = 'and';
                break;
            case '||':
                operator = 'or';
                break;
            case '!=':
                operator = '~=';
                break;
        }
        return `(${left} ${operator} ${right})`;
    }
    generateUnaryOp(op) {
        const operand = this.generateExpression(op.operand);
        // Lua uses 'not' instead of '!'
        if (op.operator === '!') {
            return `not ${operand}`;
        }
        return op.prefix ? `${op.operator}${operand}` : `${operand}${op.operator}`;
    }
    generateCall(call) {
        const args = call.args.map((a) => this.generateExpression(a)).join(', ');
        const funcName = toSnakeCase(call.callee);
        return `${funcName}(${args})`;
    }
    generateMethodCall(call) {
        const obj = this.generateExpression(call.object);
        const args = call.args.map((a) => this.generateExpression(a)).join(', ');
        const methodName = toSnakeCase(call.method);
        // Use : for method calls (passes self)
        return `${obj}:${methodName}(${args})`;
    }
    generatePropertyAccess(access) {
        const obj = this.generateExpression(access.object);
        return `${obj}.${toSnakeCase(access.property)}`;
    }
    generateIndexAccess(access) {
        const obj = this.generateExpression(access.object);
        const idx = this.generateExpression(access.index);
        return `${obj}[${idx}]`;
    }
    generateConditional(cond) {
        // Lua doesn't have ternary, use and/or idiom
        const condition = this.generateExpression(cond.condition);
        const ifTrue = this.generateExpression(cond.ifTrue);
        const ifFalse = this.generateExpression(cond.ifFalse);
        return `(${condition} and ${ifTrue} or ${ifFalse})`;
    }
    generateArrayLiteral(arr) {
        const elements = arr.elements.map((e) => this.generateExpression(e)).join(', ');
        return `{ ${elements} }`;
    }
    generateDictionaryLiteral(dict) {
        const entries = dict.entries
            .map((e) => {
            const key = this.generateExpression(e.key);
            const value = this.generateExpression(e.value);
            // Use bracket notation for non-identifier keys
            return `[${key}] = ${value}`;
        })
            .join(', ');
        return `{ ${entries} }`;
    }
    generateCast(_cast) {
        // Lua is dynamically typed, no casting needed
        return this.generateExpression(_cast.expression);
    }
    generateLambda(lambda) {
        const params = lambda.params.map((p) => toSnakeCase(p.name)).join(', ');
        if (Array.isArray(lambda.body)) {
            // Multi-statement lambda
            const bodyParts = [];
            for (const stmt of lambda.body) {
                const b = this.createBuilder();
                this.generateStatement(stmt, b);
                bodyParts.push(b.toString().trim());
            }
            return `function(${params}) ${bodyParts.join('; ')} end`;
        }
        else {
            const body = this.generateExpression(lambda.body);
            return `function(${params}) return ${body} end`;
        }
    }
    generateNew(n) {
        const args = n.args.map((a) => this.generateExpression(a)).join(', ');
        return `${n.typeName}:new(${args})`;
    }
    /* ── Lua-specific Helpers ───────────────────────────────────── */
    formatArrayType(_elementType) {
        return 'table';
    }
    formatDictType(_keyType, _valueType) {
        return 'table';
    }
    formatNullableType(_baseType) {
        return 'any';
    }
}

export { LuaExporter };
//# sourceMappingURL=LuaExporter.js.map
