package plp.orientadaObjetos1.expressao.binaria;

import plp.orientadaObjetos1.memoria.AmbienteCompilacao;
import plp.orientadaObjetos1.memoria.AmbienteExecucao;
import plp.orientadaObjetos1.excecao.declaracao.VariavelJaDeclaradaException;
import plp.orientadaObjetos1.excecao.declaracao.VariavelNaoDeclaradaException;
import plp.orientadaObjetos1.excecao.declaracao.ClasseNaoDeclaradaException;
import plp.orientadaObjetos1.excecao.declaracao.ObjetoNaoDeclaradoException;
import plp.orientadaObjetos1.expressao.Expressao;
import plp.orientadaObjetos1.util.TipoPrimitivo;
import plp.orientadaObjetos1.util.Tipo;
import plp.orientadaObjetos1.expressao.valor.Valor;
import plp.orientadaObjetos1.expressao.valor.ValorString;

/**
 * Um objeto desta classe representa uma expressao de Concatenacao entre objetos
 * <code>ValorString</code>
 */
public class ExpConcat extends ExpBinaria {

	/**
	 * Controi uma expressao de Concatenacao com as sub-expressoes
	 * especificadas. Estas sub-expressoes devem ser tais que a avaliacao das
	 * mesmas resulta em <code>ValorString</code>
	 * 
	 * @param esq
	 *            expressao da esquerda
	 * @param dir
	 *            expressao da direita
	 */
	public ExpConcat(Expressao esq, Expressao dir) {
		super(esq, dir, "++");
	}

	/**
	 * Retorna o valor da expressao de Concatenacao
	 */
	public Valor avaliar(AmbienteExecucao ambiente) throws VariavelNaoDeclaradaException, VariavelJaDeclaradaException,
			ObjetoNaoDeclaradoException {
		return obterResultadoDaConcatenacao(ambiente);
	}

	/**
	 * Realiza a verificacao de tipos desta expressao.
	 * 
	 * @param ambiente
	 *            o ambiente de compila��o.
	 * @return <code>true</code> se os tipos da expressao s�o v�lidos;
	 *         <code>false</code> caso contrario.
	 * @exception VariavelNaoDeclaradaException
	 *                se existir um identificador nao declarado no ambiente.
	 * @exception VariavelNaoDeclaradaException
	 *                se existir um identificador declarado mais de uma vez no
	 *                mesmo bloco do ambiente.
	 */
	public boolean checaTipo(AmbienteCompilacao ambiente) throws VariavelNaoDeclaradaException,
			ClasseNaoDeclaradaException {
		boolean result;
		if (super.checaTipo(ambiente) && ((TipoPrimitivo) getEsq().getTipo(ambiente)).eString() || // we
																									// changed
																									// &&
																									// to
																									// ||
				((TipoPrimitivo) getDir().getTipo(ambiente)).eString()) {
			result = true;
		} else {
			result = false;
		}
		return result;
	}

	/**
	 * Retorna os tipos possiveis desta expressao.
	 * 
	 * @param ambiente
	 *            o ambiente de compila��o.
	 * @return os tipos possiveis desta expressao.
	 */
	public Tipo getTipo(AmbienteCompilacao ambiente) {
		return TipoPrimitivo.TIPO_STRING;
	}

	/**
	 * Retorna o valor inteiro que representa o resultado da concatenacao de
	 * dois Strings
	 * 
	 * @param ambiente �
	 *            o Ambiente de Execu��o
	 * @return o valor inteiro que representa o resultado da concatenacao de
	 *         dois Strings
	 */
	private ValorString obterResultadoDaConcatenacao(AmbienteExecucao ambiente) throws VariavelJaDeclaradaException,
			VariavelNaoDeclaradaException, ObjetoNaoDeclaradoException {
		return new ValorString((((Valor) getEsq().avaliar(ambiente))).toString()
				+ (((Valor) getDir().avaliar(ambiente))).toString());
	}
}