package com.fp.bpm.query;


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

import com.fp.common.helper.BeanManager;
import com.fp.common.logger.APPLogger;
import com.fp.dto.AbstractDataTransport;
import com.fp.dto.Response;
import com.fp.dto.query.QueryBean;
import com.fp.dto.query.QueryCriteria;
import com.fp.dto.query.QueryRequest;
import com.fp.persistence.commondb.HqlStatement;
import com.fp.persistence.commondb.exception.CommondbException;
import java.util.ArrayList;
import java.util.Hashtable;

/**
 * Clase que se encarga de realizar consultas genericas a la base de datos.
 *
 * @author Jorge Vaca.
 * @version 2.1
 */
public class Query extends QueryHelper { // NOPMD by jorge on 12/10/10 21:44

    /**
     * Numero maximo de registros a entregar en en la consulta, cuando se trate de una consulta multiregistro.
     */
    private int maxresults;

    /**
     * Metodo principal que inicia la consulta de datos, El objeto QueryRequest contiene una lista de objetos QueryBean,
     * para cada uno de estos objetos arma un Query le asocia parametros y ejecuta la consulta.
     *
     * @param pQueryRequest Objeto que contiene una lista de objetos QueryBean.
     * @return boolean
     * @throws CommondbException
     * @throws ClassNotFoundException
     * @throws InstantiationException
     * @throws IllegalAccessException
     * @throws Exception
     */
    public boolean process(QueryRequest pQueryRequest) throws CommondbException, ClassNotFoundException, InstantiationException,
            IllegalAccessException, Exception {
        Response response = pQueryRequest.getResponse();
        Map<String, QueryBean> bean = pQueryRequest.getBeans();
        for (String k : bean.keySet()) {
            QueryBean qbean = bean.get(k);
            if ((qbean.getBeanName() == null)) {
                continue;
            }
            response.put(k, this.processByQueryBean(qbean));
        }
        this.query = null;
        return true;
    }

    /**
     * Metodo que se encarga de ejecutar la consulta por QueryBean, genera dicamente la sentencia, adiciona parametros
     * ejecuta la consulta y arma la respuesta de los datos del bean.
     *
     * @param pQueryBean Objeto que contiene el bean a consultas, y las restricciones.
     * @return Object
     * @throws CommondbException
     * @throws ClassNotFoundException
     * @throws InstantiationException
     * @throws IllegalAccessException
     * @throws Exception
     */
    private Object processByQueryBean(QueryBean pQueryBean) throws CommondbException, ClassNotFoundException, InstantiationException,
            IllegalAccessException, Exception {
        if ((pQueryBean.getQuerytype() != null) && (pQueryBean.getQuerytype().compareTo("E") == 0)) {
            if (pQueryBean.isMultirecord()) {
                List<AbstractDataTransport> data = new ArrayList<AbstractDataTransport>();
                data.add((AbstractDataTransport) this.createObject(pQueryBean));
                return data;
            }
            return this.createObject(pQueryBean);
        }
        this.buildSenetnce(pQueryBean);
        // return null;
        return this.queryData(pQueryBean);
    }

    /**
     * Metodo que se encarga de armar la sentencia del query, adiciona restricciones y criterios de ordenamiento de los
     * resultados.
     *
     * @param pQueryBean Objeto que contiene el bean a consultas, y las restricciones.
     * @throws CommondbException
     * @throws ClassNotFoundException
     * @throws InstantiationException
     * @throws IllegalAccessException
     * @throws Exception
     */
    @SuppressWarnings("rawtypes")
    private void buildSenetnce(QueryBean pQueryBean) throws CommondbException, ClassNotFoundException, // NOPMD by jorge
            // on 12/10/10
            // 21:44
            InstantiationException, IllegalAccessException, Exception {
        Object beanReference = Class.forName(pQueryBean.getBeanName()).newInstance();
        this.query = new StringBuffer();
        this.mParameters = new Hashtable<String, Object>();
        this.query.append("FROM " + pQueryBean.getBeanName() + " t ");
        boolean first = true;
        for (QueryCriteria criteria : pQueryBean.getCriteria()) {
            String condition = criteria.getOperation();
            if ((condition != null) && ((condition.compareTo("isNull") == 0) || (condition.compareTo("isNotNull") == 0))) {
                criteria.setValue("");
            }
            if ((criteria.getValue() == null)
                    && ((condition == null) || (condition.compareTo("isNull") != 0) || (condition.compareTo("isNotNull") != 0))) {
                continue;
            }
            if (first) {
                this.query.append(" WHERE ");
                first = false;
            } else {
                this.query.append(" AND ");
            }
            if (criteria.getOperation() == null) {
                continue;
            }
            Class type = null;
            try {
                type = BeanManager.getBeanGetterMethod(beanReference, criteria.getProperty()).getReturnType();
            } catch (Exception e1) {
            }
            super.addRestrictions(criteria, type);
        }
        super.addOrder(pQueryBean);
        if ((pQueryBean.getPage() > 0) && (pQueryBean.getPageSize() > 0)) {
            this.maxresults = pQueryBean.getPageSize();
        }
    }

    /**
     * Metodo que se encarga de ejecutar la consulta, crea un HqlStatement, fija los parametros y arma los datos de
     * respuesta.
     *
     * @param pQueryBean Objeto que contiene el bean a consultas, y las restricciones.
     * @return Object
     * @throws CommondbException
     * @throws ClassNotFoundException
     * @throws InstantiationException
     * @throws IllegalAccessException
     * @throws Exception
     */
    @SuppressWarnings("unchecked")
    private Object queryData(QueryBean pQueryBean) throws CommondbException, ClassNotFoundException, InstantiationException, IllegalAccessException,
            Exception {
        String searchCatalogDetail = (String) pQueryBean.get("catalogdesc");
        String[] nameAttributes = null;
        if (searchCatalogDetail != null) {
            nameAttributes = searchCatalogDetail.split("\\*");
        }
        APPLogger.getLogger().debug("CONSULTA: " + super.query.toString());
        HqlStatement hql = new HqlStatement(super.query.toString());
        hql.setParametersValue(super.mParameters);
        hql.setPage(pQueryBean.getPage());
        hql.setRecordperpage(this.maxresults);
        if (pQueryBean.isMultirecord()) {
            List<Object> data = new ArrayList<Object>();
            List<Object> ldata = hql.execute().getResultList();
            boolean exists = false;
            for (Object object : ldata) {
                exists = true;
                data.add(this.fillRecord(pQueryBean, object, nameAttributes));
            }
            if (!exists) {
                Object obj = this.createObject(pQueryBean);
                data.add(obj);
            }
            return (data);
        } else {
            AbstractDataTransport obj = (AbstractDataTransport) hql.getObject();
            super.completeDescriptionCatalog(obj, nameAttributes);
            this.addAditionData(pQueryBean, obj);
            return obj;
        }
    }
}
