'use strict';

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

/* ────────────────────────────────────────────────────────────────
   Python Exporter — Pygame / General Python
   
   Exports visual scripts to Python for Pygame and other
   Python-based game development frameworks.
──────────────────────────────────────────────────────────────── */
/* ── Python Type Mapping (for type hints) ─────────────────────── */
const pythonTypeMapping = {
    void: 'None',
    bool: 'bool',
    int: 'int',
    float: 'float',
    string: 'str',
    vector2: 'pygame.math.Vector2',
    vector3: 'pygame.math.Vector3',
    color: 'pygame.Color',
    entity: 'GameObject',
    array: 'list',
    dictionary: 'dict',
    any: 'Any',
};
/* ── Python Exporter ──────────────────────────────────────────── */
class PythonExporter extends LanguageExporter.ImperativeLanguageExporter {
    constructor(config = {}) {
        super({
            indent: '    ',
            ...config,
        });
        this.languageName = 'python';
        this.fileExtension = 'py';
        this.typeMapping = pythonTypeMapping;
        this.statementTerminator = '';
        this.breakKeyword = 'break';
        this.continueKeyword = 'continue';
        this.target = config.targetEngine || 'pygame';
    }
    /* ── Main Generation ────────────────────────────────────────── */
    generateScript(script) {
        const builder = this.createBuilder();
        // File header
        if (script.headerComment) {
            builder.docComment(script.headerComment, 'python');
        }
        builder.line('"""');
        builder.line('Generated by NiceToDev Visual Scripting');
        builder.line(`Target: ${this.target}`);
        builder.line('"""');
        builder.emptyLine();
        // Standard imports
        builder.line('from __future__ import annotations');
        builder.line('from typing import Any, Optional, List, Dict, Callable');
        builder.line('from dataclasses import dataclass, field');
        if (this.target === 'pygame') {
            builder.line('import pygame');
            builder.line('from pygame import Vector2, Vector3');
        }
        builder.emptyLine();
        // Custom imports
        for (const imp of script.imports) {
            this.generateImport(imp, builder);
        }
        if (script.imports.length) {
            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);
            builder.emptyLine();
        }
        return builder.toString();
    }
    generateImport(imp, builder) {
        var _a;
        if ((_a = imp.names) === null || _a === void 0 ? void 0 : _a.length) {
            builder.line(`from ${imp.module} import ${imp.names.join(', ')}`);
        }
        else if (imp.alias) {
            builder.line(`import ${imp.module} as ${imp.alias}`);
        }
        else {
            builder.line(`import ${imp.module}`);
        }
    }
    generateEnum(en, builder) {
        builder.line('from enum import Enum, auto');
        builder.emptyLine();
        builder.line(`class ${en.name}(Enum):`);
        builder.indent();
        for (const member of en.members) {
            if (member.value !== undefined) {
                builder.line(`${member.name} = ${member.value}`);
            }
            else {
                builder.line(`${member.name} = auto()`);
            }
        }
        builder.dedent();
    }
    generateClass(cls, builder) {
        // Class docstring
        if (cls.comment) {
            builder.docComment(cls.comment, 'python');
        }
        // Decorator for dataclass (optional)
        const useDataclass = cls.fields.filter((f) => !f.isStatic).length > 0;
        if (useDataclass) {
            builder.line('@dataclass');
        }
        // Class declaration
        const baseClass = cls.baseClass || this.config.baseClassName;
        const bases = baseClass ? `(${baseClass})` : '';
        builder.line(`class ${cls.name}${bases}:`);
        builder.indent();
        // Nested enums
        for (const en of cls.enums || []) {
            this.generateEnum(en, builder);
            builder.emptyLine();
        }
        // Class-level docstring
        if (cls.comment && !useDataclass) {
            builder.docComment(cls.comment, 'python');
        }
        // Fields (as class or instance attributes)
        const classFields = cls.fields.filter((f) => f.isStatic || f.isConst);
        const instanceFields = cls.fields.filter((f) => !f.isStatic && !f.isConst);
        // Class-level constants/statics
        for (const field of classFields) {
            this.generateField(field, builder);
        }
        if (classFields.length) {
            builder.emptyLine();
        }
        // Instance fields (dataclass style)
        if (useDataclass) {
            for (const field of instanceFields) {
                this.generateDataclassField(field, builder);
            }
            if (instanceFields.length) {
                builder.emptyLine();
            }
        }
        // Generate __init__ if not using dataclass
        if (!useDataclass && instanceFields.length > 0) {
            this.generateInit(instanceFields, builder);
            builder.emptyLine();
        }
        // Methods
        if (cls.methods.length === 0 && cls.fields.length === 0) {
            builder.line('pass');
        }
        else {
            for (const method of cls.methods) {
                this.generateMethod(method, builder, true);
                builder.emptyLine();
            }
        }
        builder.dedent();
    }
    generateInit(fields, builder) {
        builder.line('def __init__(self):');
        builder.indent();
        for (const field of fields) {
            const name = LanguageExporter.toSnakeCase(field.name);
            const init = field.initializer ? this.generateExpression(field.initializer) : 'None';
            builder.line(`self.${name} = ${init}`);
        }
        builder.dedent();
    }
    generateDataclassField(field, builder) {
        var _a, _b;
        const name = LanguageExporter.toSnakeCase(field.name);
        const typeHint = field.fieldType ? `: ${this.mapType(field.fieldType)}` : '';
        if (field.initializer) {
            const init = this.generateExpression(field.initializer);
            // Use field() for mutable defaults
            if (((_a = field.fieldType) === null || _a === void 0 ? void 0 : _a.type) === 'array' || ((_b = field.fieldType) === null || _b === void 0 ? void 0 : _b.type) === 'dictionary') {
                builder.line(`${name}${typeHint} = field(default_factory=lambda: ${init})`);
            }
            else {
                builder.line(`${name}${typeHint} = ${init}`);
            }
        }
        else {
            builder.line(`${name}${typeHint} = None`);
        }
    }
    generateField(field, builder) {
        if (field.comment) {
            builder.comment(field.comment);
        }
        const name = field.isConst ? LanguageExporter.toSnakeCase(field.name).toUpperCase() : LanguageExporter.toSnakeCase(field.name);
        const typeHint = field.fieldType ? `: ${this.mapType(field.fieldType)}` : '';
        const init = field.initializer ? ` = ${this.generateExpression(field.initializer)}` : ' = None';
        builder.line(`${name}${typeHint}${init}`);
    }
    generateMethod(method, builder, isClassMethod = false) {
        const name = this.convertMethodName(method.name);
        // Decorators
        if (method.isStatic) {
            builder.line('@staticmethod');
        }
        // Async decorator
        const asyncPrefix = method.isAsync ? 'async ' : '';
        // Parameters
        const params = [];
        if (isClassMethod && !method.isStatic) {
            params.push('self');
        }
        params.push(...method.params.map((p) => this.formatParameter(p)));
        // Return type
        const returnType = method.returnType.type !== 'void' ? ` -> ${this.mapType(method.returnType)}` : ' -> None';
        builder.line(`${asyncPrefix}def ${name}(${params.join(', ')})${returnType}:`);
        builder.indent();
        if (method.body.length === 0) {
            builder.line('pass');
        }
        else {
            for (const stmt of method.body) {
                this.generateStatement(stmt, builder);
            }
        }
        builder.dedent();
    }
    formatParameter(param) {
        const name = LanguageExporter.toSnakeCase(param.name);
        const typeHint = param.paramType.type !== 'any' ? `: ${this.mapType(param.paramType)}` : '';
        const defaultVal = param.defaultValue
            ? ` = ${this.generateExpression(param.defaultValue)}`
            : '';
        if (param.isVariadic) {
            return `*${name}${typeHint}`;
        }
        return `${name}${typeHint}${defaultVal}`;
    }
    convertMethodName(name) {
        // Convert Unity/common lifecycle methods to Pygame equivalents
        if (this.target === 'pygame') {
            const methodMap = {
                Start: 'on_start',
                Update: 'update',
                Draw: 'draw',
                OnDestroy: 'on_destroy',
            };
            return methodMap[name] || LanguageExporter.toSnakeCase(name);
        }
        return LanguageExporter.toSnakeCase(name);
    }
    /* ── Statement Generation ───────────────────────────────────── */
    generateVarDecl(decl, builder) {
        const name = LanguageExporter.toSnakeCase(decl.name);
        const typeHint = decl.varType && decl.varType.type !== 'any' ? `: ${this.mapType(decl.varType)}` : '';
        const init = decl.initializer ? ` = ${this.generateExpression(decl.initializer)}` : ' = None';
        builder.line(`${name}${typeHint}${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();
        if (stmt.thenBranch.length === 0) {
            builder.line('pass');
        }
        else {
            for (const s of stmt.thenBranch) {
                this.generateStatement(s, builder);
            }
        }
        builder.dedent();
        for (const elseIf of stmt.elseIfs || []) {
            builder.line(`elif ${this.generateExpression(elseIf.condition)}:`);
            builder.indent();
            if (elseIf.body.length === 0) {
                builder.line('pass');
            }
            else {
                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();
        }
    }
    generateWhile(stmt, builder) {
        const cond = this.generateExpression(stmt.condition);
        builder.line(`while ${cond}:`);
        builder.indent();
        if (stmt.body.length === 0) {
            builder.line('pass');
        }
        else {
            for (const s of stmt.body) {
                this.generateStatement(s, builder);
            }
        }
        builder.dedent();
    }
    generateFor(stmt, builder) {
        // Convert to Python while loop
        if (stmt.init) {
            this.generateStatement(stmt.init, builder);
        }
        const cond = stmt.condition ? this.generateExpression(stmt.condition) : 'True';
        builder.line(`while ${cond}:`);
        builder.indent();
        for (const s of stmt.body) {
            this.generateStatement(s, builder);
        }
        if (stmt.update) {
            builder.line(this.generateExpression(stmt.update));
        }
        builder.dedent();
    }
    generateForEach(stmt, builder) {
        const iterable = this.generateExpression(stmt.iterable);
        const variable = LanguageExporter.toSnakeCase(stmt.variable);
        builder.line(`for ${variable} in ${iterable}:`);
        builder.indent();
        if (stmt.body.length === 0) {
            builder.line('pass');
        }
        else {
            for (const s of stmt.body) {
                this.generateStatement(s, builder);
            }
        }
        builder.dedent();
    }
    generateReturn(stmt, builder) {
        if (stmt.value) {
            builder.line(`return ${this.generateExpression(stmt.value)}`);
        }
        else {
            builder.line('return');
        }
    }
    generateSwitch(stmt, builder) {
        var _a;
        // Python 3.10+ has match, use if-elif chain for compatibility
        const expr = this.generateExpression(stmt.expression);
        const tempVar = '_match_val';
        builder.line(`${tempVar} = ${expr}`);
        let first = true;
        for (const c of stmt.cases) {
            const caseVal = this.generateExpression(c.value);
            const keyword = first ? 'if' : 'elif';
            builder.line(`${keyword} ${tempVar} == ${caseVal}:`);
            builder.indent();
            if (c.body.length === 0) {
                builder.line('pass');
            }
            else {
                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();
        }
    }
    generateTry(stmt, builder) {
        var _a;
        builder.line('try:');
        builder.indent();
        if (stmt.tryBlock.length === 0) {
            builder.line('pass');
        }
        else {
            for (const s of stmt.tryBlock) {
                this.generateStatement(s, builder);
            }
        }
        builder.dedent();
        for (const c of stmt.catchBlocks || []) {
            const errorType = c.errorType || 'Exception';
            const errorVar = c.errorVar ? ` as ${c.errorVar}` : '';
            builder.line(`except ${errorType}${errorVar}:`);
            builder.indent();
            if (c.body.length === 0) {
                builder.line('pass');
            }
            else {
                for (const s of c.body) {
                    this.generateStatement(s, builder);
                }
            }
            builder.dedent();
        }
        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();
        }
    }
    /* ── Expression Generation ──────────────────────────────────── */
    generateLiteral(lit) {
        var _a, _b;
        switch (lit.valueType) {
            case 'bool':
                return lit.value ? 'True' : 'False';
            case 'string':
                return `"${LanguageExporter.escapeString(String(lit.value))}"`;
            case 'float':
            case 'int':
                return String(lit.value);
            case 'vector2': {
                const v = lit.value;
                return this.target === 'pygame'
                    ? `pygame.math.Vector2(${v.x}, ${v.y})`
                    : `(${v.x}, ${v.y})`;
            }
            case 'vector3': {
                const v = lit.value;
                return this.target === 'pygame'
                    ? `pygame.math.Vector3(${v.x}, ${v.y}, ${v.z})`
                    : `(${v.x}, ${v.y}, ${v.z})`;
            }
            case 'color': {
                const c = lit.value;
                return this.target === 'pygame'
                    ? `pygame.Color(${Math.round(c.r * 255)}, ${Math.round(c.g * 255)}, ${Math.round(c.b * 255)}, ${Math.round(((_a = c.a) !== null && _a !== void 0 ? _a : 1) * 255)})`
                    : `(${c.r}, ${c.g}, ${c.b}, ${(_b = c.a) !== null && _b !== void 0 ? _b : 1})`;
            }
            default:
                return lit.value === null ? 'None' : String(lit.value);
        }
    }
    generateBinaryOp(op) {
        const left = this.generateExpression(op.left);
        const right = this.generateExpression(op.right);
        // Python operator mapping
        let operator = op.operator;
        switch (op.operator) {
            case '&&':
                operator = 'and';
                break;
            case '||':
                operator = 'or';
                break;
        }
        return `(${left} ${operator} ${right})`;
    }
    generateUnaryOp(op) {
        const operand = this.generateExpression(op.operand);
        // Python 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 = LanguageExporter.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 = LanguageExporter.toSnakeCase(call.method);
        return `${obj}.${methodName}(${args})`;
    }
    generatePropertyAccess(access) {
        const obj = this.generateExpression(access.object);
        return `${obj}.${LanguageExporter.toSnakeCase(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);
        // Python ternary
        return `${ifTrue} if ${condition} else ${ifFalse}`;
    }
    generateArrayLiteral(arr) {
        const elements = arr.elements.map((e) => this.generateExpression(e)).join(', ');
        return `[${elements}]`;
    }
    generateDictionaryLiteral(dict) {
        const entries = dict.entries
            .map((e) => `${this.generateExpression(e.key)}: ${this.generateExpression(e.value)}`)
            .join(', ');
        return `{${entries}}`;
    }
    generateCast(cast) {
        const expr = this.generateExpression(cast.expression);
        const type = this.mapType(cast.targetType);
        // Python uses constructor-style casting for primitives
        const primitives = ['int', 'float', 'str', 'bool'];
        const typeLower = type.toLowerCase();
        if (primitives.includes(typeLower)) {
            return `${typeLower}(${expr})`;
        }
        return expr;
    }
    generateLambda(lambda) {
        const params = lambda.params.map((p) => LanguageExporter.toSnakeCase(p.name)).join(', ');
        if (Array.isArray(lambda.body)) {
            // Python lambdas can only be single expressions
            this.addWarning('Multi-statement lambdas not supported in Python, using single expression');
            return `lambda ${params}: None`;
        }
        else {
            const body = this.generateExpression(lambda.body);
            return `lambda ${params}: ${body}`;
        }
    }
    generateNew(n) {
        const args = n.args.map((a) => this.generateExpression(a)).join(', ');
        return `${n.typeName}(${args})`;
    }
    /* ── Python-specific Helpers ────────────────────────────────── */
    formatArrayType(elementType) {
        return `List[${elementType}]`;
    }
    formatDictType(keyType, valueType) {
        return `Dict[${keyType}, ${valueType}]`;
    }
    formatNullableType(baseType) {
        return `Optional[${baseType}]`;
    }
}

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