"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.TradutorElixir = void 0;
const delegua_1 = __importDefault(require("../tipos-de-simbolos/delegua"));
/**
 * Tradutor que converte código Delégua para Elixir.
 *
 * Elixir é uma linguagem funcional, então algumas conversões são necessárias:
 * - Classes → Módulos com structs
 * - Métodos → Funções que recebem structs como primeiro parâmetro
 * - Loops imperativos → Funções recursivas ou Enum.each
 * - Variables → Bindings imutáveis
 */
class TradutorElixir {
    constructor() {
        this.indentacaoAtual = 0;
        this.moduloAtual = null;
        this.modulosConhecidos = new Set();
        this.funcoesConhecidas = new Set();
        this.atributosModulo = new Map();
        this.dentroDeMetodo = false;
        this.nomeParametroStruct = null;
        this.contadorVariavelTemporaria = 0;
    }
    /**
     * Adiciona a indentação atual (Elixir usa 2 espaços por convenção)
     */
    adicionarIndentacao() {
        return ' '.repeat(this.indentacaoAtual);
    }
    /**
     * Aumenta o nível de indentação em 2 espaços
     */
    aumentarIndentacao() {
        this.indentacaoAtual += 2;
    }
    /**
     * Diminui o nível de indentação em 2 espaços
     */
    diminuirIndentacao() {
        this.indentacaoAtual -= 2;
        if (this.indentacaoAtual < 0) {
            this.indentacaoAtual = 0;
        }
    }
    /**
     * Converte identificadores de camelCase para snake_case (convenção Elixir)
     */
    converterIdentificador(nome) {
        return nome
            .replace(/([A-Z])/g, '_$1')
            .toLowerCase()
            .replace(/^_/, '');
    }
    /**
     * Converte nomes de classes/módulos, preservando PascalCase
     */
    converterNomeModulo(nome) {
        return nome.charAt(0).toUpperCase() + nome.slice(1);
    }
    /**
     * Gera nome único para variável temporária
     */
    gerarVariavelTemporaria() {
        return `_temp_${this.contadorVariavelTemporaria++}`;
    }
    /**
     * Mapeia operadores Delégua para Elixir
     */
    traduzirOperador(simbolo) {
        const tipoSimbolo = simbolo.tipo;
        switch (tipoSimbolo) {
            // Aritméticos
            case delegua_1.default.ADICAO:
                return '+';
            case delegua_1.default.SUBTRACAO:
                return '-';
            case delegua_1.default.MULTIPLICACAO:
                return '*';
            case delegua_1.default.DIVISAO:
                return '/';
            case delegua_1.default.MODULO:
                return 'rem';
            case delegua_1.default.EXPONENCIACAO:
                return '**';
            // Comparação
            case delegua_1.default.MAIOR:
                return '>';
            case delegua_1.default.MAIOR_IGUAL:
                return '>=';
            case delegua_1.default.MENOR:
                return '<';
            case delegua_1.default.MENOR_IGUAL:
                return '<=';
            case delegua_1.default.IGUAL_IGUAL:
                return '==';
            case delegua_1.default.DIFERENTE:
                return '!=';
            // Lógicos
            case delegua_1.default.E:
                return 'and';
            case delegua_1.default.OU:
                return 'or';
            case delegua_1.default.NEGACAO:
                return 'not';
            // Bitwise
            case delegua_1.default.BIT_AND:
                return '&&&';
            case delegua_1.default.BIT_OR:
                return '|||';
            case delegua_1.default.BIT_XOR:
                return '^^^';
            case delegua_1.default.BIT_NOT:
                return '~~~';
            default:
                return simbolo.lexema;
        }
    }
    /**
     * Ponto de entrada para tradução
     */
    async traduzir(declaracoes) {
        let resultado = '';
        for (const declaracao of declaracoes) {
            const traducao = await declaracao.aceitar(this);
            if (traducao) {
                resultado += traducao + '\n';
            }
        }
        return resultado;
    }
    // ========== DECLARAÇÕES ==========
    visitarDeclaracaoCabecalhoPrograma(declaracao) {
        // Elixir não tem conceito de cabeçalho de programa
        return Promise.resolve('');
    }
    async visitarDeclaracaoClasse(declaracao) {
        const nomeModulo = this.converterNomeModulo(declaracao.simbolo.lexema);
        this.modulosConhecidos.add(nomeModulo);
        let resultado = this.adicionarIndentacao();
        resultado += `defmodule ${nomeModulo} do\n`;
        this.aumentarIndentacao();
        const moduloAnterior = this.moduloAtual;
        this.moduloAtual = nomeModulo;
        // Extrair campos do struct do construtor
        const camposStruct = await this.extrairCamposStruct(declaracao);
        if (camposStruct.length > 0) {
            resultado += this.adicionarIndentacao();
            resultado += `defstruct [${camposStruct.join(', ')}]\n\n`;
        }
        // Traduzir métodos
        for (const metodo of declaracao.metodos) {
            const traducaoMetodo = await this.traduzirMetodoClasse(metodo, nomeModulo);
            resultado += traducaoMetodo + '\n\n';
        }
        this.diminuirIndentacao();
        resultado += this.adicionarIndentacao() + 'end';
        this.moduloAtual = moduloAnterior;
        return Promise.resolve(resultado);
    }
    /**
     * Extrai nomes de campos do struct a partir do construtor da classe
     */
    async extrairCamposStruct(declaracao) {
        const campos = new Set();
        // Procurar pelo construtor
        const construtor = declaracao.metodos.find(m => m.simbolo.lexema === 'construtor' || m.simbolo.lexema === 'inicializar');
        if (!construtor) {
            return [];
        }
        // Analisar corpo do construtor para encontrar atribuições a "isto.campo"
        for (const declaracaoCorpo of construtor.funcao.corpo) {
            this.extrairCamposDeDeclaracao(declaracaoCorpo, campos);
        }
        // Converter para atoms do Elixir
        return Array.from(campos).map(c => `:${this.converterIdentificador(c)}`);
    }
    /**
     * Extrai campos de uma declaração recursivamente
     */
    extrairCamposDeDeclaracao(declaracao, campos) {
        // Se é uma expressão de atribuição com isto.campo
        if (declaracao.constructor.name === 'Expressao' && declaracao.expressao) {
            const expressao = declaracao.expressao;
            // DefinirValor: usado para isto.campo = valor
            if (expressao.constructor.name === 'DefinirValor') {
                if (expressao.objeto && expressao.objeto.constructor.name === 'Isto') {
                    campos.add(expressao.nome.lexema);
                }
            }
            // Atribuir: pode ser usado para isto.campo = valor
            if (expressao.constructor.name === 'Atribuir') {
                // Verificar se o alvo é um acesso a propriedade de isto
                if (expressao.alvo && expressao.alvo.constructor.name === 'AcessoPropriedade') {
                    const acesso = expressao.alvo;
                    if (acesso.objeto && acesso.objeto.constructor.name === 'Isto') {
                        campos.add(acesso.nomePropriedade);
                    }
                }
            }
        }
        // Se é um bloco, processar declarações internas
        if (declaracao.constructor.name === 'Bloco') {
            for (const decl of declaracao.declaracoes) {
                this.extrairCamposDeDeclaracao(decl, campos);
            }
        }
    }
    /**
     * Traduz um método de classe para função de módulo
     */
    async traduzirMetodoClasse(metodo, nomeModulo) {
        const nomeMetodo = this.converterIdentificador(metodo.simbolo.lexema);
        let resultado = this.adicionarIndentacao();
        // Construtor vira função new/N
        if (metodo.simbolo.lexema === 'construtor' || metodo.simbolo.lexema === 'inicializar') {
            resultado += `def new(`;
            const parametros = metodo.funcao.parametros.map(p => this.converterIdentificador(p.nome.lexema));
            resultado += parametros.join(', ');
            resultado += ') do\n';
            this.aumentarIndentacao();
            resultado += this.adicionarIndentacao();
            resultado += `%${nomeModulo}{`;
            // Extrair inicializações do construtor
            const inicializacoes = await this.extrairInicializacoesStruct(metodo.funcao.corpo, nomeModulo);
            resultado += inicializacoes;
            resultado += '}\n';
            this.diminuirIndentacao();
            resultado += this.adicionarIndentacao() + 'end';
        }
        else {
            // Métodos normais recebem o struct como primeiro parâmetro
            resultado += `def ${nomeMetodo}(`;
            const nomeParametroStruct = this.converterIdentificador(nomeModulo.toLowerCase());
            this.nomeParametroStruct = nomeParametroStruct;
            const parametros = [nomeParametroStruct].concat(metodo.funcao.parametros.map(p => this.converterIdentificador(p.nome.lexema)));
            resultado += parametros.join(', ');
            resultado += ') do\n';
            this.aumentarIndentacao();
            this.dentroDeMetodo = true;
            for (const declaracaoCorpo of metodo.funcao.corpo) {
                const traducao = await declaracaoCorpo.aceitar(this);
                if (traducao) {
                    resultado += traducao + '\n';
                }
            }
            this.dentroDeMetodo = false;
            this.nomeParametroStruct = null;
            this.diminuirIndentacao();
            resultado += this.adicionarIndentacao() + 'end';
        }
        return resultado;
    }
    /**
     * Extrai inicializações de struct do corpo do construtor
     */
    async extrairInicializacoesStruct(corpo, nomeModulo) {
        const inicializacoes = [];
        for (const declaracao of corpo) {
            if (declaracao.constructor.name === 'Expressao' && declaracao.expressao) {
                const expressao = declaracao.expressao;
                // DefinirValor: isto.campo = valor
                if (expressao.constructor.name === 'DefinirValor') {
                    if (expressao.objeto && expressao.objeto.constructor.name === 'Isto') {
                        const campo = this.converterIdentificador(expressao.nome.lexema);
                        const valor = await expressao.valor.aceitar(this);
                        inicializacoes.push(`${campo}: ${valor}`);
                    }
                }
                // Atribuir: pode ser isto.campo = valor (se alvo é AcessoPropriedade)
                if (expressao.constructor.name === 'Atribuir') {
                    if (expressao.alvo && expressao.alvo.constructor.name === 'AcessoPropriedade') {
                        const acesso = expressao.alvo;
                        if (acesso.objeto && acesso.objeto.constructor.name === 'Isto') {
                            const campo = this.converterIdentificador(acesso.nomePropriedade);
                            const valor = await expressao.valor.aceitar(this);
                            inicializacoes.push(`${campo}: ${valor}`);
                        }
                    }
                }
            }
        }
        return inicializacoes.length > 0 ? inicializacoes.join(', ') : '';
    }
    async visitarDeclaracaoComentario(declaracao) {
        const conteudo = Array.isArray(declaracao.conteudo)
            ? declaracao.conteudo.join('\n# ')
            : declaracao.conteudo;
        return Promise.resolve(`${this.adicionarIndentacao()}# ${conteudo}`);
    }
    async visitarDeclaracaoConst(declaracao) {
        // Em Elixir, constantes em módulos são atributos de módulo (@constante)
        // Fora de módulos, são apenas bindings normais (imutáveis por padrão)
        let resultado = this.adicionarIndentacao();
        if (this.moduloAtual) {
            // Dentro de módulo, usar atributo de módulo
            const nomeAtributo = this.converterIdentificador(declaracao.simbolo.lexema);
            resultado += `@${nomeAtributo} `;
        }
        else {
            // Fora de módulo, binding normal (mantem nome original em maiúsculas)
            resultado += declaracao.simbolo.lexema;
            resultado += ' = ';
        }
        if (declaracao.inicializador) {
            resultado += await declaracao.inicializador.aceitar(this);
        }
        else {
            resultado += 'nil';
        }
        return Promise.resolve(resultado);
    }
    visitarDeclaracaoConstMultiplo(declaracao) {
        throw new Error('Método não implementado: visitarDeclaracaoConstMultiplo');
    }
    async visitarDeclaracaoDeExpressao(declaracao) {
        const resultado = this.adicionarIndentacao() + (await declaracao.expressao.aceitar(this));
        return Promise.resolve(resultado);
    }
    async visitarDeclaracaoDefinicaoFuncao(declaracao) {
        const nomeFuncao = this.converterIdentificador(declaracao.simbolo.lexema);
        this.funcoesConhecidas.add(nomeFuncao);
        let resultado = this.adicionarIndentacao();
        resultado += `def ${nomeFuncao}(`;
        // Parâmetros
        const parametros = declaracao.funcao.parametros.map(p => this.converterIdentificador(p.nome.lexema));
        resultado += parametros.join(', ');
        resultado += ') do\n';
        // Corpo
        this.aumentarIndentacao();
        for (const declaracaoCorpo of declaracao.funcao.corpo) {
            const traducao = await declaracaoCorpo.aceitar(this);
            if (traducao) {
                resultado += traducao + '\n';
            }
        }
        this.diminuirIndentacao();
        resultado += this.adicionarIndentacao() + 'end';
        return Promise.resolve(resultado);
    }
    async visitarDeclaracaoEnquanto(declaracao) {
        // Enquanto em Elixir vira função recursiva
        // Padrão: (fn -> loop = fn when cond -> corpo; loop.() end; loop = fn -> :ok end; loop.() end).()
        let resultado = this.adicionarIndentacao();
        resultado += '(fn ->\n';
        this.aumentarIndentacao();
        // Função recursiva com guard
        resultado += this.adicionarIndentacao();
        resultado += 'loop = fn when ';
        resultado += await declaracao.condicao.aceitar(this);
        resultado += ' ->\n';
        this.aumentarIndentacao();
        const traducaoCorpo = await declaracao.corpo.aceitar(this);
        resultado += traducaoCorpo;
        // Chamada recursiva
        resultado += this.adicionarIndentacao() + 'loop.()\n';
        this.diminuirIndentacao();
        resultado += this.adicionarIndentacao() + 'end\n';
        // Caso base
        resultado += this.adicionarIndentacao() + 'loop = fn -> :ok end\n';
        resultado += this.adicionarIndentacao() + 'loop.()\n';
        this.diminuirIndentacao();
        resultado += this.adicionarIndentacao() + 'end).()';
        return Promise.resolve(resultado);
    }
    async visitarDeclaracaoEscolha(declaracao) {
        let resultado = this.adicionarIndentacao();
        resultado += 'case ';
        resultado += await declaracao.identificadorOuLiteral.aceitar(this);
        resultado += ' do\n';
        this.aumentarIndentacao();
        // Processar cada caminho
        for (const caminho of declaracao.caminhos) {
            for (const condicao of caminho.condicoes) {
                resultado += this.adicionarIndentacao();
                resultado += await condicao.aceitar(this);
                resultado += ' ->\n';
            }
            this.aumentarIndentacao();
            for (const decl of caminho.declaracoes) {
                resultado += await decl.aceitar(this);
                resultado += '\n';
            }
            this.diminuirIndentacao();
        }
        // Caminho padrão
        if (declaracao.caminhoPadrao && declaracao.caminhoPadrao.declaracoes.length > 0) {
            resultado += this.adicionarIndentacao() + '_ ->\n';
            this.aumentarIndentacao();
            for (const decl of declaracao.caminhoPadrao.declaracoes) {
                resultado += await decl.aceitar(this);
                resultado += '\n';
            }
            this.diminuirIndentacao();
        }
        this.diminuirIndentacao();
        resultado += this.adicionarIndentacao() + 'end';
        return Promise.resolve(resultado);
    }
    async visitarDeclaracaoEscreva(declaracao) {
        let resultado = this.adicionarIndentacao();
        resultado += 'IO.puts(';
        if (declaracao.argumentos.length === 0) {
            resultado += '""';
        }
        else if (declaracao.argumentos.length === 1) {
            resultado += await declaracao.argumentos[0].aceitar(this);
        }
        else {
            // Múltiplos argumentos - concatenar
            const argumentos = [];
            for (const arg of declaracao.argumentos) {
                argumentos.push(await arg.aceitar(this));
            }
            resultado += argumentos.join(' <> ');
        }
        resultado += ')';
        return Promise.resolve(resultado);
    }
    visitarDeclaracaoEscrevaMesmaLinha(declaracao) {
        throw new Error('Método não implementado: visitarDeclaracaoEscrevaMesmaLinha');
    }
    async visitarDeclaracaoFazer(declaracao) {
        // Fazer...enquanto é similar a enquanto, mas executa o corpo pelo menos uma vez
        let resultado = this.adicionarIndentacao();
        resultado += '(fn ->\n';
        this.aumentarIndentacao();
        resultado += this.adicionarIndentacao();
        resultado += 'loop = fn ->\n';
        this.aumentarIndentacao();
        // Corpo
        const traducaoCorpo = await declaracao.caminhoFazer.aceitar(this);
        resultado += traducaoCorpo;
        // Verificar condição e decidir se continua
        resultado += this.adicionarIndentacao() + 'if ';
        resultado += await declaracao.condicaoEnquanto.aceitar(this);
        resultado += ' do\n';
        this.aumentarIndentacao();
        resultado += this.adicionarIndentacao() + 'loop.()\n';
        this.diminuirIndentacao();
        resultado += this.adicionarIndentacao() + 'else\n';
        this.aumentarIndentacao();
        resultado += this.adicionarIndentacao() + ':ok\n';
        this.diminuirIndentacao();
        resultado += this.adicionarIndentacao() + 'end\n';
        this.diminuirIndentacao();
        resultado += this.adicionarIndentacao() + 'end\n';
        resultado += this.adicionarIndentacao() + 'loop.()\n';
        this.diminuirIndentacao();
        resultado += this.adicionarIndentacao() + 'end).()';
        return Promise.resolve(resultado);
    }
    visitarDeclaracaoInicioAlgoritmo(declaracao) {
        // Elixir não tem conceito de início de algoritmo
        return Promise.resolve('');
    }
    async visitarDeclaracaoPara(declaracao) {
        // Para loop vira função recursiva com inicializador, condição e incremento
        let resultado = this.adicionarIndentacao();
        resultado += '(fn ->\n';
        this.aumentarIndentacao();
        // Extrair variável e valor inicial
        let nomeVar = '';
        let valorInicial = '';
        if (declaracao.inicializador) {
            const init = Array.isArray(declaracao.inicializador)
                ? declaracao.inicializador[0]
                : declaracao.inicializador;
            if (init.constructor.name === 'Var') {
                nomeVar = this.converterIdentificador(init.simbolo.lexema);
                if (init.inicializador) {
                    valorInicial = await init.inicializador.aceitar(this);
                }
                else {
                    valorInicial = '0';
                }
            }
        }
        // Função recursiva com parâmetro e guard
        resultado += this.adicionarIndentacao();
        resultado += `loop = fn ${nomeVar} when `;
        resultado += await declaracao.condicao.aceitar(this);
        resultado += ' ->\n';
        this.aumentarIndentacao();
        const traducaoCorpo = await declaracao.corpo.aceitar(this);
        resultado += traducaoCorpo;
        // Incremento e chamada recursiva
        const incremento = await declaracao.incrementar.aceitar(this);
        resultado += this.adicionarIndentacao() + `loop.(${incremento})\n`;
        this.diminuirIndentacao();
        resultado += this.adicionarIndentacao() + 'end\n';
        // Caso base
        resultado += this.adicionarIndentacao() + `loop = fn _ -> :ok end\n`;
        resultado += this.adicionarIndentacao() + `loop.(${valorInicial})\n`;
        this.diminuirIndentacao();
        resultado += this.adicionarIndentacao() + 'end).()';
        return Promise.resolve(resultado);
    }
    async visitarDeclaracaoParaCada(declaracao) {
        let resultado = this.adicionarIndentacao();
        resultado += 'Enum.each(';
        resultado += await declaracao.vetorOuDicionario.aceitar(this);
        resultado += ', fn ';
        resultado += await declaracao.variavelIteracao.aceitar(this);
        resultado += ' ->\n';
        this.aumentarIndentacao();
        const traducaoCorpo = await declaracao.corpo.aceitar(this);
        resultado += traducaoCorpo;
        this.diminuirIndentacao();
        resultado += this.adicionarIndentacao() + 'end)';
        return Promise.resolve(resultado);
    }
    async visitarDeclaracaoSe(declaracao) {
        let resultado = this.adicionarIndentacao();
        resultado += 'if ';
        resultado += await declaracao.condicao.aceitar(this);
        resultado += ' do\n';
        this.aumentarIndentacao();
        const traducaoEntao = await declaracao.caminhoEntao.aceitar(this);
        resultado += traducaoEntao;
        this.diminuirIndentacao();
        if (declaracao.caminhoSenao) {
            resultado += this.adicionarIndentacao() + 'else\n';
            this.aumentarIndentacao();
            const traducaoSenao = await declaracao.caminhoSenao.aceitar(this);
            resultado += traducaoSenao;
            this.diminuirIndentacao();
        }
        resultado += this.adicionarIndentacao() + 'end';
        return Promise.resolve(resultado);
    }
    async visitarDeclaracaoTendoComo(declaracao) {
        throw new Error('Método não implementado: visitarDeclaracaoTendoComo');
    }
    visitarDeclaracaoTente(declaracao) {
        throw new Error('Método não implementado: visitarDeclaracaoTente');
    }
    visitarDeclaracaoTextoDocumentacao(declaracao) {
        throw new Error('Método não implementado: visitarDeclaracaoTextoDocumentacao');
    }
    async visitarDeclaracaoVar(declaracao) {
        let resultado = this.adicionarIndentacao();
        resultado += this.converterIdentificador(declaracao.simbolo.lexema);
        resultado += ' = ';
        if (declaracao.inicializador) {
            resultado += await declaracao.inicializador.aceitar(this);
        }
        else {
            resultado += 'nil';
        }
        return Promise.resolve(resultado);
    }
    visitarDeclaracaoVarMultiplo(declaracao) {
        throw new Error('Método não implementado: visitarDeclaracaoVarMultiplo');
    }
    // ========== EXPRESSÕES ==========
    async visitarExpressaoDeAtribuicao(expressao) {
        const alvo = await expressao.alvo.aceitar(this);
        const valor = await expressao.valor.aceitar(this);
        return Promise.resolve(`${alvo} = ${valor}`);
    }
    async visitarExpressaoAcessoIndiceVariavel(expressao) {
        const objeto = await expressao.entidadeChamada.aceitar(this);
        const indice = await expressao.indice.aceitar(this);
        // Em Elixir, acesso por índice usa Enum.at/2
        return Promise.resolve(`Enum.at(${objeto}, ${indice})`);
    }
    visitarExpressaoAcessoIntervaloVariavel(expressao) {
        throw new Error('Método não implementado: visitarExpressaoAcessoIntervaloVariavel');
    }
    visitarExpressaoAcessoElementoMatriz(expressao) {
        throw new Error('Método não implementado: visitarExpressaoAcessoElementoMatriz');
    }
    async visitarExpressaoAcessoMetodo(expressao) {
        const objeto = await expressao.objeto.aceitar(this);
        const metodo = this.converterIdentificador(expressao.nomeMetodo);
        // AcessoMetodo é apenas a referência ao método, não a chamada
        // A chamada é feita por visitarExpressaoDeChamada
        return Promise.resolve(`${objeto}.${metodo}`);
    }
    /**
     * Mapeia métodos built-in de Delégua para Elixir
     */
    mapearMetodoBuiltIn(metodo, objeto, argumentos) {
        switch (metodo) {
            // Array/List methods
            case 'adicionar':
            case 'empilhar':
                return argumentos.length > 0 ? `[${argumentos[0]} | ${objeto}]` : `${objeto}`;
            case 'tamanho':
                return `length(${objeto})`;
            case 'inclui':
                return argumentos.length > 0 ? `Enum.member?(${objeto}, ${argumentos[0]})` : null;
            case 'inverter':
                return `Enum.reverse(${objeto})`;
            case 'mapear':
                return argumentos.length > 0 ? `Enum.map(${objeto}, ${argumentos[0]})` : null;
            case 'filtrar':
                return argumentos.length > 0 ? `Enum.filter(${objeto}, ${argumentos[0]})` : null;
            case 'ordenar':
                return `Enum.sort(${objeto})`;
            case 'juntar':
                return argumentos.length > 0 ? `Enum.join(${objeto}, ${argumentos[0]})` : `Enum.join(${objeto})`;
            case 'fatiar':
                if (argumentos.length >= 2) {
                    return `Enum.slice(${objeto}, ${argumentos[0]}, ${argumentos[1]})`;
                }
                return null;
            case 'remover':
                return argumentos.length > 0 ? `List.delete(${objeto}, ${argumentos[0]})` : null;
            case 'somar':
                return `Enum.sum(${objeto})`;
            // String methods
            case 'maiusculo':
                return `String.upcase(${objeto})`;
            case 'minusculo':
                return `String.downcase(${objeto})`;
            case 'dividir':
                return argumentos.length > 0 ? `String.split(${objeto}, ${argumentos[0]})` : null;
            case 'substituir':
                if (argumentos.length >= 2) {
                    return `String.replace(${objeto}, ${argumentos[0]}, ${argumentos[1]})`;
                }
                return null;
            case 'aparar':
                return `String.trim(${objeto})`;
            default:
                return null;
        }
    }
    /**
     * Tenta extrair o nome do módulo de uma expressão de objeto
     */
    obterNomeModulo(objetoStr) {
        // Se o objeto é uma variável simples, assumir que o módulo tem o mesmo nome em PascalCase
        // Isso é uma heurística; em casos reais, precisaríamos de análise semântica
        const match = objetoStr.match(/^([a-z_][a-z0-9_]*)$/);
        if (match) {
            return this.converterNomeModulo(match[1]);
        }
        return objetoStr;
    }
    async visitarExpressaoAcessoMetodoOuPropriedade(expressao) {
        const objeto = await expressao.objeto.aceitar(this);
        const simbolo = this.converterIdentificador(expressao.simbolo.lexema);
        // AcessoMetodoOuPropriedade é apenas a referência, não a chamada
        // A chamada com argumentos é feita por visitarExpressaoDeChamada
        return Promise.resolve(`${objeto}.${simbolo}`);
    }
    async visitarExpressaoAcessoPropriedade(expressao) {
        const objeto = await expressao.objeto.aceitar(this);
        const propriedade = this.converterIdentificador(expressao.nomePropriedade);
        return Promise.resolve(`${objeto}.${propriedade}`);
    }
    async visitarExpressaoAgrupamento(expressao) {
        const conteudo = await expressao.expressao.aceitar(this);
        return Promise.resolve(`(${conteudo})`);
    }
    visitarExpressaoArgumentoReferenciaFuncao(expressao) {
        throw new Error('Método não implementado: visitarExpressaoArgumentoReferenciaFuncao');
    }
    visitarExpressaoAtribuicaoPorIndice(expressao) {
        throw new Error('Método não implementado: visitarExpressaoAtribuicaoPorIndice');
    }
    visitarExpressaoAtribuicaoPorIndicesMatriz(expressao) {
        throw new Error('Método não implementado: visitarExpressaoAtribuicaoPorIndicesMatriz');
    }
    async visitarExpressaoBinaria(expressao) {
        const esquerda = await expressao.esquerda.aceitar(this);
        const direita = await expressao.direita.aceitar(this);
        const operador = this.traduzirOperador(expressao.operador);
        return Promise.resolve(`${esquerda} ${operador} ${direita}`);
    }
    async visitarExpressaoBloco(declaracao) {
        let resultado = '';
        for (const decl of declaracao.declaracoes) {
            const traducao = await decl.aceitar(this);
            if (traducao) {
                resultado += traducao + '\n';
            }
        }
        return Promise.resolve(resultado);
    }
    visitarExpressaoComentario(expressao) {
        throw new Error('Método não implementado: visitarExpressaoComentario');
    }
    visitarExpressaoContinua(declaracao) {
        throw new Error('Método não implementado: visitarExpressaoContinua');
    }
    async visitarExpressaoDeChamada(expressao) {
        // Processar argumentos
        const argumentos = [];
        for (const arg of expressao.argumentos) {
            const argTraduzido = await arg.aceitar(this);
            if (argTraduzido && argTraduzido.trim() !== '') {
                argumentos.push(argTraduzido);
            }
        }
        // Verificar se é instanciação de módulo (classe)
        if (expressao.entidadeChamada.constructor.name === 'Variavel') {
            const nomeEntidade = expressao.entidadeChamada.simbolo.lexema;
            if (this.modulosConhecidos.has(this.converterNomeModulo(nomeEntidade))) {
                // Chamada de construtor de módulo
                return Promise.resolve(`${this.converterNomeModulo(nomeEntidade)}.new(${argumentos.join(', ')})`);
            }
        }
        // Verificar se é chamada de método (AcessoMetodo ou AcessoMetodoOuPropriedade)
        if (expressao.entidadeChamada.constructor.name === 'AcessoMetodo') {
            const acessoMetodo = expressao.entidadeChamada;
            const objeto = await acessoMetodo.objeto.aceitar(this);
            const metodo = this.converterIdentificador(acessoMetodo.nomeMetodo);
            // Mapear métodos built-in
            const metodoMapeado = this.mapearMetodoBuiltIn(metodo, objeto, argumentos);
            if (metodoMapeado) {
                return Promise.resolve(metodoMapeado);
            }
            // Método de módulo/struct - passar o struct como primeiro argumento
            return Promise.resolve(`${this.obterNomeModulo(objeto)}.${metodo}(${objeto}${argumentos.length > 0 ? ', ' + argumentos.join(', ') : ''})`);
        }
        if (expressao.entidadeChamada.constructor.name === 'AcessoMetodoOuPropriedade') {
            const acesso = expressao.entidadeChamada;
            const objeto = await acesso.objeto.aceitar(this);
            const simbolo = this.converterIdentificador(acesso.simbolo.lexema);
            // Mapear métodos built-in
            const metodoMapeado = this.mapearMetodoBuiltIn(simbolo, objeto, argumentos);
            if (metodoMapeado) {
                return Promise.resolve(metodoMapeado);
            }
            // Método de módulo/struct
            return Promise.resolve(`${this.obterNomeModulo(objeto)}.${simbolo}(${objeto}${argumentos.length > 0 ? ', ' + argumentos.join(', ') : ''})`);
        }
        // Chamada normal de função
        const entidadeChamada = await expressao.entidadeChamada.aceitar(this);
        return Promise.resolve(`${entidadeChamada}(${argumentos.join(', ')})`);
    }
    visitarExpressaoDefinirValor(expressao) {
        throw new Error('Método não implementado: visitarExpressaoDefinirValor');
    }
    async visitarExpressaoFuncaoConstruto(expressao) {
        let resultado = 'fn ';
        // Parâmetros
        const parametros = expressao.parametros.map(p => this.converterIdentificador(p.nome.lexema));
        resultado += parametros.join(', ');
        resultado += ' ->';
        // Corpo - se for uma única expressão, inline; se for bloco, multi-linha
        if (expressao.corpo.length === 1) {
            resultado += ' ';
            const traducao = await expressao.corpo[0].aceitar(this);
            resultado += traducao;
        }
        else {
            resultado += '\n';
            this.aumentarIndentacao();
            for (const decl of expressao.corpo) {
                const traducao = await decl.aceitar(this);
                if (traducao) {
                    resultado += traducao + '\n';
                }
            }
            this.diminuirIndentacao();
            resultado += this.adicionarIndentacao();
        }
        resultado += ' end';
        return Promise.resolve(resultado);
    }
    async visitarExpressaoDeVariavel(expressao) {
        return Promise.resolve(this.converterIdentificador(expressao.simbolo.lexema));
    }
    async visitarExpressaoDicionario(expressao) {
        if (expressao.chaves.length === 0) {
            return Promise.resolve('%{}');
        }
        const pares = [];
        for (let i = 0; i < expressao.chaves.length; i++) {
            const chave = await expressao.chaves[i].aceitar(this);
            const valor = await expressao.valores[i].aceitar(this);
            // Ignorar pares vazios (separadores)
            if (chave && chave.trim() !== '' && valor && valor.trim() !== '') {
                // Em Elixir, usa-se atoms (:chave) quando possível ou string => valor
                // Por simplicidade, vamos usar sempre a sintaxe de string
                pares.push(`${chave} => ${valor}`);
            }
        }
        return Promise.resolve(`%{${pares.join(', ')}}`);
    }
    visitarExpressaoExpressaoRegular(expressao) {
        throw new Error('Método não implementado: visitarExpressaoExpressaoRegular');
    }
    visitarExpressaoFalhar(expressao) {
        throw new Error('Método não implementado: visitarExpressaoFalhar');
    }
    visitarExpressaoFimPara(declaracao) {
        throw new Error('Método não implementado: visitarExpressaoFimPara');
    }
    visitarExpressaoFormatacaoEscrita(declaracao) {
        throw new Error('Método não implementado: visitarExpressaoFormatacaoEscrita');
    }
    async visitarExpressaoIsto(expressao) {
        // "isto" em Elixir é substituído pelo nome do parâmetro do struct
        if (this.nomeParametroStruct) {
            return Promise.resolve(this.nomeParametroStruct);
        }
        // Se não estamos em contexto de método, usar nome genérico
        return Promise.resolve('self');
    }
    async visitarExpressaoLeia(expressao) {
        let resultado = 'IO.gets(';
        if (expressao.argumentos && expressao.argumentos.length > 0) {
            resultado += await expressao.argumentos[0].aceitar(this);
        }
        else {
            resultado += '""';
        }
        resultado += ') |> String.trim()';
        return Promise.resolve(resultado);
    }
    async visitarExpressaoLiteral(expressao) {
        const valor = expressao.valor;
        // Null/nulo
        if (valor === null || valor === undefined) {
            return Promise.resolve('nil');
        }
        // Boolean
        if (typeof valor === 'boolean') {
            return Promise.resolve(valor ? 'true' : 'false');
        }
        // Number
        if (typeof valor === 'number') {
            return Promise.resolve(String(valor));
        }
        // String
        if (typeof valor === 'string') {
            // Elixir suporta interpolação com #{}
            return Promise.resolve(`"${valor}"`);
        }
        return Promise.resolve(String(valor));
    }
    async visitarExpressaoLogica(expressao) {
        const esquerda = await expressao.esquerda.aceitar(this);
        const direita = await expressao.direita.aceitar(this);
        const operador = this.traduzirOperador(expressao.operador);
        return Promise.resolve(`${esquerda} ${operador} ${direita}`);
    }
    visitarExpressaoReferenciaFuncao(expressao) {
        throw new Error('Método não implementado: visitarExpressaoReferenciaFuncao');
    }
    async visitarExpressaoRetornar(expressao) {
        // Em Elixir, o retorno é implícito (última expressão)
        // Mas podemos usar explicitamente para clareza ou retorno antecipado
        let resultado = this.adicionarIndentacao();
        if (expressao.valor) {
            // Apenas retornar o valor, pois em Elixir a última expressão é o retorno
            resultado += await expressao.valor.aceitar(this);
        }
        else {
            resultado += 'nil';
        }
        return Promise.resolve(resultado);
    }
    visitarExpressaoSeparador(expressao) {
        return Promise.resolve('');
    }
    visitarExpressaoSuper(expressao) {
        throw new Error('Método não implementado: visitarExpressaoSuper');
    }
    visitarExpressaoSustar(declaracao) {
        throw new Error('Método não implementado: visitarExpressaoSustar');
    }
    async visitarExpressaoTupla(expressao) {
        // Tupla pode ter apenas um valor (expressao.valor) ou ser TuplaN com elementos
        // Por enquanto, apenas retornar o valor se houver
        if (expressao.valor !== undefined) {
            return Promise.resolve(`{${expressao.valor}}`);
        }
        // Se não houver valor, tupla vazia
        return Promise.resolve('{}');
    }
    async visitarExpressaoTuplaN(expressao) {
        const valores = [];
        for (const elemento of expressao.elementos) {
            const valorTraduzido = await elemento.aceitar(this);
            valores.push(valorTraduzido);
        }
        return Promise.resolve(`{${valores.join(', ')}}`);
    }
    visitarExpressaoTipoDe(expressao) {
        throw new Error('Método não implementado: visitarExpressaoTipoDe');
    }
    async visitarExpressaoUnaria(expressao) {
        const operando = await expressao.operando.aceitar(this);
        const operador = this.traduzirOperador(expressao.operador);
        // Elixir não tem ++ ou --, então operações de incremento/decremento precisam ser convertidas
        if (expressao.operador.tipo === delegua_1.default.INCREMENTAR) {
            return Promise.resolve(`${operando} + 1`);
        }
        if (expressao.operador.tipo === delegua_1.default.DECREMENTAR) {
            return Promise.resolve(`${operando} - 1`);
        }
        // Operações unárias normais (-, !, ~)
        if (expressao.incidenciaOperador === 'ANTES') {
            return Promise.resolve(`${operador} ${operando}`);
        }
        else {
            return Promise.resolve(`${operando} ${operador}`);
        }
    }
    async visitarExpressaoVetor(expressao) {
        if (expressao.valores.length === 0) {
            return Promise.resolve('[]');
        }
        const valores = [];
        for (const valor of expressao.valores) {
            const valorTraduzido = await valor.aceitar(this);
            // Ignorar separadores vazios
            if (valorTraduzido && valorTraduzido.trim() !== '') {
                valores.push(valorTraduzido);
            }
        }
        return Promise.resolve(`[${valores.join(', ')}]`);
    }
}
exports.TradutorElixir = TradutorElixir;
//# sourceMappingURL=tradutor-elixir.js.map