package com.gfi.utils;

import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.List;

import com.gfi.constants.Constants;

/**
 * Agrupa utilidades para las SQLs.
 * 
 *  
 */
public class UtilSQL {

	public static final String AND = " AND ";
	public static final String VARIABLE = " ? ";
	private static final String LIKE = " LIKE ";
	private static final String IGUAL_VAR = " = ? ";
	private static final String PATRON_LIKE = "%";
	private static final String ESCAPE = " ESCAPE '\\' ";
	private static final String CHAR_TO_ESCAPE = "_";
	private static final String MAYOR_IGUAL_VAR = " >= ? ";
	private static final String MENOR_IGUAL_VAR = " <= ? ";

	/**
	 * Constructor privado
	 */
	private UtilSQL() {
	}

	/**
	 * Obtiene un integer, devolviendo null (y no 0) en el caso de que el campo
	 * este vacio.
	 * 
	 * @param rs
	 *            ResultSet
	 * @param column
	 *            String
	 * @return Integer
	 * @throws SQLException
	 *             e
	 */
	public static Integer getInt(ResultSet rs, String column)
			throws SQLException {
		String value = rs.getString(column);
		return value != null ? Integer.valueOf(value) : null;
	}

	/**
	 * Obtiene un long, devolviendo null (y no 0) en el caso de que el campo
	 * este vacio.
	 * 
	 * @param rs
	 *            ResultSet
	 * @param column
	 *            String
	 * @return Long
	 * @throws SQLException
	 *             e
	 */
	public static Long getLong(ResultSet rs, String column) throws SQLException {
		String value = rs.getString(column);
		return value != null ? Long.valueOf(value) : null;
	}

	/**
	 * Añade a la columna la clausula translate y upper, para realizar
	 * comparaciones sin tener en cuenta caracteres especiales ni mayusculas
	 * 
	 * @param columna
	 *            String
	 * @return String
	 */
	public static String translateUpper(String columna) {
		StringBuilder sb = new StringBuilder(Constants.STRING_BUILDER_INIT);
		sb.append(" TRANSLATE(UPPER(");
		sb.append(columna);
		sb.append("),'\u00c1\u00c9\u00cd\u00d3\u00da\u00dc','AEIOUU') ");
		return sb.toString();
	}

	/**
	 * Crea un filtro en una sentencia
	 * 
	 * @param where
	 *            StringBuilder
	 * @param params
	 *            List
	 * @param campo
	 *            String
	 * @param value
	 *            Object
	 * @param upperAndTranslate
	 *            Aplica las funciones UPPER Y TRANSLATE a la columna por la que
	 *            se quiere filtrar.
	 */
	public static void createFilterSQL(StringBuilder where,
			List<Object> params, String campo, Object value,
			boolean upperAndTranslate) {
		// TODO MIRAR AND
		// if (!Utilidades.isEmpty(where.toString())) {
		where.append(UtilSQL.AND);
		// }

		if (value instanceof String && value != null) {
			// Si es un String, translateUpper
			if (upperAndTranslate) {
				where.append(UtilSQL.translateUpper(campo)).append(
						UtilSQL.IGUAL_VAR);
				params.add(Utilities.upperTranslateString((String) value));
			} else {
				where.append(campo).append(UtilSQL.IGUAL_VAR);
				params.add(value);
			}

		} else {
			// Si NO es un String
			where.append(campo).append(UtilSQL.IGUAL_VAR);
			params.add(value);
		}
	}

	/**
	 * Crea un filtro en una sentencia
	 * 
	 * @param where
	 *            StringBuilder
	 * @param params
	 *            List
	 * @param campo
	 *            String
	 * @param value
	 *            Object
	 */
	public static void createFilterSQL(StringBuilder where,
			List<Object> params, String campo, Object value) {
		UtilSQL.createFilterSQL(where, params, campo, value, true);
	}

	/**
	 * Crea un filtro LIKE en una sentencia.
	 * 
	 * @param where
	 *            StringBuilder
	 * @param params
	 *            List
	 * @param campo
	 *            String
	 * @param value
	 *            Valor por el que se quiere filtrar. Si es 'String', se quitan
	 *            los acentos (no las dieresis) y se convierte a mayusculas.
	 * @param startsWith
	 *            boolean
	 * @param upperAndTranslate
	 *            Aplica las funciones UPPER Y TRANSLATE a la columna por la que
	 *            se quiere filtrar.
	 */
	public static void createFilterLikeSQL(StringBuilder where,
			List<Object> params, String campo, Object value,
			boolean startsWith, boolean upperAndTranslate) {
		// TODO MIRAR AND
		// if (!Utilidades.isEmpty(where.toString())) {
		where.append(UtilSQL.AND);
		// }

		if (value instanceof String && value != null) {
			// Si es un String, translateUpper
			if (upperAndTranslate) {
				where.append(UtilSQL.translateUpper(campo));
			} else {
				where.append(campo);
			}
			where.append(UtilSQL.LIKE).append(UtilSQL.VARIABLE);

			// Param
			StringBuilder sbValue = new StringBuilder();
			if (!startsWith) {
				// Se aplica el patron like al principio de la cadena
				sbValue.append(UtilSQL.PATRON_LIKE);
			}
			// Si contiene algun char para escapar, hacerlo
			String strValue = (String) value;
			if (UtilSQL.haveLikeEscapableChar(strValue)) {
				strValue = UtilSQL.escapeLikeChar(strValue);
				where.append(UtilSQL.ESCAPE);
			}
			// Si es un String, translateUpper
			if (upperAndTranslate) {
				sbValue.append(Utilities.upperTranslateString(strValue));
			} else {
				sbValue.append(strValue);
			}
			sbValue.append(UtilSQL.PATRON_LIKE);
			params.add(sbValue.toString());
		} else {
			// Si NO es un String, Igual
			where.append(campo).append(UtilSQL.IGUAL_VAR);
			params.add(value);
		}
	}

	/**
	 * Crea un filtro LIKE en una sentencia
	 * 
	 * @param where
	 *            StringBuilder
	 * @param params
	 *            List
	 * @param campo
	 *            String
	 * @param value
	 *            Object
	 * @param startsWith
	 *            boolean
	 */
	public static void createFilterLikeSQL(StringBuilder where,
			List<Object> params, String campo, Object value, boolean startsWith) {
		UtilSQL.createFilterLikeSQL(where, params, campo, value, startsWith,
				true);
	}

	/**
	 * Crea un filtro desde-hasta.
	 * 
	 * @param where
	 *            StringBuilder
	 * @param params
	 *            List
	 * @param campo
	 *            String
	 * @param valueFrom
	 *            Object, (Numbre, Date)
	 * @param valueTo
	 *            Object, (Numbre, Date)
	 * @param truncate
	 *            boolean, hace un truncate del campo. Quita las horas en Date y
	 *            los decimales en Number
	 */
	public static void createFilterFromToSQL(StringBuilder where,
			List<Object> params, String campo, Object valueFrom,
			Object valueTo, boolean truncate) {
		StringBuilder sbCampo = new StringBuilder();
		// Truncate si se indica
		if (truncate) {
			sbCampo.append(" TRUNC(").append(campo).append(")");
		} else {
			sbCampo.append(campo);
		}

		if (valueFrom != null) {
			where.append(UtilSQL.AND).append(sbCampo.toString())
					.append(UtilSQL.MAYOR_IGUAL_VAR);
			params.add(valueFrom);
		}
		if (valueTo != null) {
			where.append(UtilSQL.AND).append(sbCampo.toString())
					.append(UtilSQL.MENOR_IGUAL_VAR);
			params.add(valueTo);
		}
	}

	/**
	 * Crea un filtro desde-hasta.
	 * 
	 * @param where
	 *            StringBuilder
	 * @param params
	 *            List
	 * @param campo
	 *            String
	 * @param valueFrom
	 *            Object, (Numbre, Date)
	 * @param valueTo
	 *            Object, (Numbre, Date)
	 */
	public static void createFilterFromToSQL(StringBuilder where,
			List<Object> params, String campo, Object valueFrom, Object valueTo) {
		UtilSQL.createFilterFromToSQL(where, params, campo, valueFrom, valueTo,
				false);
	}

	// UTILIDADES PRIVADAS //

	/**
	 * Comprueba si tiene algun caracter que necesite escapar
	 * 
	 * @param value
	 *            String
	 * @return Boolean
	 */
	private static Boolean haveLikeEscapableChar(String value) {
		return !Utilities.isEmpty(value)
				&& value.contains(UtilSQL.CHAR_TO_ESCAPE);
	}

	/**
	 * Escapa los caracteres que lo necesitan
	 * 
	 * @param value
	 *            String
	 * @return String
	 */
	private static String escapeLikeChar(String value) {
		String resultado = value;
		if (!Utilities.isEmpty(value)) {
			resultado = value.replaceAll("\\".concat(UtilSQL.CHAR_TO_ESCAPE),
					"\\\\".concat(UtilSQL.CHAR_TO_ESCAPE));
		}
		return resultado;
	}
}