package br.univali.portugol.nucleo.execucao;

import br.univali.portugol.nucleo.programa.Programa;
import br.univali.portugol.nucleo.asa.*;
import br.univali.portugol.nucleo.mensagens.ErroExecucao;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 *
 * @author fillipi
 */
public class DetectaNosParada implements VisitanteASA
{
    private List<NoBloco> nosParada;
    
    private boolean depuracaoDetalhada = false;

    private Integer linhaBlocoAnterior = null;
    
    public DetectaNosParada(boolean depuracaoDetalhada)
    {
        this.depuracaoDetalhada = depuracaoDetalhada;
    }
    
    private Object interpretarListaBlocos(List<NoBloco> blocos) throws ExcecaoVisitaASA
    {        
        if (blocos != null)
        {
            for (NoBloco noBloco : blocos)
            {
                noBloco.aceitar(this);
            }
        }
        return null;
    }
    
    public List<NoBloco> executar(Programa programa, String[] parametros) throws ErroExecucao
    {
        nosParada = new ArrayList<>();
        
        try
        {
            programa.getArvoreSintaticaAbstrata().aceitar(this);
        }
        catch (ExcecaoVisitaASA ex)
        {
            Logger.getLogger(DetectaNosParada.class.getName()).log(Level.SEVERE, null, ex);
        }
        
        return nosParada;
    }

    @Override
    public Object visitar(NoDeclaracaoFuncao declaracaoFuncao) throws ExcecaoVisitaASA
    {
        interpretarListaBlocos(declaracaoFuncao.getBlocos());        
        return null;
    }
    
    @Override
    public Object visitar(ASAPrograma asap) throws ExcecaoVisitaASA
    {
        List<NoDeclaracao> listaDeclaracoesGlobais = asap.getListaDeclaracoesGlobais();
        for (NoDeclaracao noDeclaracao : listaDeclaracoesGlobais)
        {
            if (noDeclaracao instanceof NoDeclaracaoFuncao) {
                noDeclaracao.aceitar(this);
            } else {
                nosParada.add((NoBloco)noDeclaracao);
            }
        }
        return null;
    }
    
    @Override
    public Object visitar(NoCaso noCaso) throws ExcecaoVisitaASA
    {
        if (noCaso.getExpressao() != null) 
            nosParada.add(noCaso.getExpressao());
        return null;
    }

    @Override
    public Object visitar(NoEnquanto noEnquanto) throws ExcecaoVisitaASA
    {
        nosParada.add(noEnquanto.getCondicao());
        interpretarListaBlocos(noEnquanto.getBlocos());
        return null;
    }

    @Override
    public Object visitar(NoEscolha noEscolha) throws ExcecaoVisitaASA
    {
        List<NoCaso> casos = noEscolha.getCasos();
        nosParada.add(noEscolha.getExpressao());
            
        for(NoCaso caso : casos){
            interpretarListaBlocos(caso.getBlocos());
        }
        return null;
    }

    @Override
    public Object visitar(NoFacaEnquanto noFacaEnquanto) throws ExcecaoVisitaASA
    {        
        nosParada.add(noFacaEnquanto.getCondicao());
        interpretarListaBlocos(noFacaEnquanto.getBlocos());        
        return null;
    }

    @Override
    public Object visitar(NoPara noPara) throws ExcecaoVisitaASA
    {
        NoExpressao condicao = noPara.getCondicao();
        nosParada.add(noPara);
        nosParada.add(noPara.getIncremento());  
        if (depuracaoDetalhada) {
            if (noPara.getInicializacoes() != null && !noPara.getInicializacoes().isEmpty())
            {
                nosParada.add(noPara.getInicializacoes().get(0));
            }
            
            nosParada.add(condicao);
        }
        interpretarListaBlocos(noPara.getBlocos());
        return null;
    }

    @Override
    public Object visitar(NoSe noSe) throws ExcecaoVisitaASA
    {
        nosParada.add(noSe.getCondicao());
        List<NoBloco> blocosVer = noSe.getBlocosVerdadeiros(); 
        List<NoBloco> blocosFal = noSe.getBlocosFalsos();
        interpretarListaBlocos(blocosVer);
        interpretarListaBlocos(blocosFal);

        return null;
    }

    @Override
    public Object visitar(NoCadeia noCadeia) throws ExcecaoVisitaASA
    {
        return null;
    }

    @Override
    public Object visitar(NoCaracter noCaracter) throws ExcecaoVisitaASA
    {
        return null;
    }

    @Override
    public Object visitar(NoChamadaFuncao chamadaFuncao) throws ExcecaoVisitaASA
    {
        if (depuracaoDetalhada) {
            nosParada.add(chamadaFuncao);
        } else { 
            if (linhaBlocoAnterior == null || chamadaFuncao.getTrechoCodigoFonte().getLinha() != linhaBlocoAnterior) {
                linhaBlocoAnterior = chamadaFuncao.getTrechoCodigoFonte().getLinha();
                nosParada.add(chamadaFuncao);
            }
        }
        return null;
    }

    @Override
    public Object visitar(NoDeclaracaoMatriz noDeclaracaoMatriz) throws ExcecaoVisitaASA
    {
        if (depuracaoDetalhada) {
            nosParada.add(noDeclaracaoMatriz);
        } else { 
            if (linhaBlocoAnterior == null || noDeclaracaoMatriz.getTrechoCodigoFonteNome().getLinha() != linhaBlocoAnterior) {
                linhaBlocoAnterior = noDeclaracaoMatriz.getTrechoCodigoFonteNome().getLinha();
                nosParada.add(noDeclaracaoMatriz);
            }
        }
        return null;
    }

    @Override
    public Object visitar(NoDeclaracaoVariavel noDeclaracaoVariavel) throws ExcecaoVisitaASA
    {
        if (depuracaoDetalhada) {
            nosParada.add(noDeclaracaoVariavel);
        } else { 
            if (linhaBlocoAnterior == null || noDeclaracaoVariavel.getTrechoCodigoFonteNome().getLinha() != linhaBlocoAnterior) {
                linhaBlocoAnterior = noDeclaracaoVariavel.getTrechoCodigoFonteNome().getLinha();
                nosParada.add(noDeclaracaoVariavel);
            }
        }
        return null;
    }

    @Override
    public Object visitar(NoDeclaracaoVetor noDeclaracaoVetor) throws ExcecaoVisitaASA
    {
        if (depuracaoDetalhada) {
            nosParada.add(noDeclaracaoVetor);
        } else { 
            if (linhaBlocoAnterior == null || noDeclaracaoVetor.getTrechoCodigoFonteNome().getLinha() != linhaBlocoAnterior) {
                linhaBlocoAnterior = noDeclaracaoVetor.getTrechoCodigoFonteNome().getLinha();
                nosParada.add(noDeclaracaoVetor);
            }
        }
        return null;
    }

    @Override
    public Object visitar(NoInteiro noInteiro) throws ExcecaoVisitaASA
    {
        return null;
    }

    @Override
    public Object visitar(NoLogico noLogico) throws ExcecaoVisitaASA
    {
        return null;
    }

    @Override
    public Object visitar(NoMatriz noMatriz) throws ExcecaoVisitaASA
    {
        return null;
    }

    @Override
    public Object visitar(NoMenosUnario noMenosUnario) throws ExcecaoVisitaASA
    {
        return null;
    }

    @Override
    public Object visitar(NoNao noNao) throws ExcecaoVisitaASA
    {
        return null;
    }

    @Override
    public Object visitar(NoOperacaoLogicaIgualdade noOperacaoLogicaIgualdade) throws ExcecaoVisitaASA
    {
        return null;
    }

    @Override
    public Object visitar(NoOperacaoLogicaDiferenca noOperacaoLogicaDiferenca) throws ExcecaoVisitaASA
    {
        return null;
    }

    @Override
    public Object visitar(NoOperacaoAtribuicao noOperacaoAtribuicao) throws ExcecaoVisitaASA
    {
        if (depuracaoDetalhada) {
            nosParada.add(noOperacaoAtribuicao);
        } else { 
            if (linhaBlocoAnterior == null || noOperacaoAtribuicao.getTrechoCodigoFonte().getLinha() != linhaBlocoAnterior) {
                linhaBlocoAnterior = noOperacaoAtribuicao.getTrechoCodigoFonte().getLinha();
                nosParada.add(noOperacaoAtribuicao);
            }
        }
        return null;
    }

    @Override
    public Object visitar(NoOperacaoLogicaE noOperacaoLogicaE) throws ExcecaoVisitaASA
    {
        return null;
    }

    @Override
    public Object visitar(NoOperacaoLogicaOU noOperacaoLogicaOU) throws ExcecaoVisitaASA
    {
        return null;
    }

    @Override
    public Object visitar(NoOperacaoLogicaMaior noOperacaoLogicaMaior) throws ExcecaoVisitaASA
    {
        return null;
    }

    @Override
    public Object visitar(NoOperacaoLogicaMaiorIgual noOperacaoLogicaMaiorIgual) throws ExcecaoVisitaASA
    {
        return null;
    }

    @Override
    public Object visitar(NoOperacaoLogicaMenor noOperacaoLogicaMenor) throws ExcecaoVisitaASA
    {
        return null;
    }

    @Override
    public Object visitar(NoOperacaoLogicaMenorIgual noOperacaoLogicaMenorIgual) throws ExcecaoVisitaASA
    {
        return null;
    }

    @Override
    public Object visitar(NoOperacaoSoma noOperacaoSoma) throws ExcecaoVisitaASA
    {
        return null;
    }

    @Override
    public Object visitar(NoOperacaoSubtracao noOperacaoSubtracao) throws ExcecaoVisitaASA
    {
        return null;
    }

    @Override
    public Object visitar(NoOperacaoDivisao noOperacaoDivisao) throws ExcecaoVisitaASA
    {
        return null;
    }

    @Override
    public Object visitar(NoOperacaoMultiplicacao noOperacaoMultiplicacao) throws ExcecaoVisitaASA
    {
        return null;
    }

    @Override
    public Object visitar(NoOperacaoModulo noOperacaoModulo) throws ExcecaoVisitaASA
    {
        return null;
    }

    @Override
    public Object visitar(NoPare noPare) throws ExcecaoVisitaASA
    {
        nosParada.add(noPare);
        return null;
    }

    @Override
    public Object visitar(NoReal noReal) throws ExcecaoVisitaASA
    {
        return null;
    }

    @Override
    public Object visitar(NoReferenciaMatriz noReferenciaMatriz) throws ExcecaoVisitaASA
    {
        return null;
    }

    @Override
    public Object visitar(NoReferenciaVariavel noReferenciaVariavel) throws ExcecaoVisitaASA
    {
        return null;
    }

    @Override
    public Object visitar(NoReferenciaVetor noReferenciaVetor) throws ExcecaoVisitaASA
    {
        return null;
    }

    @Override
    public Object visitar(NoRetorne noRetorne) throws ExcecaoVisitaASA
    {
        nosParada.add(noRetorne);
        return null;
    }

    @Override
    public Object visitar(NoVetor noVetor) throws ExcecaoVisitaASA
    {
        return null;
    }

    @Override
    public Object visitar(NoDeclaracaoParametro noDeclaracaoParametro) throws ExcecaoVisitaASA
    {
        return null;
    }

    @Override
    public Object visitar(NoInclusaoBiblioteca noInclusaoBiblioteca) throws ExcecaoVisitaASA
    {
        return null;
    }

    @Override
    public Object visitar(NoOperacaoBitwiseRightShift noOperacaoBitwiseRightShift) throws ExcecaoVisitaASA
    {
        return null;
    }

    @Override
    public Object visitar(NoOperacaoBitwiseE noOperacaoBitwiseE) throws ExcecaoVisitaASA
    {
        return null;
    }

    @Override
    public Object visitar(NoOperacaoBitwiseOu noOperacaoBitwiseOu) throws ExcecaoVisitaASA
    {
        return null;
    }

    @Override
    public Object visitar(NoOperacaoBitwiseXOR noOperacaoBitwiseXOR) throws ExcecaoVisitaASA
    {
        return null;
    }

    @Override
    public Object visitar(NoBitwiseNao noOperacaoBitwiseNao) throws ExcecaoVisitaASA
    {
        return null;
    }

    @Override
    public Object visitar(NoOperacaoBitwiseLeftShift noOperacaoBitwiseLeftShift) throws ExcecaoVisitaASA
    {
        return null;
    }

    @Override
    public Object visitar(NoContinue noContinue) throws ExcecaoVisitaASA
    {
        return null;
    }

    @Override
    public Object visitar(NoTitulo noTitulo) throws ExcecaoVisitaASA
    {
        return null;
    }

    @Override
    public Object visitar(NoVaPara noVaPara) throws ExcecaoVisitaASA
    {
        return null;
    }

}
