package com.fp.persistence.commondb;

import java.util.List;
import java.util.Map;
import java.util.Set;

import javax.persistence.Query;

import com.fp.common.helper.BeanManager;
import com.fp.common.logger.APPLogger;
import com.fp.dto.query.DtoQuery;
import com.fp.dto.query.Filter;
import com.fp.dto.query.QueryBean;
import com.fp.dto.query.QueryCriteria;

/**
 * Clase que agrega los parametros y valores a una consulta
 * 
 * @author wpatiño
 */
public class GeneralQuery {

    /**
     * Método que agrega los parámetros de una consulta
     * 
     * @param qb
     * @param jpql
     * @param mcriteria
     * @throws Exception
     */
    public static void addParameters(QueryBean qb, StringBuilder jpql, Map<String, Object> mcriteria) throws Exception {
        GeneralQuery.addParameters(qb, jpql, mcriteria, false);
    }

    /**
     * Método que agrega los parámetros de una consulta
     * 
     * @param qb
     * @param jpql
     * @param mcriteria
     * @throws Exception
     */
    public static void addParameters(QueryBean qb, StringBuilder jpql, Map<String, Object> mcriteria, boolean isnative) throws Exception {
        if (qb == null) {
            return;
        }
        List<QueryCriteria> lcriteria = qb.getCriteria();
        for (QueryCriteria obj : lcriteria) {
            String condition = obj.getOperation();
            if (obj.getProperty() == null || obj.getValue() == null && condition == null) {
                continue;
            }
            String aux = obj.getProperty();
            if (isnative) {
                if (aux.indexOf(".") > 0) {
                    // Es if va en este punto por que el in puede llegar en un campo de llave primaria o en cualquier
                    // otro campo.
                    aux = aux.substring(3, aux.length());
                }
                String param = obj.getProperty();
                if (condition.compareTo("in") == 0 || condition.compareTo("not in") == 0) {
                    jpql.append(" and t.").append(aux).append(" ").append(obj.getOperation()).append(" (").append(obj.getValue()).append(") ");
                    aux = null;
                } else {
                    jpql.append(" and t.").append(aux).append(" ").append(obj.getOperation()).append(" :").append(param);
                    aux = param;
                }

            } else {
                aux = aux.replace(".", "_");
                jpql.append(" and t.").append(obj.getProperty()).append(" ").append(obj.getOperation()).append(" :").append(aux);
            }

            if (aux != null) {
                mcriteria.put(aux, obj.getValue());
            }

        }
        // Order by de la sentencia
        boolean first = true;
        for (QueryCriteria order : qb.getOrder(true)) {
            if (first) {
                jpql.append(" ORDER BY ");
            } else {
                jpql.append(" ,");
            }
            jpql.append("t.").append(order.getProperty()).append(" asc ");
            first = false;
        }
    }

    /**
     * Metodo que agrega el valos a los parametros para la consulta
     * 
     * @param qb
     * @param mcriteria
     * @param qry
     * @throws Exception
     */
    public static void setParameters(QueryBean qb, Map<String, Object> mcriteria, Query qry) throws Exception {
        if (qb == null) {
            return;
        }
        Object beanReference = Class.forName(qb.getBeanName()).newInstance();
        Set<String> s = mcriteria.keySet();
        Class type = null;
        for (String key : s) {
            try {
                String aux = key;
                aux = aux.replace("_", ".");
                type = BeanManager.getBeanGetterMethod(beanReference, aux).getReturnType();
            } catch (Exception e1) {
                APPLogger.getLogger().error(e1.getStackTrace());
            }
            Object value = mcriteria.get(key);
            try {
                value = BeanManager.convertObject(value, type);
            } catch (Exception e) {
            }
            qry.setParameter(key, value);
        }
        Integer page = qb.getPage();
        if (page != null && page >= 0 && qb.getPageSize() > 0) {
            if (page > 1) {
                qry.setFirstResult((qb.getPage() - 1) * qb.getPageSize());
            }
            qry.setMaxResults(qb.getPageSize());
        }
    }

    public static void paginate(QueryBean qb, Query qry) throws Exception {
        Integer page = new Integer(qb.getPage());
        Integer recordperpage = new Integer(qb.getPageSize());
        if (page != null && recordperpage != null && page >= 0 && recordperpage > 0) {
            if (page > 1) {
                qry.setFirstResult((page - 1) * recordperpage);
            }
            qry.setMaxResults(recordperpage);
        }
    }

    public static void addParametersJSF(StringBuilder jpql, DtoQuery dto) {
        GeneralQuery.addParametersJSF(jpql, dto, true);
    }

    /**
     * Agrega parametros a una consulta JSF
     * 
     * @param jpql Consulta
     * @param dto Objeto data transport
     */
    public static void addParametersJSF(StringBuilder jpql, DtoQuery dto, boolean addwhere) {
        List<Filter> lfilters = dto.getLfilter();
        boolean first = addwhere;
        String aux = " and ";
        if (!lfilters.isEmpty()) {
            for (Filter filter : lfilters) {
                String condition = filter.getCondition() != null ? "=" : filter.getValue().indexOf("%") >= 0 ? "like" : "=";
                jpql.append(aux).append("t." + filter.getField() + " ").append(condition).append(" :" + filter.getField().replace(".", "_"));
            }
        }
    }

    /**
     * Fija el valor a parametros de una consulta JSF
     * 
     * @param jpql Consulta
     * @param dto Objeto data transport
     * @param
     */
    public static void setParametersJSF(StringBuilder jpql, DtoQuery dto, Query qry) throws Exception {
        List<Filter> lfilters = dto.getLfilter();
        Object beanReference = Class.forName(dto.getBeanname()).newInstance();
        try {
            Object beankey = Class.forName(dto.getBeanname() + "Key").newInstance();
            if (beankey != null) {
                BeanManager.setBeanAttributeValue(beanReference, "pk", beankey);
            }
        } catch (Exception e) {
            // No hacer nada
        }

        Class type = null;
        if (!lfilters.isEmpty()) {
            for (Filter filter : lfilters) {
                type = BeanManager.getBeanGetterMethod(beanReference, filter.getField()).getReturnType();
                Object value = BeanManager.convertObject(filter.getValue(), type);
                qry.setParameter(filter.getField().replace(".", "_"), value);
            }
        }
        Integer pagina = dto.getPage();
        Integer numreg = dto.getRecperpage() == null ? 10 : dto.getRecperpage();
        // Control de paginacion.
        if (dto.isMultirecord()) {
            qry.setFirstResult(pagina);
            qry.setMaxResults(numreg);
        }
    }

    /**
     * Fija el valor a parametros de una consulta JSF
     * 
     * @param jpql Consulta
     * @param dto Objeto data transport
     * @param
     */
    public static void setParametersNative(StringBuilder jpql, DtoQuery dto, Query qry) throws Exception {
        List<Filter> lfilters = dto.getLfilter();
        if (!lfilters.isEmpty()) {
            for (Filter filter : lfilters) {
                qry.setParameter(filter.getField().replace(".", "_"), filter.getValue());
            }
        }
        Integer page = dto.getPage();
        if (page != null && page >= 0 && dto.getRecperpage() > 0) {
            if (page > 1) {
                qry.setFirstResult((dto.getPage() - 1) * dto.getRecperpage());
            }
            qry.setMaxResults(dto.getRecperpage());
        }
    }
}
