/**
 * 
 */
package com.ejie.m62fb.utils.sql;

import java.util.ArrayList;
import java.util.List;

import org.springframework.util.StringUtils;

import com.ejie.m62fb.utils.bean.BeanAccess;
import com.ejie.m62fb.utils.bean.BeanUtils;

/**
 * <p>
 * Clase para configurar un filtro sobre una columna de tabla de base de datos.
 * </p>
 * 
XXXX
 * 
 * @see com.ejie.utils.sql.where.OperadoresSQL
 */
public abstract class FiltroWhereGen{
	
	public static final String AND =" AND ";	
	public static final String OR =" OR ";	
	private static final String WHERE =" WHERE ";
	private static final String ESCAPE = " ESCAPE  '\\' "; 
	private static final String LIKE = " LIKE "; 
	private static final String IGUAL = "=";
	private static final String DISTINTO1 = "<>";
	private static final String DISTINTO2 = "!=";
	
	/**Lista de par&aacute;metros de la <code>SQL</code*/
	protected List<Object> params;
	/**Query <code>SQL</code> a modificar*/
	protected StringBuilder query;
	/**Indica si hay que implementar <code>LIKE</code> y como */
	protected LikeInWhere like;
	
	/**
	 * @param params Lista de par&aacute;metros de la <code>SQL</code
	 * @param query Query <code>SQL</code> a modificar
	 * @param like Indica si hay que implementar <code>LIKE</code> y como
	 */
	public FiltroWhereGen(final List<Object> params,final StringBuilder query,final LikeInWhere like) {
		super();
		this.params = params;
		this.query = query;
		this.like = like == null? new LikeInWhere():like;
	}

	/**
	 * <p>
	 * Funci&oacute;n que deben implementar las clases que hereden de <code>Filtro</code> para llenar la lista de filtros, <code>filtros</code>.<br>
	 * En teor&iacute;a, en esta funci&oacute;n se definir&iacute;a el filtro para todos los campos de la entidad, para usarse en las funciones <code>findAll</code> y <code>findAllLike</code>
	 * </p>  
	 */
	protected abstract void generarWhereBE();
	/**
	 * <p>Agrega un filtro en el atributo <code>query</code> de la clase</p>
	 * @param where <code>SQL</code> con la <code>WHERE</code> a agregar
	 * @param dato Valor de la <code>WHERE</code>
	 * @param operadorLogico <code>AND</code> o <code>OR</code> a agregar al atributo <code>query</code> si hay m&aacute;s filtros
	 */
	public void addFiltro (final String where,final BeanAccess dato,final String operadorLogico){
		
		Object obj = BeanUtils.getValor(dato);
		if (obj==null || 
			(obj instanceof String && ((String) obj).trim().length()==0)){
			return ;
		}
		
		//Modificar la where
		modificarQueryConWhere(where, operadorLogico);
		//Añadir par&aacute;metro 
		aniadirParam(obj);		
	}
	
	/**
	 * <p>Agrega el filtro del par&aacute;metro <code>where</code> si <code>addOrNot</code> es (<code>true</code>).</p>
	 * @param where <code>SQL</code> con la <code>WHERE</code> a agregar
	 * @param addOrNot (<code>true</code>) hay que añadir o (<code>false</code>) no se añade.</p>
	 * @param operadorLogico <code>AND</code> o <code>OR</code> a agregar al atributo <code>query</code> si hay m&aacute;s filtros
	 */
	public void addNulo(final String where,boolean addOrNot,final String operadorLogico){
		if(addOrNot){
			modificarQueryConWhere(where, operadorLogico);
		}
	}
	
	/**
	 * <p>Agrega el filtro del par&aacute;metro <code>where</code> si <code>addOrNot</code> es (<code>true</code>).</p>
	 * @param where <code>SQL</code> con la <code>WHERE</code> a agregar
	 * @param addOrNot (<code>true</code>) hay que añadir o (<code>false</code>) no se añade.</p>
	 * @param operadorLogico <code>AND</code> o <code>OR</code> a agregar al atributo <code>query</code> si hay m&aacute;s filtros
	 */
	public void addAnotherCondition(final String where,boolean addOrNot,final String operadorLogico){
		if(addOrNot){
			modificarQueryConCondition(where, operadorLogico);
		}
	}
	/**
	 * <p>Agrega un filtro en el atributo <code>query</code> de la clase</p>
	 * @param where <code>SQL</code> con la <code>WHERE</code> a agregar
	 * @param listaValores Lista de valores de la <code>WHERE</code>
	 * @param operadorLogico <code>AND</code> o <code>OR</code> a agregar al atributo <code>query</code> si hay m&aacute;s filtros
	 */
	public void addFiltro (final String where,final List<BeanAccess> listaValores,final String operadorLogico){
		
			
		List<Object> valores = new ArrayList<Object>();
		
		//guardar los valores en una lista de objetos y si alguno es nulo no se modifica la where
		for(BeanAccess f:listaValores){
			Object obj = BeanUtils.getValor(f);
			if (obj==null|| 
					(obj instanceof String && ((String) obj).trim().length()==0)){
				return ;
			}
			valores.add(obj);
		}
		//Modificar la where
		modificarQueryConWhere(where, operadorLogico);
		
		//se añaden los parametros
		for(Object o:valores){
			aniadirParam(o);
		}
	}
	
	/**
	 * <p>
	 * Se agrega el par&aacute;metro <code>obj</code> a la lista <code>params</code>.<br>
	 * En el caso de ser un <code>String</code> lo transforma a mayusculas.
	 * </p>
	 * @param obj 
	 */
	protected void aniadirParam(final Object obj){
		//si se tiene que implementar LIKE
		if (obj instanceof String ){
			String valorUpper=((String)obj).toUpperCase();
			if(this.like.isImplementar()){
				//comienza por...
				if(this.like.isStartsWith()){
					this.params.add(valorUpper+"%");
				}
				//contiene ....
				else{
					this.params.add("%"+valorUpper+"%");
				}
			}
			else{
				this.params.add(valorUpper);	
			}
				
		}
		else{
			this.params.add(obj);	
		}
	}
	/**
	 * <p>Modifica el atributo <code>query</code> de la clase con la <code>WHERE</code> y <code>operadorLogico</code></p>
	 * @param where <code>SQL</code> con la <code>WHERE</code> a agregar
	 * @param operadorLogico <code>AND</code> o <code>OR</code> a agregar al atributo <code>query</code> si hay m&aacute;s filtros
	 */
	public void modificarQueryConWhere(final String where,final String operadorLogico){
		//se añade operador lógico si se da el caso
		if(aniadirOpLogico()){
			this.query.append(operadorLogico);
		}
		
		//se añade la where pasada como parámetro
		//si se tiene que implementar LIKE

		if (this.like.isImplementar()){
			String whereConLike = StringUtils.replace(where, IGUAL, LIKE);
			whereConLike = StringUtils.replace(whereConLike, DISTINTO1, LIKE);
			whereConLike = StringUtils.replace(whereConLike, DISTINTO2, LIKE);
			//this.query.append(whereConLike+ESCAPE);
			// GAZAMAGO
			this.query.append(whereConLike);
		}
		else{
			this.query.append(where);
		}
			
		
				
	}
	/**
	 * <p>Modifica el atributo <code>query</code> de la clase con la <code>WHERE</code> y <code>operadorLogico</code></p>
	 * @param where <code>SQL</code> con la <code>WHERE</code> a agregar
	 * @param operadorLogico <code>AND</code> o <code>OR</code> a agregar al atributo <code>query</code> si hay m&aacute;s filtros
	 */
	private void modificarQueryConCondition(final String where,final String operadorLogico){
		//se añade operador lógico si se da el caso
		if(aniadirOpLogico()){
			this.query.append(operadorLogico);
		}
		
		//se añade la where pasada como parámetro
		//si se tiene que implementar LIKE
		this.query.append(where);
		
	}
	/**
	 * <p>Devuelve <code>true</code> si contiene filtros en la <code>WHERE</code> y <code>false</code> si no.</p>
	 * <p>Si no la encuentra, devuelve un <code>String</code> vac&iacute;o</p>
	 *
	 * @return <code>true</code> si contiene <code>WHERE</code> y <code>false</code> si no.
	 */
	protected boolean aniadirOpLogico(){
		//si no está vacia se busca la palabra WHERE
		if (this.query.length()>0)
		{
			int posicionWhere = this.query.toString().toUpperCase().indexOf(WHERE);
			if  (posicionWhere!=-1){
				if (this.query.substring(posicionWhere + WHERE.length(), this.query.length()).trim().length()!=0){
					return true;
				}	
			}
			else{
				this.query.append(WHERE);
			}
		}
		return false;
	}
}
