"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.TradutorMermaidJs = void 0;
const construtos_1 = require("../construtos");
const mermaid_1 = require("./mermaid");
const delegua_1 = __importDefault(require("../tipos-de-simbolos/delegua"));
/**
 * [MermaidJs](https://mermaid.js.org/) é uma especificação que nos permite
 * criar fluxogramas através de uma notação por texto.
 *
 * Este tradutor converte estruturas da avaliação sintática em um fluxograma
 * compatível com o MermaidJs.
 *
 * Diferentemente de outros tradutores, este não trabalha diretamente com `string`s.
 * Construtos sim devolvem `string`s, mas declarações devolvem um vetor de
 * `VerticeFluxograma`.
 * @see VerticeFluxograma
 */
class TradutorMermaidJs {
    visitarDeclaracaoCabecalhoPrograma(declaracao) {
        throw new Error('Método não implementado.');
    }
    async visitarDeclaracaoClasse(declaracao) {
        var _a, _b;
        const nomeClasse = declaracao.simbolo.lexema;
        const superClasse = declaracao.superClasse
            ? (((_a = declaracao.superClasse.simbolo) === null || _a === void 0 ? void 0 : _a.lexema) || ((_b = declaracao.superClasse.nome) === null || _b === void 0 ? void 0 : _b.lexema))
            : undefined;
        const linha = declaracao.linha;
        // Cria arestas de entrada e saída para a classe
        const textoInicio = `Classe${nomeClasse}Inicio[Início: Classe ${nomeClasse}]`;
        const arestaInicial = new mermaid_1.ArestaFluxograma(declaracao, textoInicio);
        const textoFim = `Classe${nomeClasse}Fim[Fim: Classe ${nomeClasse}]`;
        const arestaFinal = new mermaid_1.ArestaFluxograma(declaracao, textoFim);
        // Cria o subgrafo da classe
        const subgrafo = new mermaid_1.SubgrafoClasse(nomeClasse, linha, arestaInicial, arestaFinal, superClasse);
        // Salva o estado anterior
        const anterioresAntes = [...this.anteriores];
        // Processa métodos
        if (declaracao.metodos && declaracao.metodos.length > 0) {
            for (const metodoDeclaracao of declaracao.metodos) {
                const nomeMetodo = metodoDeclaracao.simbolo.lexema;
                const linhaMetodo = metodoDeclaracao.linha;
                const ehConstrutor = nomeMetodo === 'construtor' || nomeMetodo === 'iniciar';
                // Cria arestas de entrada e saída para o método
                const textoInicioMetodo = `Metodo${nomeMetodo}${nomeClasse}Inicio[Início: ${nomeMetodo}()]`;
                const arestaInicialMetodo = new mermaid_1.ArestaFluxograma(metodoDeclaracao, textoInicioMetodo);
                const textoFimMetodo = `Metodo${nomeMetodo}${nomeClasse}Fim[Fim: ${nomeMetodo}()]`;
                const arestaFinalMetodo = new mermaid_1.ArestaFluxograma(metodoDeclaracao, textoFimMetodo);
                // Cria o subgrafo do método
                const subgrafoMetodo = new mermaid_1.SubgrafoMetodo(nomeMetodo, nomeClasse, linhaMetodo, arestaInicialMetodo, arestaFinalMetodo, ehConstrutor);
                // Traduz o corpo do método
                this.anteriores = [arestaInicialMetodo];
                if (metodoDeclaracao.funcao.corpo && metodoDeclaracao.funcao.corpo.length > 0) {
                    for (const declaracaoCorpo of metodoDeclaracao.funcao.corpo) {
                        const verticesCorpo = await declaracaoCorpo.aceitar(this);
                        subgrafoMetodo.vertices = subgrafoMetodo.vertices.concat(verticesCorpo);
                    }
                }
                // Conecta o último vértice do corpo ao fim do método
                if (this.anteriores.length > 0) {
                    for (const anterior of this.anteriores) {
                        subgrafoMetodo.vertices.push(new mermaid_1.VerticeFluxograma(anterior, arestaFinalMetodo));
                    }
                }
                // Adiciona o método ao subgrafo da classe
                if (ehConstrutor) {
                    subgrafo.construtor = subgrafoMetodo;
                }
                else {
                    subgrafo.metodos.push(subgrafoMetodo);
                }
            }
        }
        // Restaura o estado anterior
        this.anteriores = anterioresAntes;
        // Armazena o subgrafo da classe
        this.declaracoesClasses[nomeClasse] = subgrafo;
        return Promise.resolve([]);
    }
    async visitarDeclaracaoComentario(declaracao) {
        return Promise.resolve('');
    }
    async visitarDeclaracaoConst(declaracao) {
        let texto = `Linha${declaracao.linha}(variável: ${declaracao.simbolo.lexema}`;
        texto += await this.logicaComumTraducaoVarEConst(declaracao, texto);
        const aresta = new mermaid_1.ArestaFluxograma(declaracao, texto);
        const vertices = this.logicaComumConexaoArestas(aresta);
        this.anteriores.push(aresta);
        return Promise.resolve(vertices);
    }
    visitarDeclaracaoConstMultiplo(declaracao) {
        throw new Error('Método não implementado.');
    }
    async visitarDeclaracaoDeExpressao(declaracao) {
        // Verifica se é uma chamada de função
        if (declaracao.expressao.constructor === construtos_1.Chamada) {
            const chamada = declaracao.expressao;
            const verticesChamada = await this.traduzirChamadaFuncao(declaracao, chamada);
            if (verticesChamada.length > 0) {
                return Promise.resolve(verticesChamada);
            }
        }
        // Se não for uma chamada de função ou não for uma função conhecida,
        // trata como expressão normal
        let texto = `Linha${declaracao.linha}(`;
        const textoConstruto = await declaracao.expressao.aceitar(this);
        texto += textoConstruto + ')';
        const aresta = new mermaid_1.ArestaFluxograma(declaracao, texto);
        const vertices = this.logicaComumConexaoArestas(aresta);
        this.anteriores.push(aresta);
        return Promise.resolve(vertices);
    }
    async visitarDeclaracaoDefinicaoFuncao(declaracao) {
        const nomeFuncao = declaracao.simbolo.lexema;
        const linha = declaracao.linha;
        // Cria arestas de entrada e saída para a função
        const textoInicio = `Func${nomeFuncao}Inicio[Início: ${nomeFuncao}()]`;
        const arestaInicial = new mermaid_1.ArestaFluxograma(declaracao, textoInicio);
        const textoFim = `Func${nomeFuncao}Fim[Fim: ${nomeFuncao}()]`;
        const arestaFinal = new mermaid_1.ArestaFluxograma(declaracao, textoFim);
        // Cria o subgrafo da função
        const subgrafo = new mermaid_1.SubgrafoFuncao(nomeFuncao, linha, arestaInicial, arestaFinal);
        // Salva o estado atual de anteriores
        const anterioresAntes = [...this.anteriores];
        this.anteriores = [arestaInicial];
        // Processa o corpo da função
        if (declaracao.funcao.corpo && declaracao.funcao.corpo.length > 0) {
            for (const declaracaoCorpo of declaracao.funcao.corpo) {
                const verticesCorpo = await declaracaoCorpo.aceitar(this);
                subgrafo.vertices = subgrafo.vertices.concat(verticesCorpo);
            }
        }
        // Conecta o fim do corpo à aresta final
        if (this.anteriores.length > 0) {
            for (const anterior of this.anteriores) {
                subgrafo.vertices.push(new mermaid_1.VerticeFluxograma(anterior, arestaFinal));
            }
        }
        // Restaura o estado anterior
        this.anteriores = anterioresAntes;
        // Armazena o subgrafo
        this.declaracoesFuncoes[nomeFuncao] = subgrafo;
        // Não adiciona ao fluxo principal
        return Promise.resolve([]);
    }
    async visitarDeclaracaoEnquanto(declaracao) {
        let texto = `Linha${declaracao.linha}(enquanto `;
        const condicao = await declaracao.condicao.aceitar(this);
        texto += condicao + ')';
        const aresta = new mermaid_1.ArestaFluxograma(declaracao, texto);
        let vertices = this.logicaComumConexaoArestas(aresta);
        this.anteriores.push(aresta);
        // Corpo, normalmente um `Bloco`.
        const verticesCorpo = await declaracao.corpo.aceitar(this);
        vertices = vertices.concat(verticesCorpo);
        const ultimaArestaCorpo = verticesCorpo[verticesCorpo.length - 1].destino;
        const verticeLaco = new mermaid_1.VerticeFluxograma(ultimaArestaCorpo, aresta);
        vertices.push(verticeLaco);
        return Promise.resolve(vertices);
    }
    async visitarDeclaracaoEscolha(declaracao) {
        let texto = `Linha${declaracao.linha}(escolha um caminho pelo valor de `;
        const textoIdentificadorOuLiteral = await declaracao.identificadorOuLiteral.aceitar(this);
        texto += textoIdentificadorOuLiteral + ')';
        const aresta = new mermaid_1.ArestaFluxograma(declaracao, texto);
        let vertices = this.logicaComumConexaoArestas(aresta);
        const arestasCaminho = [];
        for (const caminho of declaracao.caminhos) {
            arestasCaminho.push(await this.logicaComumCaminhoEscolha(declaracao, caminho, caminho.condicoes[0].linha, textoIdentificadorOuLiteral, false));
        }
        if (declaracao.caminhoPadrao) {
            arestasCaminho.push(await this.logicaComumCaminhoEscolha(declaracao, declaracao.caminhoPadrao, declaracao.caminhoPadrao.declaracoes[0].linha - 1, textoIdentificadorOuLiteral, true));
        }
        for (const conjunto of Object.values(arestasCaminho)) {
            const verticeEscolhaECaminho = new mermaid_1.VerticeFluxograma(aresta, conjunto.caminho);
            vertices.push(verticeEscolhaECaminho);
            vertices = vertices.concat(conjunto.declaracoesCaminho);
            this.anteriores.push(conjunto.declaracoesCaminho[conjunto.declaracoesCaminho.length - 1].destino);
        }
        return Promise.resolve(vertices);
    }
    async visitarDeclaracaoEscreva(declaracao) {
        let texto = `Linha${declaracao.linha}(escreva: `;
        for (const argumento of declaracao.argumentos) {
            const valor = await argumento.aceitar(this);
            texto += valor + ', ';
        }
        texto = texto.slice(0, -2);
        texto += ')';
        const aresta = new mermaid_1.ArestaFluxograma(declaracao, texto);
        const vertices = this.logicaComumConexaoArestas(aresta);
        this.anteriores.push(aresta);
        return Promise.resolve(vertices);
    }
    visitarDeclaracaoEscrevaMesmaLinha(declaracao) {
        throw new Error('Método não implementado.');
    }
    async visitarDeclaracaoFazer(declaracao) {
        const texto = `Linha${declaracao.linha}(fazer)`;
        const aresta = new mermaid_1.ArestaFluxograma(declaracao, texto);
        let vertices = this.logicaComumConexaoArestas(aresta);
        this.anteriores.push(aresta);
        // Corpo, normalmente um `Bloco`.
        const verticesCorpo = await declaracao.caminhoFazer.aceitar(this);
        vertices = vertices.concat(verticesCorpo);
        const ultimaArestaCorpo = verticesCorpo[verticesCorpo.length - 1].destino;
        const condicao = await declaracao.condicaoEnquanto.aceitar(this);
        let textoEnquanto = `Linha${declaracao.condicaoEnquanto.linha}(enquanto ${condicao})`;
        const arestaEnquanto = new mermaid_1.ArestaFluxograma(declaracao, textoEnquanto);
        const verticeEnquanto = new mermaid_1.VerticeFluxograma(ultimaArestaCorpo, arestaEnquanto);
        vertices.push(verticeEnquanto);
        const verticeCondicaoComFazer = new mermaid_1.VerticeFluxograma(arestaEnquanto, aresta);
        vertices.push(verticeCondicaoComFazer);
        this.anteriores.pop();
        this.anteriores.push(arestaEnquanto);
        return Promise.resolve(vertices);
    }
    visitarDeclaracaoInicioAlgoritmo(declaracao) {
        throw new Error('Método não implementado.');
    }
    async visitarDeclaracaoParaCada(declaracao) {
        const textoVariavelIteracao = await declaracao.variavelIteracao.aceitar(this);
        let texto = `Linha${declaracao.linha}(para cada ${textoVariavelIteracao} em `;
        const textoVariavelIterada = await declaracao.vetorOuDicionario.aceitar(this);
        texto += textoVariavelIterada + ')';
        const aresta = new mermaid_1.ArestaFluxograma(declaracao, texto);
        let vertices = this.logicaComumConexaoArestas(aresta);
        this.anteriores.push(aresta);
        // Corpo, normalmente um `Bloco`.
        const verticesCorpo = await declaracao.corpo.aceitar(this);
        vertices = vertices.concat(verticesCorpo);
        const ultimaArestaCorpo = verticesCorpo[verticesCorpo.length - 1].destino;
        vertices.push(new mermaid_1.VerticeFluxograma(ultimaArestaCorpo, aresta));
        return Promise.resolve(vertices);
    }
    async visitarDeclaracaoPara(declaracao) {
        let texto = `Linha${declaracao.linha}(para `;
        if (declaracao.inicializador) {
            for (const declaracaoInicializadora of declaracao.inicializador) {
                // Normalmente é `Var`.
                const declaracaoVar = declaracaoInicializadora;
                const valorInicializacao = await declaracaoVar.inicializador.aceitar(this);
                texto += `uma variável ${declaracaoVar.simbolo.lexema} inicializada com ${valorInicializacao}, `;
            }
            texto = texto.slice(0, -2);
        }
        texto += ')';
        const aresta = new mermaid_1.ArestaFluxograma(declaracao, texto);
        let vertices = this.logicaComumConexaoArestas(aresta);
        this.anteriores.push(aresta);
        // Condição
        const textoCondicao = await declaracao.condicao.aceitar(this);
        const textoArestaCondicao = `Linha${declaracao.linha}Condicao{se ${textoCondicao}}`;
        const arestaCondicao = new mermaid_1.ArestaFluxograma(declaracao, textoArestaCondicao);
        vertices = vertices.concat(this.logicaComumConexaoArestas(arestaCondicao));
        this.anteriores.push(arestaCondicao);
        this.ultimaDicaVertice = 'Sim';
        // Corpo, normalmente um `Bloco`.
        const verticesCorpo = await declaracao.corpo.aceitar(this);
        vertices = vertices.concat(verticesCorpo);
        // Incremento
        const ultimaArestaCorpo = verticesCorpo[verticesCorpo.length - 1].destino;
        const textoIncremento = await declaracao.incrementar.aceitar(this);
        const arestaIncremento = new mermaid_1.ArestaFluxograma(declaracao, `Linha${declaracao.linha}Incremento(${textoIncremento})`);
        const verticeIncremento = new mermaid_1.VerticeFluxograma(ultimaArestaCorpo, arestaIncremento);
        vertices.push(verticeIncremento);
        const verticeLaco = new mermaid_1.VerticeFluxograma(arestaIncremento, arestaCondicao);
        vertices.push(verticeLaco);
        // Configura a condição como anterior
        this.anteriores.pop();
        this.anteriores.push(arestaCondicao);
        this.ultimaDicaVertice = 'Não';
        return Promise.resolve(vertices);
    }
    async visitarDeclaracaoSe(declaracao) {
        let texto = `Linha${declaracao.linha}{se `;
        const condicao = await declaracao.condicao.aceitar(this);
        texto += condicao;
        texto += `}`;
        const aresta = new mermaid_1.ArestaFluxograma(declaracao, texto);
        let vertices = this.logicaComumConexaoArestas(aresta);
        this.anteriores.push(aresta);
        this.ultimaDicaVertice = 'Sim';
        // Caminho então, normalmente um `Bloco`.
        const verticesEntao = await declaracao.caminhoEntao.aceitar(this);
        vertices = vertices.concat(verticesEntao);
        const ultimaArestaEntao = verticesEntao.length > 0
            ? verticesEntao[verticesEntao.length - 1].destino
            : aresta;
        if (declaracao.caminhoSenao) {
            this.anteriores = [];
            const arestaSenao = new mermaid_1.ArestaFluxograma(declaracao, `Linha${declaracao.caminhoSenao.linha}(senão)`);
            vertices.push(new mermaid_1.VerticeFluxograma(aresta, arestaSenao, 'Não'));
            this.anteriores.push(arestaSenao);
            const verticesSenao = await declaracao.caminhoSenao.aceitar(this);
            vertices = vertices.concat(verticesSenao);
        }
        this.anteriores.push(ultimaArestaEntao);
        return Promise.resolve(vertices);
    }
    async visitarDeclaracaoTendoComo(declaracao) {
        const textoVariavelIteracao = await declaracao.inicializacaoVariavel.aceitar(this);
        let texto = `Linha${declaracao.linha}(tendo ${textoVariavelIteracao} como `;
        texto += declaracao.simboloVariavel.lexema + ')';
        const aresta = new mermaid_1.ArestaFluxograma(declaracao, texto);
        let vertices = this.logicaComumConexaoArestas(aresta);
        this.anteriores.push(aresta);
        // Corpo, normalmente um `Bloco`.
        const verticesCorpo = await declaracao.corpo.aceitar(this);
        vertices = vertices.concat(verticesCorpo);
        const ultimaArestaCorpo = verticesCorpo[verticesCorpo.length - 1].destino;
        vertices.push(new mermaid_1.VerticeFluxograma(ultimaArestaCorpo, aresta));
        return Promise.resolve(vertices);
    }
    async visitarDeclaracaoTente(declaracao) {
        const texto = `Linha${declaracao.linha}(tente)`;
        const aresta = new mermaid_1.ArestaFluxograma(declaracao, texto);
        let vertices = this.logicaComumConexaoArestas(aresta);
        this.anteriores.push(aresta);
        // Caminho tente (try)
        const verticesTente = [];
        for (const declaracaoTente of declaracao.caminhoTente) {
            const verticesDeclaracao = await declaracaoTente.aceitar(this);
            verticesTente.push(...verticesDeclaracao);
        }
        vertices = vertices.concat(verticesTente);
        const ultimaArestaTente = verticesTente.length > 0
            ? verticesTente[verticesTente.length - 1].destino
            : aresta;
        const anterioresAposTente = [];
        // Caminho pegue (catch) - se existir
        if (declaracao.caminhoPegue) {
            this.anteriores = [aresta];
            const arestaPegue = new mermaid_1.ArestaFluxograma(declaracao, `Linha${declaracao.linha}Pegue(pegue)`);
            vertices.push(new mermaid_1.VerticeFluxograma(aresta, arestaPegue, 'Erro'));
            this.anteriores.push(arestaPegue);
            const verticesPegue = [];
            if (Array.isArray(declaracao.caminhoPegue)) {
                for (const declaracaoPegue of declaracao.caminhoPegue) {
                    const verticesDeclaracao = await declaracaoPegue.aceitar(this);
                    verticesPegue.push(...verticesDeclaracao);
                }
            }
            vertices = vertices.concat(verticesPegue);
            const ultimaArestaPegue = verticesPegue.length > 0
                ? verticesPegue[verticesPegue.length - 1].destino
                : arestaPegue;
            anterioresAposTente.push(ultimaArestaPegue);
        }
        // Caminho senão (else) - se existir
        if (declaracao.caminhoSenao && declaracao.caminhoSenao.length > 0) {
            this.anteriores = [ultimaArestaTente];
            const arestaSenao = new mermaid_1.ArestaFluxograma(declaracao, `Linha${declaracao.linha}Senao(senão - sem erro)`);
            vertices.push(new mermaid_1.VerticeFluxograma(ultimaArestaTente, arestaSenao, 'Sucesso'));
            this.anteriores.push(arestaSenao);
            const verticesSenao = [];
            for (const declaracaoSenao of declaracao.caminhoSenao) {
                const verticesDeclaracao = await declaracaoSenao.aceitar(this);
                verticesSenao.push(...verticesDeclaracao);
            }
            vertices = vertices.concat(verticesSenao);
            const ultimaArestaSenao = verticesSenao.length > 0
                ? verticesSenao[verticesSenao.length - 1].destino
                : arestaSenao;
            anterioresAposTente.push(ultimaArestaSenao);
        }
        else {
            // Se não há senão, o caminho de sucesso também continua
            anterioresAposTente.push(ultimaArestaTente);
        }
        // Caminho finalmente (finally) - se existir
        if (declaracao.caminhoFinalmente && declaracao.caminhoFinalmente.length > 0) {
            this.anteriores = anterioresAposTente;
            const arestaFinalmente = new mermaid_1.ArestaFluxograma(declaracao, `Linha${declaracao.linha}Finalmente(finalmente)`);
            vertices = vertices.concat(this.logicaComumConexaoArestas(arestaFinalmente));
            this.anteriores.push(arestaFinalmente);
            const verticesFinalmente = [];
            for (const declaracaoFinalmente of declaracao.caminhoFinalmente) {
                const verticesDeclaracao = await declaracaoFinalmente.aceitar(this);
                verticesFinalmente.push(...verticesDeclaracao);
            }
            vertices = vertices.concat(verticesFinalmente);
        }
        else {
            // Se não há finalmente, os anteriores são os caminhos após tente
            this.anteriores = anterioresAposTente;
        }
        return Promise.resolve(vertices);
    }
    visitarDeclaracaoTextoDocumentacao(declaracao) {
        throw new Error('Método não implementado.');
    }
    async visitarDeclaracaoVar(declaracao) {
        let texto = `Linha${declaracao.linha}(variável: ${declaracao.simbolo.lexema}`;
        texto += await this.logicaComumTraducaoVarEConst(declaracao, texto);
        const aresta = new mermaid_1.ArestaFluxograma(declaracao, texto);
        const vertices = this.logicaComumConexaoArestas(aresta);
        this.anteriores.push(aresta);
        return Promise.resolve(vertices);
    }
    visitarDeclaracaoVarMultiplo(declaracao) {
        throw new Error('Método não implementado.');
    }
    async visitarExpressaoDeAtribuicao(expressao) {
        const textoAlvo = await expressao.alvo.aceitar(this);
        const textoValor = await expressao.valor.aceitar(this);
        return Promise.resolve(`${textoAlvo} recebe: ${textoValor}`);
    }
    async visitarExpressaoAcessoIndiceVariavel(expressao) {
        const textoIndice = await expressao.indice.aceitar(this);
        return Promise.resolve(`no índice ${textoIndice}`);
    }
    visitarExpressaoAcessoIntervaloVariavel(expressao) {
        throw new Error('Método não implementado.');
    }
    visitarExpressaoAcessoElementoMatriz(expressao) {
        throw new Error('Método não implementado.');
    }
    async visitarExpressaoAcessoMetodo(expressao) {
        return Promise.resolve(`método ${expressao.nomeMetodo}`);
    }
    async visitarExpressaoAcessoMetodoOuPropriedade(expressao) {
        return Promise.resolve(`método ou propriedade ${expressao.simbolo.lexema}`);
    }
    async visitarExpressaoAcessoPropriedade(expressao) {
        return Promise.resolve(`propriedade ${expressao.nomePropriedade}`);
    }
    async visitarExpressaoAgrupamento(expressao) {
        return await expressao.expressao.aceitar(this);
    }
    async visitarExpressaoArgumentoReferenciaFuncao(expressao) {
        const nomeFuncao = expressao.simboloFuncao.lexema;
        return Promise.resolve(`referência à função ${nomeFuncao}`);
    }
    async visitarExpressaoAtribuicaoPorIndice(expressao) {
        const textoObjeto = await expressao.objeto.aceitar(this);
        const textoIndice = await expressao.indice.aceitar(this);
        const textoValor = await expressao.valor.aceitar(this);
        return Promise.resolve(`${textoObjeto} no índice ${textoIndice} recebe: ${textoValor}`);
    }
    visitarExpressaoAtribuicaoPorIndicesMatriz(expressao) {
        throw new Error('Método não implementado.');
    }
    async visitarExpressaoBinaria(expressao) {
        const operandoEsquerdo = await expressao.esquerda.aceitar(this);
        const operandoDireito = await expressao.direita.aceitar(this);
        switch (expressao.operador.tipo) {
            case delegua_1.default.ADICAO:
                return Promise.resolve(`somar ${operandoEsquerdo} e ${operandoDireito}`);
            case delegua_1.default.SUBTRACAO:
                return Promise.resolve(`subtrair ${operandoDireito} de ${operandoEsquerdo}`);
            case delegua_1.default.MULTIPLICACAO:
                return Promise.resolve(`multiplicar ${operandoEsquerdo} por ${operandoDireito}`);
            case delegua_1.default.DIVISAO:
                return Promise.resolve(`dividir ${operandoEsquerdo} por ${operandoDireito}`);
            case delegua_1.default.MODULO:
                return Promise.resolve(`resto de ${operandoEsquerdo} dividido por ${operandoDireito}`);
            case delegua_1.default.MENOR:
                return Promise.resolve(`${operandoEsquerdo} for menor que ${operandoDireito}`);
            case delegua_1.default.MENOR_IGUAL:
                return Promise.resolve(`${operandoEsquerdo} for menor ou igual a ${operandoDireito}`);
            case delegua_1.default.MAIOR:
                return Promise.resolve(`${operandoEsquerdo} for maior que ${operandoDireito}`);
            case delegua_1.default.MAIOR_IGUAL:
                return Promise.resolve(`${operandoEsquerdo} for maior ou igual a ${operandoDireito}`);
            case delegua_1.default.IGUAL_IGUAL:
                return Promise.resolve(`${operandoEsquerdo} for igual a ${operandoDireito}`);
            case delegua_1.default.DIFERENTE:
                return Promise.resolve(`${operandoEsquerdo} for diferente de ${operandoDireito}`);
        }
        return Promise.resolve('');
    }
    async visitarExpressaoBloco(bloco) {
        let vertices = [];
        for (const declaracao of bloco.declaracoes) {
            const verticesDeclaracao = await declaracao.aceitar(this);
            vertices = vertices.concat(verticesDeclaracao);
        }
        return Promise.resolve(vertices);
    }
    async visitarExpressaoComentario(expressao) {
        return Promise.resolve('');
    }
    async visitarExpressaoContinua(declaracao) {
        const texto = `Linha${declaracao.linha}(continua)`;
        const aresta = new mermaid_1.ArestaFluxograma(declaracao, texto);
        const vertices = this.logicaComumConexaoArestas(aresta);
        this.anteriores.push(aresta);
        return Promise.resolve(vertices);
    }
    async visitarExpressaoDeChamada(expressao) {
        const textoEntidadeChamada = await expressao.entidadeChamada.aceitar(this);
        let texto = `chamada a ${textoEntidadeChamada}`;
        if (expressao.argumentos.length > 0) {
            texto += `, com argumentos: `;
            for (const argumento of expressao.argumentos) {
                const textoArgumento = await argumento.aceitar(this);
                texto += `${textoArgumento}, `;
            }
            texto = texto.slice(0, -2);
        }
        else {
            texto += `, sem argumentos`;
        }
        return Promise.resolve(texto);
    }
    async visitarExpressaoDefinirValor(expressao) {
        const textoObjeto = await expressao.objeto.aceitar(this);
        const textoValor = await expressao.valor.aceitar(this);
        return Promise.resolve(`${expressao.nome.lexema} em ${textoObjeto} recebe ${textoValor}`);
    }
    async visitarExpressaoFuncaoConstruto(expressao) {
        let texto = 'função anônima';
        if (expressao.parametros && expressao.parametros.length > 0) {
            const parametros = expressao.parametros.map(p => p.nome.lexema).join(', ');
            texto += `(${parametros})`;
        }
        else {
            texto += '()';
        }
        return Promise.resolve(texto);
    }
    async visitarExpressaoDeVariavel(expressao) {
        return Promise.resolve(expressao.simbolo.lexema);
    }
    async visitarExpressaoDicionario(expressao) {
        let texto = `dicionário`;
        if (expressao.chaves.length > 0) {
            texto += `, com `;
            for (const [chave, indice] of Object.entries(expressao.chaves)) {
                texto += `chave ${chave} definida com o valor ${expressao.valores[0]}`;
            }
        }
        else {
            texto += ' vazio';
        }
        return Promise.resolve(texto);
    }
    async visitarExpressaoExpressaoRegular(expressao) {
        // Representa a expressão regular como texto para o fluxograma
        const padraoRegex = expressao.valor ? String(expressao.valor) : expressao.simbolo.lexema;
        return Promise.resolve(`expressão regular: /${padraoRegex}/`);
    }
    async visitarExpressaoFalhar(expressao) {
        let texto = `Linha${expressao.linha}(falhar`;
        if (expressao.explicacao) {
            const textoExplicacao = await expressao.explicacao.aceitar(this);
            texto += `: ${textoExplicacao}`;
        }
        texto += ')';
        const aresta = new mermaid_1.ArestaFluxograma(expressao, texto);
        const vertices = this.logicaComumConexaoArestas(aresta);
        this.anteriores.push(aresta);
        return Promise.resolve(vertices);
    }
    visitarExpressaoFimPara(declaracao) {
        throw new Error('Método não implementado.');
    }
    async visitarExpressaoFormatacaoEscrita(declaracao) {
        const textoExpressao = await declaracao.expressao.aceitar(this);
        let formato = textoExpressao;
        // Adiciona informações de formatação se especificadas
        const partes = [textoExpressao];
        if (declaracao.espacos > 0) {
            partes.push(`${declaracao.espacos} espaços`);
        }
        if (declaracao.casasDecimais > 0) {
            partes.push(`${declaracao.casasDecimais} casas decimais`);
        }
        if (partes.length > 1) {
            formato = `${partes[0]} (${partes.slice(1).join(', ')})`;
        }
        return Promise.resolve(formato);
    }
    async visitarExpressaoIsto(expressao) {
        return Promise.resolve('this');
    }
    async visitarExpressaoLeia(expressao) {
        let texto = 'leia da entrada';
        if (expressao.argumentos && expressao.argumentos.length > 0) {
            const textoArgumento = await expressao.argumentos[0].aceitar(this);
            texto += `, imprimindo antes: \\'${textoArgumento}\\'`;
        }
        return Promise.resolve(texto);
    }
    async visitarExpressaoLiteral(expressao) {
        switch (expressao.tipo) {
            case 'lógico':
                return Promise.resolve(expressao.valor ? 'verdadeiro' : 'falso');
            case 'texto':
                return Promise.resolve(`\\'${expressao.valor}\\'`);
            default:
                return Promise.resolve(String(expressao.valor));
        }
    }
    async visitarExpressaoLogica(expressao) {
        const operandoEsquerdo = await expressao.esquerda.aceitar(this);
        const operandoDireito = await expressao.direita.aceitar(this);
        switch (expressao.operador.tipo) {
            case delegua_1.default.E:
                return Promise.resolve(`${operandoEsquerdo} e ${operandoDireito}`);
            case delegua_1.default.OU:
                return Promise.resolve(`${operandoEsquerdo} ou ${operandoDireito}`);
        }
        return Promise.resolve('');
    }
    async visitarExpressaoReferenciaFuncao(expressao) {
        const nomeFuncao = expressao.simboloFuncao.lexema;
        return Promise.resolve(`@${nomeFuncao}`);
    }
    async visitarExpressaoRetornar(expressao) {
        let texto = `Linha${expressao.linha}(retorna`;
        if (expressao.valor) {
            texto += `: ${await expressao.valor.aceitar(this)}`;
        }
        texto += ')';
        const aresta = new mermaid_1.ArestaFluxograma(expressao, texto);
        const vertices = this.logicaComumConexaoArestas(aresta);
        this.anteriores.push(aresta);
        return Promise.resolve(vertices);
    }
    async visitarExpressaoSeparador(expressao) {
        return Promise.resolve(`${expressao.conteudo} `);
    }
    async visitarExpressaoSuper(expressao) {
        return Promise.resolve('super');
    }
    async visitarExpressaoSustar(declaracao) {
        const texto = `Linha${declaracao.linha}(sustar)`;
        const aresta = new mermaid_1.ArestaFluxograma(declaracao, texto);
        const vertices = this.logicaComumConexaoArestas(aresta);
        this.anteriores.push(aresta);
        return Promise.resolve(vertices);
    }
    async visitarExpressaoTupla(expressao) {
        // Tupla base pode ter um único valor
        if (expressao.valor !== undefined) {
            return Promise.resolve(`tupla(${expressao.valor})`);
        }
        // Se não houver valor, tupla vazia
        return Promise.resolve('tupla()');
    }
    async visitarExpressaoTuplaN(expressao) {
        const valores = [];
        for (const elemento of expressao.elementos) {
            const valorTraduzido = await elemento.aceitar(this);
            valores.push(valorTraduzido);
        }
        return Promise.resolve(`tupla(${valores.join(', ')})`);
    }
    visitarExpressaoTipoDe(expressao) {
        throw new Error('Método não implementado.');
    }
    async visitarExpressaoUnaria(expressao) {
        const textoOperando = await expressao.operando.aceitar(this);
        let textoOperador = '';
        switch (expressao.operador.tipo) {
            case delegua_1.default.INCREMENTAR:
                textoOperador = `incrementar ${textoOperando} em 1`;
                break;
            case delegua_1.default.DECREMENTAR:
                textoOperador = `decrementar ${textoOperando} de 1`;
                break;
        }
        switch (expressao.incidenciaOperador) {
            case 'ANTES':
                return Promise.resolve(`${textoOperador}, devolver valor de ${textoOperando}`);
            case 'DEPOIS':
                return Promise.resolve(`devolver valor de ${textoOperando}, ${textoOperador}`);
        }
    }
    async visitarExpressaoVetor(expressao) {
        let texto = `vetor: `;
        for (const elemento of expressao.valores) {
            texto += await elemento.aceitar(this);
        }
        return Promise.resolve(texto);
    }
    /**
     * Traduz uma declaração de Expressao que contém uma chamada de função,
     * criando os vértices necessários para conectar ao subgrafo da função.
     */
    async traduzirChamadaFuncao(declaracaoExpressao, chamada) {
        // Verifica se é uma chamada a uma função conhecida
        if (chamada.entidadeChamada.constructor === construtos_1.Variavel) {
            const variavel = chamada.entidadeChamada;
            const nomeFuncao = variavel.simbolo.lexema;
            if (this.declaracoesFuncoes[nomeFuncao]) {
                const subgrafo = this.declaracoesFuncoes[nomeFuncao];
                let vertices = [];
                // Conecta do fluxo atual para a entrada da função
                const textoPreChamada = `Linha${declaracaoExpressao.linha}(${await chamada.aceitar(this)})`;
                const arestaPreChamada = new mermaid_1.ArestaFluxograma(declaracaoExpressao, textoPreChamada);
                vertices = vertices.concat(this.logicaComumConexaoArestas(arestaPreChamada));
                // Conecta a pré-chamada ao início da função
                vertices.push(new mermaid_1.VerticeFluxograma(arestaPreChamada, subgrafo.arestaInicial));
                // A saída da função volta para o fluxo principal
                this.anteriores = [subgrafo.arestaFinal];
                return Promise.resolve(vertices);
            }
        }
        // Se não for uma função conhecida, trata como expressão normal
        return Promise.resolve([]);
    }
    logicaComumConexaoArestas(aresta) {
        const vertices = [];
        while (this.anteriores.length > 0) {
            const anterior = this.anteriores.shift();
            let textoVertice = undefined;
            if (this.ultimaDicaVertice) {
                textoVertice = String(this.ultimaDicaVertice);
                this.ultimaDicaVertice = undefined;
            }
            vertices.push(new mermaid_1.VerticeFluxograma(anterior, aresta, textoVertice));
        }
        return vertices;
    }
    async logicaComumCaminhoEscolha(declaracaoEscolha, caminhoEscolha, linha, textoIdentificadorOuLiteral, caminhoPadrao) {
        let textoCaso = '';
        if (!caminhoPadrao) {
            textoCaso = `caso ${textoIdentificadorOuLiteral} seja igual a `;
            for (const condicao of caminhoEscolha.condicoes) {
                const textoCondicao = await condicao.aceitar(this);
                textoCaso += `${textoCondicao} ou `;
            }
            textoCaso = textoCaso.slice(0, -4);
            textoCaso += ':';
        }
        else {
            textoCaso = `caso ${textoIdentificadorOuLiteral} tenha qualquer outro valor:`;
        }
        let textoCaminho = `Linha${linha}(${textoCaso})`;
        const arestaCondicaoCaminho = new mermaid_1.ArestaFluxograma(declaracaoEscolha, textoCaminho);
        this.anteriores.push(arestaCondicaoCaminho);
        let verticesResolvidos = [];
        for (const declaracaoCaminho of caminhoEscolha.declaracoes) {
            const verticesDeclaracoes = await declaracaoCaminho.aceitar(this);
            verticesResolvidos = verticesResolvidos.concat(verticesDeclaracoes);
            this.anteriores.pop();
            this.anteriores.push(verticesDeclaracoes[verticesDeclaracoes.length - 1].destino);
        }
        this.anteriores.pop();
        return Promise.resolve({
            caminho: arestaCondicaoCaminho,
            declaracoesCaminho: verticesResolvidos,
        });
    }
    async logicaComumTraducaoVarEConst(declaracaoVarOuConst, textoInicial) {
        if (declaracaoVarOuConst.inicializador) {
            textoInicial += `, iniciada com: ${await declaracaoVarOuConst.inicializador.aceitar(this)}`;
        }
        textoInicial += ')';
        return Promise.resolve(textoInicial);
    }
    /**
     * Ponto de entrada para a tradução de declarações em um fluxograma
     * no formato MermaidJs.
     * @param {Declaracao[]} declaracoes As declarações a serem traduzidas.
     * @returns {string} Texto no formato MermaidJs representando o fluxograma.
     */
    async traduzir(declaracoes) {
        this.anteriores = [];
        this.vertices = [];
        let resultado = 'graph TD;\n';
        this.indentacaoAtual = 4;
        this.declaracoesFuncoes = {};
        this.declaracoesClasses = {};
        for (const declaracao of declaracoes) {
            this.vertices = this.vertices.concat(await declaracao.aceitar(this));
        }
        // Renderiza os subgrafos de funções
        if (Object.keys(this.declaracoesFuncoes).length > 0) {
            for (const [nomeFuncao, subgrafo] of Object.entries(this.declaracoesFuncoes)) {
                resultado += `    subgraph ${nomeFuncao}["Função: ${nomeFuncao}()"]\n`;
                // Renderiza os vértices da função
                for (const vertice of subgrafo.vertices) {
                    resultado += '    ' + vertice.paraTexto();
                }
                resultado += `    end\n`;
            }
        }
        // Renderiza os subgrafos de classes
        if (Object.keys(this.declaracoesClasses).length > 0) {
            for (const [nomeClasse, subgrafo] of Object.entries(this.declaracoesClasses)) {
                resultado += subgrafo.paraTexto();
            }
        }
        if (this.vertices.length === 0 && this.anteriores.length === 0) {
            resultado += `    Vazio;\n`;
        }
        if (this.vertices.length > 0) {
            for (const vertice of this.vertices) {
                resultado += vertice.paraTexto();
            }
        }
        if (this.anteriores.length > 0) {
            while (this.anteriores.length > 0) {
                const anterior = this.anteriores.shift();
                let seta = '-->';
                if (this.ultimaDicaVertice) {
                    seta = `-->|${this.ultimaDicaVertice}|`;
                    this.ultimaDicaVertice = undefined;
                }
                resultado += `    ${anterior.texto}${seta}Fim;\n`;
            }
        }
        return resultado;
    }
}
exports.TradutorMermaidJs = TradutorMermaidJs;
//# sourceMappingURL=tradutor-mermaidjs.js.map