package com.gfi.utils;

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.math.BigDecimal;
import java.text.DecimalFormat;
import java.text.DecimalFormatSymbols;
import java.text.SimpleDateFormat;
import java.util.Collection;
import java.util.Date;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;

import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.i18n.LocaleContextHolder;

import com.ejie.ab39a.constantes.Constantes;
import com.gfi.constants.Constants;

/**
 * Agrupa utilidades genéricas.
 * 
 *  
 * 
 */
public class Utilities {

	private static final Logger logger = LoggerFactory.getLogger(Utilities.class);

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

	/**
	 * Devuelve true en caso de que la cadena sea:
	 * <li><b>null</b></li>
	 * <li><b>length == 0</b></li>.<br>
	 * 
	 * @param cadena
	 *            String cadena
	 * @return Boolean true/false
	 */
	public static Boolean isEmpty(String cadena) {
		return cadena == null || cadena.trim().length() == 0;
	}

	/**
	 * Devuelve true en caso de que el numero sea:
	 * <li><b>null</b></li>
	 * <li><b>0</b></li>.<br>
	 * El objeto pasado debe implementar el metodo (toString).
	 * 
	 * @param number
	 *            Object numero
	 * @return Boolean true/false
	 */
	public static Boolean isEmptyNumber(Object number) {
		String strNumber = String.valueOf(number);
		return Utilities.isEmpty(strNumber) || strNumber.equalsIgnoreCase("0") || strNumber.equalsIgnoreCase("null");
	}

	/**
	 * Devuelve true en caso de que el numero sea:
	 * <li><b>null</b></li>
	 * <li><b>0</b></li>.<br>
	 * El objeto pasado debe implementar el metodo (toString).
	 * 
	 * @param number
	 *            Object numero
	 * @return Boolean true/false
	 */
	public static Boolean isEmptyNumberSin0(Object number) {
		String strNumber = String.valueOf(number);
		return Utilities.isEmpty(strNumber) || strNumber.equalsIgnoreCase("null");
	}

	/**
	 * Devuelve true en caso de que la lista sea:
	 * <li><b>null</b></li>
	 * <li><b>length == 0</b></li>.<br>
	 * 
	 * @param lista
	 *            List<?> lista
	 * @return Boolean true/false
	 */
	public static Boolean isEmptyList(List<?> lista) {
		return lista == null || lista.isEmpty();
	}

	/**
	 * Compara si dos objetos son iguales, o ambos null.
	 * 
	 * @param obj1
	 *            Object obj1
	 * @param obj2
	 *            Object obj2
	 * @return boolean true/false
	 */
	public static boolean areEquals(Object obj1, Object obj2) {
		boolean resultado = false;
		if (obj1 != null) {
			resultado = obj1.equals(obj2);
		} else {
			resultado = obj2 == null;
		}
		return resultado;
	}

	/**
	 * Devuelve un entero con el número que contiene la cadena recibida. Retorna
	 * null si no es un integer (no lanza excepcion).
	 * 
	 * @param str
	 *            String str
	 * @return Integer int
	 */
	public static Integer string2Integer(String str) {
		Integer numero = null;
		try {
			numero = !Utilities.isEmpty(str) ? Integer.valueOf(str) : null;
		} catch (NumberFormatException e) {
			Utilities.logger.warn("string2Integer", e);
		}
		return numero;
	}

	/**
	 * Devuelve un Long con el número que contiene la cadena recibida. Retorna
	 * null si no es un long (no lanza excepcion).
	 * 
	 * @param str
	 *            String str
	 * @return Long long
	 */
	public static Long string2Long(String str) {
		Long numero = null;
		try {
			numero = !Utilities.isEmpty(str) ? Long.valueOf(str) : null;
		} catch (NumberFormatException e) {
			Utilities.logger.warn("string2Long", e);
		}
		return numero;
	}

	/**
	 * Padding de un string
	 * 
	 * @param value
	 *            String value
	 * @param size
	 *            int size
	 * @return String string
	 */
	public static String padLeft(String value, int size) {
		return Utilities.padString(value, size, ' ', true);
	}

	/**
	 * Padding de un string
	 * 
	 * @param value
	 *            String value
	 * @param size
	 *            int size
	 * @return String padLeft
	 */
	public static String padRight(String value, int size) {
		return Utilities.padString(value, size, ' ', false);
	}

	/**
	 * Padding de un string
	 * 
	 * @param val
	 *            String val
	 * @param size
	 *            int size
	 * @param character
	 *            char character
	 * @param padLeft
	 *            boolean padLeft
	 * @return String string
	 */
	public static String padString(String val, int size, char character, boolean padLeft) {
		if (Utilities.logger.isTraceEnabled()) {
			Utilities.logger.trace("padString {} {} {} {}", new Object[] { val, size, character, padLeft });
		}
		String value = val;
		if (value == null) {
			value = "";
		}
		int pads = size - value.length();
		String result;
		if (pads < 0) {
			result = padLeft ? value.substring(Math.abs(pads), value.length()) : value.substring(0, size);
		} else {
			char[] padding = new char[pads];
			for (int i = 0; i < padding.length; i++) {
				padding[i] = character;
			}
			result = padLeft ? new String(padding).concat(value) : value.concat(new String(padding));
		}
		return result;
	}

	/**
	 * Metodo que obtiene una cadena con los parámetros recibidos
	 * 
	 * @param str
	 *            String[] str
	 * @return String string
	 */
	public static String concat(String... str) {
		final int strBuffer = 512;
		StringBuilder stb = new StringBuilder(strBuffer);
		for (int i = 0; i < str.length; i++) {
			stb.append(str[i]);
		}
		if (Utilities.logger.isTraceEnabled()) {
			Utilities.logger.trace("concat, resultado: {}", stb.toString());
		}
		return stb.toString();
	}

	/**
	 * Comprueba si el valor existe en la lista. Si la lista no es de strings, a
	 * cada uno de sus elementos se le aplicara la funcion 'String.valueOf' para
	 * realizar la comparacion.
	 * 
	 * @param c
	 *            Coleccion c
	 * @param value
	 *            Valor a encontrar valor
	 * @return boolean true|false
	 */
	public static boolean listContainsAsString(Collection<?> c, String value) {
		if (Utilities.logger.isTraceEnabled()) {
			Utilities.logger.trace("listContainsAsString {} {}", new Object[] { c, value });
		}
		boolean result = false;
		Iterator<?> iter = c.iterator();
		Object o = null;
		for (; iter.hasNext(); o = iter.next()) {
			if (o != null && String.valueOf(o).equals(value)) {
				result = true;
				break;
			}
		}
		if (Utilities.logger.isTraceEnabled()) {
			Utilities.logger.trace("listContainsAsString, resultado: {}", result);
		}
		return result;
	}

	/**
	 * Compara dos intervalos de enteros positivos.<br/>
	 * Devuelve <code>true</code> si un subconjunto del primer intervalo esta
	 * contenido en el conjunto del segundo intervalo.<br/>
	 * Ambos intervalos se consideran <u>cerrados</u>.
	 * 
	 * @param iInf
	 *            Limite inferior del primer intervalo
	 * @param iSup
	 *            Limite superior del primer intervalo
	 * @param iInf2
	 *            Limite inferior del segundo intervalo
	 * @param iSup2
	 *            Limite superior del segundo intervalo
	 * @return boolean boolean
	 */
	public static boolean isOverlappedInterval(Long iInf, Long iSup, Long iInf2, Long iSup2) {
		Long inf = iInf != null ? iInf : 0L;
		Long inf2 = iInf2 != null ? iInf2 : 0L;
		Long sup = iSup != null ? iSup : Long.MAX_VALUE;
		Long sup2 = iSup2 != null ? iSup2 : Long.MAX_VALUE;
		return !((inf.compareTo(inf2) > 0 && sup.compareTo(sup2) > 0)
				|| (inf.compareTo(inf2) < 0 && sup.compareTo(sup2) < 0));
	}

	/**
	 * Devuelve el string pasado con el Translate(UPPER()))) pero en java
	 * 
	 * @param texto
	 *            String texto
	 * @return String string
	 */
	public static String upperTranslateString(String texto) {
		return Utilities.isEmpty(texto) ? texto
				: texto.toUpperCase().replaceAll("\u00c1", "A").replaceAll("\u00c9", "E").replaceAll("\u00cd", "I")
						.replaceAll("\u00d3", "O").replaceAll("\u00da", "U").replaceAll("\u00dc", "U");
	}

	/**
	 * Convierte un valor Importe (BigDecimal) a su representacion en String
	 * 
	 * @param importe
	 *            BigDecimal, decimal que se quiere formatear
	 * @param numDecimales
	 *            int, maximo numero de decimales
	 * @return String, cadena que representa el decimal
	 * @throws IllegalArgumentException
	 *             si no puede formatear el numero
	 */
	public static String formatImporteToString(BigDecimal importe, int numDecimales) throws IllegalArgumentException {
		if (importe == null) {
			return null;
		}
		DecimalFormat df = new DecimalFormat("#,##0.00", new DecimalFormatSymbols(Constants.LOCALE_ES));
		df.setMinimumFractionDigits(numDecimales);
		df.setMaximumFractionDigits(numDecimales);
		return df.format(importe);
	}

	/**
	 * Convierte un valor importe (String) a su representacion en BigDecimal
	 * 
	 * @param cadena
	 *            String, Cadena que representa el importe
	 * @return BigDecimal, que representa el importe
	 */
	public static BigDecimal formatStringToImporte(String cadena) {
		BigDecimal importe = null;
		if (!Utilities.isEmpty(cadena)) {
			String nuevaCad = cadena.replace(Constants.SEPARADOR_MILES, "");
			try {
				importe = new BigDecimal(nuevaCad.replace(Constants.SEPARADOR_DECIMALES, Constants.SEPARADOR_MILES));
			} catch (NumberFormatException nfe) {
				importe = null;
			}
		}
		return importe;
	}

	/**
	 * Formatea un Date para ser utilizado dependiendo del idioma.
	 * 
	 * @param entrada
	 *            Date fecha entrada
	 * 
	 * @return String resultado
	 */
	public static String formatearDateIdioma(Date entrada) {

		String resultado = "";
		Locale locale = LocaleContextHolder.getLocaleContext().getLocale();
		String mascara = locale.getLanguage().equals(Constantes.LOCALE_EU.getLanguage()) ? Constantes.EU_DATE_MASK
				: Constantes.ES_DATE_MASK;
		if (entrada != null) {
			SimpleDateFormat formato = new SimpleDateFormat(mascara, locale);
			resultado = formato.format(entrada);
		}
		return resultado;

	}

	/**
	 * Descarga un documento
	 * 
	 * @param response
	 *            la response
	 * @param documentName
	 *            el nombre del documento
	 * @param inputStream
	 *            el stream
	 * @throws IOException
	 *             excepción de E/S
	 */
	public static void downloadDocument(HttpServletResponse response, String documentName, InputStream inputStream)
			throws IOException {
		final int num1024 = 1024;
		byte[] binaryBuffer = new byte[num1024];
		ServletOutputStream out = response.getOutputStream();
		response.setHeader("Set-Cookie", "fileDownload=true; path=/");
		response.setContentType("application/application/vnd.ms-excel");
		response.setHeader("Content-Disposition", "attachment; filename=" + documentName);
		int bytesReaded = 0;
		while ((bytesReaded = inputStream.read(binaryBuffer)) != -1) {
			out.write(binaryBuffer, 0, bytesReaded);
		}
		out.flush();
		out.close();
	}

	/**
	 * Descarga un documento
	 * 
	 * @param response
	 *            la response response
	 * @param documentName
	 *            el nombre del documento documentName
	 * @param inputStream
	 *            el stream inputStream
	 * @param contentType
	 *            String contentType
	 * @throws IOException
	 *             excepción de E/S
	 */
	public static void downloadDocument(HttpServletResponse response, String documentName, InputStream inputStream,
			String contentType) throws IOException {
		final int num1024 = 1024;
		byte[] binaryBuffer = new byte[num1024];
		ServletOutputStream out = response.getOutputStream();
		response.setHeader("Set-Cookie", "fileDownload=true; path=/");
		response.setContentType(contentType);
		response.setHeader("Content-Disposition", "attachment; filename=" + documentName);
		int bytesReaded = 0;
		while ((bytesReaded = inputStream.read(binaryBuffer)) != -1) {
			out.write(binaryBuffer, 0, bytesReaded);
		}
		out.flush();
		out.close();
	}

	/**
	 * Copiar un fichero
	 * 
	 * @param fOrigen
	 *            String fOrigen
	 * @param fDestino
	 *            String fDestino
	 * @return boolean boolean
	 */
	public static boolean copiarFichero(String fOrigen, String fDestino) {
		try {
			FileInputStream in = new FileInputStream(fOrigen);
			FileOutputStream out = new FileOutputStream(fDestino);
			byte[] buf = new byte[Constantes.MIL_VEINTICUATRO];
			int len;
			while ((len = in.read(buf)) > 0) {
				out.write(buf, 0, len);
			}
			in.close();
			out.close();
			return true;
		} catch (IOException ioe) {
			return false;
		}
	}

}