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

/* ────────────────────────────────────────────────────────────────
   TypeScript Exporter — Browser/Node.js Runtime
   
   Exports visual scripts to TypeScript for browser or Node.js
   execution, including the NiceToDev game engine runtime.
──────────────────────────────────────────────────────────────── */
/* ── TypeScript Type Mapping ──────────────────────────────────── */
const typescriptTypeMapping = {
    void: 'void',
    bool: 'boolean',
    int: 'number',
    float: 'number',
    string: 'string',
    vector2: 'Vec2',
    vector3: 'Vec3',
    color: 'Color',
    entity: 'EntityId',
    array: 'Array',
    dictionary: 'Map',
    any: 'unknown',
};
/* ── TypeScript Exporter ──────────────────────────────────────── */
class TypeScriptExporter extends ImperativeLanguageExporter {
    constructor(config = {}) {
        super({
            indent: '  ',
            ...config,
        });
        this.languageName = 'typescript';
        this.fileExtension = 'ts';
        this.typeMapping = typescriptTypeMapping;
        this.statementTerminator = ';';
        this.breakKeyword = 'break';
        this.continueKeyword = 'continue';
        this.target = config.targetEngine || 'nice2dev';
    }
    /* ── Main Generation ────────────────────────────────────────── */
    generateScript(script) {
        const builder = this.createBuilder();
        // File header
        if (script.headerComment) {
            builder.docComment(script.headerComment, 'jsdoc');
        }
        builder.line('/**');
        builder.line(' * Generated by NiceToDev Visual Scripting');
        builder.line(` * Target: ${this.target}`);
        builder.line(' */');
        builder.emptyLine();
        // Imports
        if (this.target === 'nice2dev') {
            builder.line("import type { EntityId, Vec2, Vec3, Color } from '@nice2dev/game-engine';");
            builder.line("import { World, System } from '@nice2dev/game-engine';");
        }
        // Custom imports
        for (const imp of script.imports) {
            this.generateImport(imp, builder);
        }
        if (script.imports.length || this.target === 'nice2dev') {
            builder.emptyLine();
        }
        // Enums
        for (const en of script.enums || []) {
            this.generateEnum(en, builder);
            builder.emptyLine();
        }
        // Classes
        for (const cls of script.classes) {
            this.generateClass(cls, builder);
            builder.emptyLine();
        }
        // Top-level functions
        for (const func of script.functions || []) {
            this.generateMethod(func, builder, false);
            builder.emptyLine();
        }
        return builder.toString();
    }
    generateImport(imp, builder) {
        var _a, _b;
        if (imp.defaultImport && ((_a = imp.names) === null || _a === void 0 ? void 0 : _a.length)) {
            builder.line(`import ${imp.defaultImport}, { ${imp.names.join(', ')} } from '${imp.module}';`);
        }
        else if (imp.defaultImport) {
            builder.line(`import ${imp.defaultImport} from '${imp.module}';`);
        }
        else if ((_b = imp.names) === null || _b === void 0 ? void 0 : _b.length) {
            builder.line(`import { ${imp.names.join(', ')} } from '${imp.module}';`);
        }
        else if (imp.alias) {
            builder.line(`import * as ${imp.alias} from '${imp.module}';`);
        }
        else {
            builder.line(`import '${imp.module}';`);
        }
    }
    generateEnum(en, builder) {
        builder.line(`export enum ${en.name} {`);
        builder.indent();
        for (const member of en.members) {
            if (member.value !== undefined) {
                builder.line(`${member.name} = ${JSON.stringify(member.value)},`);
            }
            else {
                builder.line(`${member.name},`);
            }
        }
        builder.dedent();
        builder.line('}');
    }
    generateClass(cls, builder) {
        var _a, _b, _c;
        // JSDoc comment
        if (cls.comment) {
            builder.docComment(cls.comment, 'jsdoc');
        }
        // Decorators/attributes
        for (const attr of cls.attributes || []) {
            builder.line(`@${attr}`);
        }
        // Class declaration
        const modifiers = [];
        if (cls.isAbstract)
            modifiers.push('abstract');
        const exportKeyword = 'export ';
        const baseClass = cls.baseClass ? ` extends ${cls.baseClass}` : '';
        const interfaces = ((_a = cls.interfaces) === null || _a === void 0 ? void 0 : _a.length) ? ` implements ${cls.interfaces.join(', ')}` : '';
        builder.line(`${exportKeyword}${modifiers.join(' ')} class ${cls.name}${baseClass}${interfaces} {`
            .replace(/\s+/g, ' ')
            .trim());
        builder.indent();
        // Nested enums
        for (const en of cls.enums || []) {
            builder.line(`static readonly ${en.name} = {`);
            builder.indent();
            for (const member of en.members) {
                builder.line(`${member.name}: ${(_b = member.value) !== null && _b !== void 0 ? _b : `'${member.name}'`},`);
            }
            builder.dedent();
            builder.line('} as const;');
            builder.emptyLine();
        }
        // Fields
        for (const field of cls.fields) {
            this.generateField(field, builder);
        }
        if (cls.fields.length) {
            builder.emptyLine();
        }
        // Properties
        for (const prop of cls.properties || []) {
            this.generateProperty(prop, builder);
        }
        if ((_c = cls.properties) === null || _c === void 0 ? void 0 : _c.length) {
            builder.emptyLine();
        }
        // Constructor if needed
        const instanceFields = cls.fields.filter((f) => !f.isStatic && f.initializer);
        if (instanceFields.length > 0 && !cls.methods.some((m) => m.name === 'constructor')) {
            this.generateConstructor(instanceFields, builder);
            builder.emptyLine();
        }
        // Methods
        for (let i = 0; i < cls.methods.length; i++) {
            this.generateMethod(cls.methods[i], builder, true);
            if (i < cls.methods.length - 1) {
                builder.emptyLine();
            }
        }
        builder.dedent();
        builder.line('}');
    }
    generateField(field, builder) {
        var _a;
        if (field.comment) {
            builder.line(`/** ${field.comment} */`);
        }
        // Decorators
        for (const attr of field.attributes || []) {
            builder.line(`@${attr}`);
        }
        const parts = [];
        // Visibility
        parts.push(field.visibility || 'private');
        // Modifiers
        if (field.isStatic)
            parts.push('static');
        if (field.isReadonly)
            parts.push('readonly');
        // Name and type
        const name = toLowerCamelCase(field.name);
        const type = field.fieldType ? this.mapType(field.fieldType) : 'unknown';
        const optional = ((_a = field.fieldType) === null || _a === void 0 ? void 0 : _a.nullable) ? '?' : '';
        // Initializer
        const init = field.initializer ? ` = ${this.generateExpression(field.initializer)}` : '';
        builder.line(`${parts.join(' ')} ${name}${optional}: ${type}${init};`);
    }
    generateProperty(prop, builder) {
        const visibility = prop.visibility || 'public';
        const staticMod = prop.isStatic ? 'static ' : '';
        const type = this.mapType(prop.propType);
        const name = toLowerCamelCase(prop.name);
        // Decorators
        for (const attr of prop.attributes || []) {
            builder.line(`@${attr}`);
        }
        if (prop.getter || prop.setter) {
            if (prop.getter) {
                builder.line(`${visibility} ${staticMod}get ${name}(): ${type} {`);
                builder.indent();
                for (const stmt of prop.getter) {
                    this.generateStatement(stmt, builder);
                }
                builder.dedent();
                builder.line('}');
            }
            if (prop.setter) {
                builder.line(`${visibility} ${staticMod}set ${name}(value: ${type}) {`);
                builder.indent();
                for (const stmt of prop.setter) {
                    this.generateStatement(stmt, builder);
                }
                builder.dedent();
                builder.line('}');
            }
        }
    }
    generateConstructor(fields, builder) {
        builder.line('constructor() {');
        builder.indent();
        for (const field of fields) {
            const name = toLowerCamelCase(field.name);
            if (field.initializer) {
                builder.line(`this.${name} = ${this.generateExpression(field.initializer)};`);
            }
        }
        builder.dedent();
        builder.line('}');
    }
    generateMethod(method, builder, isClassMethod = false) {
        var _a;
        // JSDoc
        if ((_a = method.attributes) === null || _a === void 0 ? void 0 : _a.length) {
            builder.line('/**');
            for (const attr of method.attributes) {
                builder.line(` * @${attr}`);
            }
            builder.line(' */');
        }
        const parts = [];
        if (isClassMethod) {
            parts.push(method.visibility || 'public');
            if (method.isStatic)
                parts.push('static');
            if (method.isAbstract)
                parts.push('abstract');
        }
        else {
            parts.push('export');
        }
        if (method.isAsync)
            parts.push('async');
        const name = method.name === 'constructor' ? 'constructor' : toLowerCamelCase(method.name);
        const params = method.params.map((p) => this.formatParameter(p)).join(', ');
        // Return type (skip for constructor)
        const returnType = method.name === 'constructor'
            ? ''
            : `: ${method.isAsync ? 'Promise<' : ''}${this.mapType(method.returnType)}${method.isAsync ? '>' : ''}`;
        if (isClassMethod) {
            builder.line(`${parts.join(' ')} ${name}(${params})${returnType} {`);
        }
        else {
            builder.line(`${parts.join(' ')} function ${name}(${params})${returnType} {`);
        }
        builder.indent();
        if (method.body.length === 0) {
            builder.line('// TODO: implement');
        }
        else {
            for (const stmt of method.body) {
                this.generateStatement(stmt, builder);
            }
        }
        builder.dedent();
        builder.line('}');
    }
    formatParameter(param) {
        const name = toLowerCamelCase(param.name);
        const type = this.mapType(param.paramType);
        const optional = param.defaultValue ? '?' : '';
        const defaultVal = param.defaultValue
            ? ` = ${this.generateExpression(param.defaultValue)}`
            : '';
        if (param.isVariadic) {
            return `...${name}: ${type}[]`;
        }
        return `${name}${optional}: ${type}${defaultVal}`;
    }
    /* ── Statement Generation ───────────────────────────────────── */
    generateVarDecl(decl, builder) {
        const keyword = decl.isConst ? 'const' : 'let';
        const name = toLowerCamelCase(decl.name);
        const typeAnnotation = decl.varType && decl.varType.type !== 'any' ? `: ${this.mapType(decl.varType)}` : '';
        const init = decl.initializer ? ` = ${this.generateExpression(decl.initializer)}` : '';
        builder.line(`${keyword} ${name}${typeAnnotation}${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.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.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.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.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.indent();
        for (const s of stmt.body) {
            this.generateStatement(s, builder);
        }
        builder.dedent();
        builder.line('}');
    }
    generateForEach(stmt, builder) {
        const iterable = this.generateExpression(stmt.iterable);
        const variable = toLowerCamelCase(stmt.variable);
        builder.line(`for (const ${variable} of ${iterable}) {`);
        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) {
        var _a;
        const expr = this.generateExpression(stmt.expression);
        builder.line(`switch (${expr}) {`);
        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 ((_a = stmt.defaultCase) === null || _a === void 0 ? void 0 : _a.length) {
            builder.line('default:');
            builder.indent();
            for (const s of stmt.defaultCase) {
                this.generateStatement(s, builder);
            }
            builder.dedent();
        }
        builder.dedent();
        builder.line('}');
    }
    generateTry(stmt, builder) {
        var _a;
        builder.line('try {');
        builder.indent();
        for (const s of stmt.tryBlock) {
            this.generateStatement(s, builder);
        }
        builder.dedent();
        builder.line('}');
        for (const c of stmt.catchBlocks || []) {
            const errorVar = c.errorVar || 'e';
            builder.line(`catch (${errorVar}) {`);
            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.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 `"${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:
                if (lit.value === null)
                    return 'null';
                if (lit.value === undefined)
                    return 'undefined';
                return 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 funcName = toLowerCamelCase(call.callee);
        const typeArgs = ((_a = call.typeArgs) === null || _a === void 0 ? void 0 : _a.length)
            ? `<${call.typeArgs.map((t) => this.mapType(t)).join(', ')}>`
            : '';
        return `${funcName}${typeArgs}(${args})`;
    }
    generateMethodCall(call) {
        const obj = this.generateExpression(call.object);
        const args = call.args.map((a) => this.generateExpression(a)).join(', ');
        const methodName = toLowerCamelCase(call.method);
        return `${obj}.${methodName}(${args})`;
    }
    generatePropertyAccess(access) {
        const obj = this.generateExpression(access.object);
        return `${obj}.${toLowerCamelCase(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(', ');
        return `[${elements}]`;
    }
    generateDictionaryLiteral(dict) {
        if (dict.entries.length === 0) {
            return 'new Map()';
        }
        const entries = dict.entries
            .map((e) => `[${this.generateExpression(e.key)}, ${this.generateExpression(e.value)}]`)
            .join(', ');
        return `new Map([${entries}])`;
    }
    generateCast(cast) {
        const expr = this.generateExpression(cast.expression);
        const type = this.mapType(cast.targetType);
        return `(${expr} as ${type})`;
    }
    generateLambda(lambda) {
        const params = lambda.params
            .map((p) => {
            const name = toLowerCamelCase(p.name);
            const type = this.mapType(p.paramType);
            return `${name}: ${type}`;
        })
            .join(', ');
        const returnType = lambda.returnType ? `: ${this.mapType(lambda.returnType)}` : '';
        if (Array.isArray(lambda.body)) {
            // Multi-statement arrow function
            const bodyBuilder = this.createBuilder();
            for (const stmt of lambda.body) {
                this.generateStatement(stmt, bodyBuilder);
            }
            return `(${params})${returnType} => { ${bodyBuilder.toString().trim()} }`;
        }
        else {
            const body = this.generateExpression(lambda.body);
            return `(${params})${returnType} => ${body}`;
        }
    }
    generateNew(n) {
        const args = n.args.map((a) => this.generateExpression(a)).join(', ');
        return `new ${n.typeName}(${args})`;
    }
    /* ── TypeScript-specific Helpers ────────────────────────────── */
    formatArrayType(elementType) {
        return `${elementType}[]`;
    }
    formatDictType(keyType, valueType) {
        return `Map<${keyType}, ${valueType}>`;
    }
    formatNullableType(baseType) {
        return `${baseType} | null`;
    }
}

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