'use strict';

var LanguageExporter = require('../LanguageExporter.js');

/* ────────────────────────────────────────────────────────────────
   C# Exporter — Unity MonoBehaviour Generation
   
   Exports visual scripts to C# for Unity game engine.
   Generates MonoBehaviour classes with proper Unity lifecycle.
──────────────────────────────────────────────────────────────── */
/* ── C# Type Mapping ──────────────────────────────────────────── */
const csharpTypeMapping = {
    void: 'void',
    bool: 'bool',
    int: 'int',
    float: 'float',
    string: 'string',
    vector2: 'Vector2',
    vector3: 'Vector3',
    color: 'Color',
    entity: 'GameObject',
    array: 'List',
    dictionary: 'Dictionary',
    any: 'object',
};
/* ── C# Exporter ──────────────────────────────────────────────── */
class CSharpExporter extends LanguageExporter.ImperativeLanguageExporter {
    constructor(config = {}) {
        super({
            targetEngine: 'unity',
            ...config,
        });
        this.languageName = 'csharp';
        this.fileExtension = 'cs';
        this.typeMapping = csharpTypeMapping;
        this.statementTerminator = ';';
        this.breakKeyword = 'break';
        this.continueKeyword = 'continue';
    }
    /* ── Main Generation ────────────────────────────────────────── */
    generateScript(script) {
        const builder = this.createBuilder();
        // File header
        if (script.headerComment) {
            builder.docComment(script.headerComment, 'csharp');
        }
        // Usings
        builder.line('using System;');
        builder.line('using System.Collections;');
        builder.line('using System.Collections.Generic;');
        builder.line('using UnityEngine;');
        builder.line('using UnityEngine.Events;');
        // Additional imports
        for (const imp of script.imports) {
            this.generateImport(imp, builder);
        }
        builder.emptyLine();
        // Namespace
        const namespace = this.config.namespace || 'NiceToDev.Generated';
        builder.line(`namespace ${namespace}`);
        builder.line('{');
        builder.indent();
        // Enums
        for (const en of script.enums || []) {
            this.generateEnum(en, builder);
            builder.emptyLine();
        }
        // Classes
        for (let i = 0; i < script.classes.length; i++) {
            this.generateClass(script.classes[i], builder);
            if (i < script.classes.length - 1) {
                builder.emptyLine();
            }
        }
        builder.dedent();
        builder.line('}');
        return builder.toString();
    }
    generateImport(imp, builder) {
        builder.line(`using ${imp.module};`);
    }
    generateEnum(en, builder) {
        builder.line(`public enum ${en.name}`);
        builder.line('{');
        builder.indent();
        for (let i = 0; i < en.members.length; i++) {
            const member = en.members[i];
            const valuePart = member.value !== undefined ? ` = ${member.value}` : '';
            const comma = i < en.members.length - 1 ? ',' : '';
            builder.line(`${member.name}${valuePart}${comma}`);
        }
        builder.dedent();
        builder.line('}');
    }
    generateClass(cls, builder) {
        var _a, _b;
        // Class doc comment
        if (cls.comment) {
            builder.docComment(cls.comment, 'csharp');
        }
        // Attributes
        for (const attr of cls.attributes || []) {
            builder.line(`[${attr}]`);
        }
        // Class declaration
        const modifiers = [];
        if (cls.isAbstract)
            modifiers.push('abstract');
        if (cls.isSealed)
            modifiers.push('sealed');
        if (cls.isPartial)
            modifiers.push('partial');
        const baseClass = cls.baseClass || this.config.baseClassName || 'MonoBehaviour';
        const interfaces = ((_a = cls.interfaces) === null || _a === void 0 ? void 0 : _a.length) ? `, ${cls.interfaces.join(', ')}` : '';
        builder.line(`public ${modifiers.join(' ')} class ${cls.name} : ${baseClass}${interfaces}`.replace(/\s+/g, ' '));
        builder.line('{');
        builder.indent();
        // Nested enums
        for (const en of cls.enums || []) {
            this.generateEnum(en, builder);
            builder.emptyLine();
        }
        // Fields
        const hasFields = cls.fields.length > 0;
        for (const field of cls.fields) {
            this.generateField(field, builder);
        }
        if (hasFields)
            builder.emptyLine();
        // Properties
        const hasProps = (((_b = cls.properties) === null || _b === void 0 ? void 0 : _b.length) || 0) > 0;
        for (const prop of cls.properties || []) {
            this.generateProperty(prop, builder);
        }
        if (hasProps)
            builder.emptyLine();
        // Methods
        for (let i = 0; i < cls.methods.length; i++) {
            this.generateMethod(cls.methods[i], builder);
            if (i < cls.methods.length - 1) {
                builder.emptyLine();
            }
        }
        builder.dedent();
        builder.line('}');
    }
    generateField(field, builder) {
        if (field.comment) {
            builder.docComment(field.comment, 'csharp');
        }
        // Attributes
        for (const attr of field.attributes || []) {
            builder.line(`[${attr}]`);
        }
        if (field.isExported) {
            builder.line('[SerializeField]');
        }
        const visibility = field.visibility || 'private';
        const staticMod = field.isStatic ? 'static ' : '';
        const constMod = field.isConst ? 'const ' : '';
        const readonlyMod = field.isReadonly && !field.isConst ? 'readonly ' : '';
        const typeName = field.fieldType ? this.mapType(field.fieldType) : 'object';
        const init = field.initializer ? ` = ${this.generateExpression(field.initializer)}` : '';
        builder.line(`${visibility} ${staticMod}${constMod}${readonlyMod}${typeName} ${field.name}${init};`);
    }
    generateProperty(prop, builder) {
        const visibility = prop.visibility || 'public';
        const staticMod = prop.isStatic ? 'static ' : '';
        const typeName = this.mapType(prop.propType);
        // Attributes
        for (const attr of prop.attributes || []) {
            builder.line(`[${attr}]`);
        }
        if (prop.getter && prop.setter) {
            // Full property
            builder.line(`${visibility} ${staticMod}${typeName} ${prop.name}`);
            builder.line('{');
            builder.indent();
            builder.line('get');
            builder.line('{');
            builder.indent();
            for (const stmt of prop.getter) {
                this.generateStatement(stmt, builder);
            }
            builder.dedent();
            builder.line('}');
            builder.line('set');
            builder.line('{');
            builder.indent();
            for (const stmt of prop.setter) {
                this.generateStatement(stmt, builder);
            }
            builder.dedent();
            builder.line('}');
            builder.dedent();
            builder.line('}');
        }
        else {
            // Auto property
            const getterPart = prop.getter ? 'get; ' : '';
            const setterPart = prop.setter ? 'set; ' : 'private set; ';
            builder.line(`${visibility} ${staticMod}${typeName} ${prop.name} { ${getterPart}${setterPart}}`);
        }
    }
    generateMethod(method, builder) {
        // Attributes
        for (const attr of method.attributes || []) {
            builder.line(`[${attr}]`);
        }
        const visibility = method.visibility || 'private';
        const staticMod = method.isStatic ? 'static ' : '';
        const virtualMod = method.isVirtual ? 'virtual ' : '';
        const overrideMod = method.isOverride ? 'override ' : '';
        const asyncMod = method.isAsync ? 'async ' : '';
        // Coroutine returns IEnumerator in Unity
        const returnType = method.isCoroutine ? 'IEnumerator' : this.mapType(method.returnType);
        const params = method.params.map((p) => this.formatParameter(p)).join(', ');
        builder.line(`${visibility} ${staticMod}${virtualMod}${overrideMod}${asyncMod}${returnType} ${method.name}(${params})`);
        builder.line('{');
        builder.indent();
        for (const stmt of method.body) {
            this.generateStatement(stmt, builder);
        }
        builder.dedent();
        builder.line('}');
    }
    formatParameter(param) {
        const type = this.mapType(param.paramType);
        const defaultVal = param.defaultValue
            ? ` = ${this.generateExpression(param.defaultValue)}`
            : '';
        const variadic = param.isVariadic ? 'params ' : '';
        return `${variadic}${type} ${param.name}${defaultVal}`;
    }
    /* ── Statement Generation ───────────────────────────────────── */
    generateVarDecl(decl, builder) {
        const keyword = decl.isConst ? 'const ' : '';
        const typeName = decl.varType ? this.mapType(decl.varType) : 'var';
        const init = decl.initializer ? ` = ${this.generateExpression(decl.initializer)}` : '';
        builder.line(`${keyword}${typeName} ${decl.name}${init};`);
    }
    generateAssignment(assign, builder) {
        const target = this.generateExpression(assign.target);
        const value = this.generateExpression(assign.value);
        builder.line(`${target} ${assign.operator} ${value};`);
    }
    generateIf(stmt, builder) {
        var _a;
        const cond = this.generateExpression(stmt.condition);
        builder.line(`if (${cond})`);
        builder.line('{');
        builder.indent();
        for (const s of stmt.thenBranch) {
            this.generateStatement(s, builder);
        }
        builder.dedent();
        builder.line('}');
        for (const elseIf of stmt.elseIfs || []) {
            builder.line(`else if (${this.generateExpression(elseIf.condition)})`);
            builder.line('{');
            builder.indent();
            for (const s of elseIf.body) {
                this.generateStatement(s, builder);
            }
            builder.dedent();
            builder.line('}');
        }
        if ((_a = stmt.elseBranch) === null || _a === void 0 ? void 0 : _a.length) {
            builder.line('else');
            builder.line('{');
            builder.indent();
            for (const s of stmt.elseBranch) {
                this.generateStatement(s, builder);
            }
            builder.dedent();
            builder.line('}');
        }
    }
    generateWhile(stmt, builder) {
        const cond = this.generateExpression(stmt.condition);
        builder.line(`while (${cond})`);
        builder.line('{');
        builder.indent();
        for (const s of stmt.body) {
            this.generateStatement(s, builder);
        }
        builder.dedent();
        builder.line('}');
    }
    generateFor(stmt, builder) {
        const initBuilder = this.createBuilder();
        if (stmt.init) {
            this.generateStatement(stmt.init, initBuilder);
        }
        const init = initBuilder.toString().trim().replace(/;$/, '');
        const cond = stmt.condition ? this.generateExpression(stmt.condition) : '';
        const update = stmt.update ? this.generateExpression(stmt.update) : '';
        builder.line(`for (${init}; ${cond}; ${update})`);
        builder.line('{');
        builder.indent();
        for (const s of stmt.body) {
            this.generateStatement(s, builder);
        }
        builder.dedent();
        builder.line('}');
    }
    generateForEach(stmt, builder) {
        const iterable = this.generateExpression(stmt.iterable);
        builder.line(`foreach (var ${stmt.variable} in ${iterable})`);
        builder.line('{');
        builder.indent();
        for (const s of stmt.body) {
            this.generateStatement(s, builder);
        }
        builder.dedent();
        builder.line('}');
    }
    generateReturn(stmt, builder) {
        if (stmt.value) {
            builder.line(`return ${this.generateExpression(stmt.value)};`);
        }
        else {
            builder.line('return;');
        }
    }
    generateSwitch(stmt, builder) {
        const expr = this.generateExpression(stmt.expression);
        builder.line(`switch (${expr})`);
        builder.line('{');
        builder.indent();
        for (const c of stmt.cases) {
            builder.line(`case ${this.generateExpression(c.value)}:`);
            builder.indent();
            for (const s of c.body) {
                this.generateStatement(s, builder);
            }
            builder.line('break;');
            builder.dedent();
        }
        if (stmt.defaultCase) {
            builder.line('default:');
            builder.indent();
            for (const s of stmt.defaultCase) {
                this.generateStatement(s, builder);
            }
            builder.line('break;');
            builder.dedent();
        }
        builder.dedent();
        builder.line('}');
    }
    generateTry(stmt, builder) {
        var _a;
        builder.line('try');
        builder.line('{');
        builder.indent();
        for (const s of stmt.tryBlock) {
            this.generateStatement(s, builder);
        }
        builder.dedent();
        builder.line('}');
        for (const c of stmt.catchBlocks || []) {
            const errorType = c.errorType || 'Exception';
            const errorVar = c.errorVar || 'e';
            builder.line(`catch (${errorType} ${errorVar})`);
            builder.line('{');
            builder.indent();
            for (const s of c.body) {
                this.generateStatement(s, builder);
            }
            builder.dedent();
            builder.line('}');
        }
        if ((_a = stmt.finallyBlock) === null || _a === void 0 ? void 0 : _a.length) {
            builder.line('finally');
            builder.line('{');
            builder.indent();
            for (const s of stmt.finallyBlock) {
                this.generateStatement(s, builder);
            }
            builder.dedent();
            builder.line('}');
        }
    }
    /* ── Expression Generation ──────────────────────────────────── */
    generateLiteral(lit) {
        var _a;
        switch (lit.valueType) {
            case 'bool':
                return lit.value ? 'true' : 'false';
            case 'string':
                return `"${LanguageExporter.escapeString(String(lit.value))}"`;
            case 'float':
                return `${lit.value}f`;
            case 'int':
                return String(lit.value);
            case 'vector2': {
                const v = lit.value;
                return `new Vector2(${v.x}f, ${v.y}f)`;
            }
            case 'vector3': {
                const v = lit.value;
                return `new Vector3(${v.x}f, ${v.y}f, ${v.z}f)`;
            }
            case 'color': {
                const c = lit.value;
                return `new Color(${c.r}f, ${c.g}f, ${c.b}f, ${(_a = c.a) !== null && _a !== void 0 ? _a : 1}f)`;
            }
            default:
                return lit.value === null ? 'null' : String(lit.value);
        }
    }
    generateBinaryOp(op) {
        const left = this.generateExpression(op.left);
        const right = this.generateExpression(op.right);
        return `(${left} ${op.operator} ${right})`;
    }
    generateUnaryOp(op) {
        const operand = this.generateExpression(op.operand);
        return op.prefix ? `${op.operator}${operand}` : `${operand}${op.operator}`;
    }
    generateCall(call) {
        var _a;
        const args = call.args.map((a) => this.generateExpression(a)).join(', ');
        const typeArgs = ((_a = call.typeArgs) === null || _a === void 0 ? void 0 : _a.length)
            ? `<${call.typeArgs.map((t) => this.mapType(t)).join(', ')}>`
            : '';
        return `${call.callee}${typeArgs}(${args})`;
    }
    generateMethodCall(call) {
        const obj = this.generateExpression(call.object);
        const args = call.args.map((a) => this.generateExpression(a)).join(', ');
        return `${obj}.${call.method}(${args})`;
    }
    generatePropertyAccess(access) {
        const obj = this.generateExpression(access.object);
        return `${obj}.${access.property}`;
    }
    generateIndexAccess(access) {
        const obj = this.generateExpression(access.object);
        const idx = this.generateExpression(access.index);
        return `${obj}[${idx}]`;
    }
    generateConditional(cond) {
        const condition = this.generateExpression(cond.condition);
        const ifTrue = this.generateExpression(cond.ifTrue);
        const ifFalse = this.generateExpression(cond.ifFalse);
        return `(${condition} ? ${ifTrue} : ${ifFalse})`;
    }
    generateArrayLiteral(arr) {
        const elements = arr.elements.map((e) => this.generateExpression(e)).join(', ');
        const type = arr.elementType ? this.mapType(arr.elementType) : 'object';
        return `new List<${type}> { ${elements} }`;
    }
    generateDictionaryLiteral(dict) {
        const entries = dict.entries
            .map((e) => `{ ${this.generateExpression(e.key)}, ${this.generateExpression(e.value)} }`)
            .join(', ');
        return `new Dictionary<object, object> { ${entries} }`;
    }
    generateCast(cast) {
        const expr = this.generateExpression(cast.expression);
        const type = this.mapType(cast.targetType);
        return `(${type})${expr}`;
    }
    generateLambda(lambda) {
        const params = lambda.params
            .map((p) => {
            const type = this.mapType(p.paramType);
            return `${type} ${p.name}`;
        })
            .join(', ');
        if (Array.isArray(lambda.body)) {
            // Multi-statement lambda
            const bodyBuilder = this.createBuilder();
            for (const stmt of lambda.body) {
                this.generateStatement(stmt, bodyBuilder);
            }
            return `(${params}) => { ${bodyBuilder.toString()} }`;
        }
        else {
            // Expression lambda
            const body = this.generateExpression(lambda.body);
            return `(${params}) => ${body}`;
        }
    }
    generateNew(n) {
        const args = n.args.map((a) => this.generateExpression(a)).join(', ');
        return `new ${n.typeName}(${args})`;
    }
    /* ── Unity-specific Helpers ─────────────────────────────────── */
    formatArrayType(elementType) {
        return `List<${elementType}>`;
    }
    formatNullableType(baseType) {
        // Value types need ?
        const valueTypes = ['int', 'float', 'bool', 'Vector2', 'Vector3', 'Color'];
        return valueTypes.includes(baseType) ? `${baseType}?` : baseType;
    }
}

exports.CSharpExporter = CSharpExporter;
//# sourceMappingURL=CSharpExporter.js.map
