package com.fp.common.helper;

import java.lang.reflect.Method;
import java.math.BigDecimal;
import java.sql.Date;
import java.sql.Timestamp;
import java.text.MessageFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import javax.persistence.EntityManager;

/**
 * Clase en la cual se definen contantes de la aplicacion.
 *
 * @author Jorge Vaca.
 * @version 2.1
 */
public class Constant {

    /**
     * Codigo de respuesta ok al ejecutar una transaccion.
     */
    public static final String RS_SUCCESS = "0";
    /**
     * Constante que define un valor BigDecimal Cero.
     */
    public static final BigDecimal BD_ZERO = BigDecimal.ZERO;
    /**
     * Constante que define un valor BigDecimal uno (1).
     */
    public static final BigDecimal BD_ONE = BigDecimal.ONE;
    /**
     * Constante que define un valor BigDecimal uno negativo (1).
     */
    public static final BigDecimal BD_ONE_NEGATIVE = new BigDecimal("-1");
    /**
     * Constante que define un valor BigDecimal cien (100).
     */
    public static final BigDecimal BD_ONE_HUNDRED = new BigDecimal("100");
    /**
     * Constante que define un valor BigDecimal de un centavo (0.01).
     */
    public static final BigDecimal BD_ONE_HUNDREDTH = new BigDecimal("0.01");
    /**
     * Constante que define un valor 1 en string.
     */
    public static final String STR_ONE = "1";
    /**
     * Constante que define un valor Y en string.
     */
    public static final String STR_Y = "Y";
    /**
     * Constante que define un valor N en string.
     */
    public static final String STR_N = "N";
    /**
     * Codigo de respuesta ok al ejecutar una transaccion.
     */
    public static final String NO_DATA_FOUND = "COMMONDB-0006";
    /**
     * UNCHECKED WARNING
     */
    public static final String WARN_UNCHECKED = "unchecked";
    public static final String INFORMIX_DIALECT = "org.hibernate.dialect.InformixDialect";
    public static final String ORACLE_DIALECT = "org.hibernate.dialect.Oracle10gDialect";
    public static final String POSTGRES_DIALECT = "org.hibernate.dialect.PostgreSQLDialect";

    /**
     * Metodo que valida si el valor de un cadena es "Y". si el valor es null o
     * diferente retorna false.
     *
     * @param pValue Valor a comprobar si es Y, en ese caso retorna true,
     * @return boolean
     * @throws Exception
     */
    public static boolean ifYes(String pValue) throws Exception {
        boolean result = false;
        if (pValue == null) {
            return false;
        }
        if (pValue.compareTo("Y") == 0) {
            return true;
        }
        return result;
    }

    /**
     * Metodo que se encarga de cambiar a mayuscula la primera letra de una
     * palabra.
     *
     * @param pString String a cambiar a mayusculas las primeras letras de cada
     * palabra del parrafo.
     * @return String
     * @throws Exception
     */
    public static String capitalize(String pString) throws Exception {
        String[] words = pString.split("\\s");
        String resp = "";
        for (String s : words) {
            resp = resp + Constant.capitalizeword(s) + " ";
        }
        return resp;
    }

    /**
     * Metodo que se encarga de cambiar a mayuscula la primera letra de una
     * palabra.
     *
     * @param s Palabra a cambiar a mayuscula la primera letra.
     * @return String
     * @throws Exception
     */
    private static String capitalizeword(String s) throws Exception {
        if (s.length() == 0) {
            return s;
        }
        return s.substring(0, 1).toUpperCase() + s.substring(1).toLowerCase();
    }
    /**
     * Constante que define un unchecked.
     */
    public static final String VUNCHECKED = "unchecked";
    /**
     * Constante que define la cuenta.
     */
    public static final String VACCOUNT = "account";
    /**
     * Constante que define la compania asociada a la cuenta.
     */
    public static final String VCOMPANY = "company";
    /**
     * Constante que define la fecha de vigencia de un registro.
     */
    public static final String VDATETO = "dateto";
    /**
     * Anio mes de particion de una tabla.
     */
    public static final String VPARTITION = "partition";
    /**
     * Codigo de modulo.
     */
    public static final String VMODULECODE = "modulecode";
    /**
     * Codigo de producto.
     */
    public static final String VPRODUCTCODE = "productcode";
    /**
     * Codigo de subproducto.
     */
    public static final String VSUBPRODUCTCODE = "subproductcode";
    /**
     * Codigo de moneda.
     */
    public static final String VCURRENCYCODE = "currencycode";
    /**
     * Fecha de trabajo.
     */
    public static final String VWORKINGDATE = "workingdate";
    /**
     * Separador del key de maps cuando este se compone de varios atributos.
     */
    public static String SEPARATOR = "^";
    /**
     * Codigo de modulo asociado a una transaccion.
     */
    public static final String VTRANSACTIONMODULE = "transactionmodule";
    /**
     * Codigo de transaccion.
     */
    public static final String VTRANSACTIONCODE = "transactioncode";
    /**
     * Codigo de version de una transaccion.
     */
    public static final String VTRANSACTIONVERSION = "transactionversion";
    /**
     * Almacena el el timestamp por defecto de expiracion
     */
    private static Timestamp vDefaulttimestamp;

    /**
     * Devuelve la fecha hasta default de 2999-12-31
     *
     * @return Fecha hasta 2999-12-31
     * @throws Exception
     */
    public static Timestamp getDefaultExpiryTimestamp() throws Exception {
        if (Constant.vDefaulttimestamp == null) {
            SimpleDateFormat fd = new SimpleDateFormat("yyyy-MM-dd");
            ;
            java.util.Date a = fd.parse("2999-12-31");
            Constant.vDefaulttimestamp = new Timestamp(a.getTime());
        }
        return Constant.vDefaulttimestamp;
    }
    /**
     * Almacena la fecha por defecto de expiracion
     */
    private static Date vDefaultdate;

    /**
     * Devuelve la fecha hasta default de 2999-12-31
     *
     * @return Fecha hasta 2999-12-31
     * @throws Exception
     */
    public static java.sql.Date getDefaultExpiryDate() throws Exception {
        if (Constant.vDefaultdate == null) {
            SimpleDateFormat fd = new SimpleDateFormat("yyyy-MM-dd");
            ;
            java.util.Date a = fd.parse("2999-12-31");
            Constant.vDefaultdate = new java.sql.Date(a.getTime());
        }
        return Constant.vDefaultdate;
    }
    /**
     * Almacena el formato yyyyMM
     */
    private static SimpleDateFormat vPartition;

    /**
     * Formatea una fecha para ser empleada como campo de particionamiento en
     * una Tabla
     *
     * @param pDate Fecha de Referencia.
     * @return String Fecha con el formato yyyyMM
     * @throws Exception
     */
    public static String getPartition(Date pDate) throws Exception {
        if (Constant.vPartition == null) {
            Constant.vPartition = new SimpleDateFormat("yyyyMM");
        }
        String aux = "";
        try {
            aux = Constant.vPartition.format(pDate);
        } catch (Exception e) {
            aux = Constant.vPartition.format(pDate);
        }
        return aux;
    }

    /**
     * Formatea una fecha para ser empleada como campo de particionamiento en
     * una Tabla
     *
     * @param pDate Fecha de Referencia.
     * @return String Fecha con el formato yyyyMM
     * @throws Exception
     */
    public static String getPartition(Timestamp pDate) throws Exception {
        if (Constant.vPartition == null) {
            Constant.vPartition = new SimpleDateFormat("yyyyMM");
        }
        String aux = "";
        try {
            aux = Constant.vPartition.format(pDate);
        } catch (Exception e) {
            aux = Constant.vPartition.format(pDate);
        }
        return aux;
    }

    /**
     * Entrega el la particion default de caducidad de registros.
     *
     * @return Particion con el formato yyyyMM
     * @throws Exception
     */
    public static String getDefaultPartition() throws Exception {
        return "299912";
    }

    /**
     * Entrega el dialecto de la base de datos.
     *
     * @param pEntityManager Referencia a una session de base de datos.
     * @return String
     * @throws Exception
     */
    public static String getDialect(EntityManager pEntityManager) throws Exception {
        Object dialect = null;
        dialect = pEntityManager.getEntityManagerFactory().getProperties().get("hibernate.dialect");
        return dialect.toString();
    }

    /**
     * Retorna true si el dialecto de base es Oracle.
     *
     * @param pEntityManager Referencia a una session de base de datos.
     * @return boolean
     * @throws Exception
     */
    public static boolean isOracleDialect(EntityManager pEntityManager) throws Exception {
        String dialect = null;
        dialect = (String) pEntityManager.getEntityManagerFactory().getProperties().get("hibernate.dialect");
        if (DialectDB.POSTGRES.getCode().compareTo(dialect) == 0) {
            return true;
        }
        return false;
    }

    /**
     * Retorna true si el dialecto de base es Oracle.
     *
     * @param pEntityManager Referencia a una session de base de datos.
     * @return boolean
     * @throws Exception
     */
    public static boolean isSybaseDialect(EntityManager pEntityManager) throws Exception {
        String dialect = null;
        dialect = (String) pEntityManager.getEntityManagerFactory().getProperties().get("hibernate.dialect");
        if (DialectDB.SYBASE.getCode().compareTo(dialect) == 0) {
            return true;
        }
        return false;
    }

    /**
     * Para poder realizar la comparación los objetos deben tener sobreescrito
     * el metodo equals
     *
     * @param obj E del mismo tipo de la lista
     * @param l Lista de objetos de tipo E
     * @return
     * @throws InvocationTargetException
     * @throws IllegalAccessException
     * @throws
     */
    public static <E> int isInList(E obj, List<E> l, String... equalmethods) throws Exception {
        Method m = null;
        if ((equalmethods != null) && (equalmethods.length > 0) && (equalmethods[0] != null)) {
            m = obj.getClass().getMethod(equalmethods[0], Object.class);
        }
        int i = 0;
        if ((l == null) || l.isEmpty()) {
            return -1;
        }
        for (E item : l) {
            Boolean result = (Boolean) (m != null ? m.invoke(obj, item) : obj.equals(item));
            if (result) {
                return i;
            }
            i++;
        }
        return -1;
    }

    /**
     * Para poder realizar la comparación los objetos deben tener sobreescrito
     * el metodo equals
     *
     * @param l Lista de beans
     * @param obj Bean del mismo tipo de la lista
     * @return E | null
     */
    public static <E> E isInList(List<E> l, E obj) {
        if ((l == null) || l.isEmpty()) {
            return null;
        }
        for (E item : l) {
            if (obj.equals(item)) {
                return item;
            }
        }
        return null;
    }

    public static <T> boolean existRepeated(List<T> lMod) {
        if ((lMod == null) || lMod.isEmpty()) {
            return false;
        }
        HashSet<T> hs = new HashSet<T>();
        for (T item : lMod) {
            hs.add(item);
        }
        return lMod.size() == hs.size() ? false : true;
    }

    public static <T> List<T> getFinalList(List<T> lDB, List<T> lMod, List<T> lDel, String... equalmethods) throws Exception {
        List<T> lFinal = new ArrayList<T>();
        // Quitar eliminados
        if (!lDB.isEmpty()) {
            for (T rdb : lDB) {
                if (Constant.isInList(rdb, lDel, equalmethods) < 0) {
                    lFinal.add(rdb);
                }
            }
        }

        List<T> lFinalCopy = new ArrayList<T>(lFinal);
        // actualizar modificados y agregar nuevos
        if ((lMod != null) && !lMod.isEmpty()) {
            for (T rmod : lMod) {
                int index = Constant.isInList(rmod, lFinalCopy, equalmethods);
                if (index >= 0) {
                    lFinal.set(index, rmod);
                } else {
                    lFinal.add(rmod);
                }
            }
        }
        return lFinal;
    }

    /**
     * Función que transforma una lista de objetos a una lista específica
     *
     * @param lobj Lista de objetos
     * @param cl Clase a la que se va a convertir cada miembro de la lista
     * @return
     * @throws Exception
     */
    @SuppressWarnings("unchecked")
    public static <T> List<T> convertList(List<?> lobj, Class<T> cl) throws Exception {
        List<T> l = new ArrayList<T>();
        if (lobj != null) {
            for (Object obj : lobj) {
                if (cl.isInstance(obj)) {
                    l.add((T) obj);
                }
            }
        }
        return l;
    }

    /**
     * M&eacute;todo que se encarga de formatear mensajes con parametros
     *
     * @param message
     * @param parameters
     * @return
     */
    public static String formatMessage(String message, Object... parameters) {
        if (parameters != null) {
            Object[] parametersAux = parameters;
            for (int i = 0; i < parametersAux.length; i++) {
                Object objeto = parametersAux[i];
                if (objeto instanceof Integer || objeto instanceof Long) {
                    message = message.replaceAll("\\{" + i + "\\}", "{" + i + ",number,#}");
                } else if (objeto instanceof BigDecimal) {
                    message = message.replaceAll("\\{" + i + "\\}", "{" + i + ",number,#,###.##}");
                }
            }
            message = MessageFormat.format(message, parameters);
        }
        return message;
    }
}
