package com.fp.bpm.query;

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

import com.fp.common.exception.CommonException;
import com.fp.common.exception.SavePointException;
import com.fp.common.logger.APPLogger;
import com.fp.dto.query.QueryBean;
import com.fp.dto.query.QueryRequest;
import com.fp.dto.rules.QueryRule;
import com.fp.dto.rules.TransactionRule;
import com.fp.persistence.commondb.db.DataHelper;
import com.fp.persistence.commondb.exception.CommondbException;
import com.fp.persistence.pgeneral.gene.TgeneQueryProcess;
import com.fp.persistence.pgeneral.gene.TgeneTransactionQuery;

/**
 * Motor de consultas, el proceso se ejecuta de acuerodo a querytype.<br>
 * 
 * @author Jorge Vaca
 * @version 2.1
 */
public class QueryProcessor {

    /** Objeto con beans utilizados en el proceso de consulta. */
    private QueryRequest queryRequest;

    /**
     * Metodo que se encarga de ejecutar consultas contra la base de datos, adicionalmente ejecuta consultas especiales
     * si estas estarian parametrizadas. Proceso tipo de consulta.<br>
     * "P" Ejecuta consulta por reglas asociadas a un codigo de consulta. "T" Ejecuta consulta por reglas asociadas a la
     * transaccion. "N" Ejecuta consultas genericas.
     * 
     * @param pQueryRequest Objeto con beans utilizados en el proceso de consulta.
     * @param pResponse Objeto en el cual se alamcena la respuesta de una transaccion.
     * @return boolean
     * @throws CommondbException
     * @throws Exception
     */
    public boolean process(QueryRequest pQueryRequest) throws CommondbException, Exception {
        Thread.currentThread().setName(pQueryRequest.getJournalId());
        this.queryRequest = pQueryRequest;
        String queryType = (String) pQueryRequest.get("querytype");

        if ((queryType == null) || (queryType.compareTo("N") == 0)) {
        	if(!pQueryRequest.isJsf()){
                Query qry = new Query();
                qry.process(pQueryRequest);        		
        	}else{
        		QueryJsf qry = new QueryJsf();
        		qry.execute(pQueryRequest);
        	}

        }
        // Ejecuta consultas especiales para una transaccion.
        if ((queryType != null) && (queryType.compareTo("T") == 0)) {
            this.processByTransaction();
        }
        // Ejecuta consultas especiales para un codigo de query.
        if ((queryType == null) || (queryType.compareTo("P") == 0)) {
            // Si es null entra el contro esta en el metodo interno
            this.processQueryByAlias();
        }
        return true;
    }

    /**
     * Metodo que se encarga de ejecutar comandos especiales de consulta por queryalias.
     * 
     * @param pQueryRequest Objeto con beans utilizados en el proceso de consulta.
     * @param pResponse Objeto que contiene los datos de respuesta de la consulta.
     * @throws Exception
     */
    public void processQueryByAlias() throws Exception {
        Map<String, QueryBean> bean = this.queryRequest.getBeans();
        for (String k : bean.keySet()) {
            QueryBean qbean = bean.get(k);
            if (qbean.getQueryalias() != null) {
                this.processByQueryCode(qbean.getQueryalias());
            }
        }
        String queryalias = (String) this.queryRequest.get("queryalias");
        if (queryalias != null) {
            this.processByQueryCode(queryalias);
        }
    }

    /**
     * Fija un nuevo valor en queryRequest
     * 
     * @param pQueryRequest nuevo valor para queryRequest
     */
    public void setQueryRequest(QueryRequest pQueryRequest) {
        this.queryRequest = pQueryRequest;
    }

    /**
     * Metodo que se encarga de ejecutar procesos especiales de consulta por codigo de consulta.
     * 
     * @param pQueryRequest Objeto con beans utilizados en el proceso de consulta.
     * @param pQueryCode Codigo de consulta.
     * @throws Exception
     */
    private void processByQueryCode(String pQueryCode) throws Exception {
        List<TgeneQueryProcess> lprocess = DataHelper.getInstance().getTgeneQueryProcess(pQueryCode);
        if (lprocess == null) {
            return;
        }
        for (TgeneQueryProcess obj : lprocess) {
            this.processByCode(obj.getProcesscode(), pQueryCode);
        }
    }

    /**
     * 
     * @param pResponse Objeto con beans utilizados en el proceso de consulta.
     * @param pProcess Codigo de proceso a ejecutar.
     * @param pQueryCode Codigo de consulta.
     * @throws Exception
     */
    private void processByCode(String pProcess, String pQueryCode) throws Exception {
        QueryRule rule = null;
        try {
            rule = (QueryRule) Class.forName(pProcess).newInstance();
        } catch (ClassNotFoundException e) {
            throw new CommonException("COMMON-0012", "CLASE {0} A EJECUTAR NO EXISTE CODE: {0}", e, pQueryCode);
        } catch (ClassCastException e) {
            throw new CommonException("COMMON-0013", "PROCESO {0} A EJECUTAR CODE: {1} NO IMPLEMENTA QUERYRULE", e, pProcess, pQueryCode);
        }
        APPLogger.getLogger().info("INICIO ==> " + rule);
        rule.process(this.queryRequest);
        APPLogger.getLogger().info("FIN ==> " + rule);
    }

    /**
     * Metodo que ejecuta procesos especiales de consulta por transaccion.
     * 
     * @param pQueryRequest Objeto con beans utilizados en el proceso de consulta.
     * @throws Exception
     */
    private void processByTransaction() throws Exception {
        if ((this.queryRequest.getTransactionModule() == null) || (this.queryRequest.getTransactionCode() == null)) {
            return;
        }
        List<TgeneTransactionQuery> lquery = DataHelper.getInstance().getTgeneTransactionQuery(this.queryRequest.getTransactionModule(),
                this.queryRequest.getTransactionCode(), this.queryRequest.getTransactionVersion());
        if (lquery == null) {
            return;
        }
        for (TgeneTransactionQuery obj : lquery) {
            try {
                this.processByTransactionRule(obj);
            } catch (SavePointException e) {
                // Detiene la ejecucion de componentes de negocio y graba la transaccion.
                break;
            }

        }
    }

    /**
     * Metodo encargado de ejecutar cada una de las reglas definidas en la transaccion.
     * 
     * @param pSaveRequest Objeto con beans utilizados en el proceso de una transaccion.
     * @param pProcess Objeto que contiene la regla a ejecutar.
     * @throws Exception
     */
    private void processByTransactionRule(TgeneTransactionQuery pQuery) throws Exception {
        QueryRule rule = null;
        try {
            rule = (QueryRule) Class.forName(pQuery.getProcesscode()).newInstance();
        } catch (ClassNotFoundException e) {
            throw new CommonException("COMMON-0014", "CLASE {0} A EJECUTAR NO EXISTE  MODULE: {1} TRANS: {2} VERSION {3}", e,
                    pQuery.getProcesscode(), this.queryRequest.getTransactionModule(), this.queryRequest.getTransactionCode(),
                    this.queryRequest.getTransactionVersion());
        } catch (ClassCastException e) {
            throw new CommonException("COMMON-0015", "PROCESO {0} A EJECUTAR MODULE: {1} TRANS: {2} VERSION {3} NO IMPLEMENTA {4}", e,
                    pQuery.getProcesscode(), this.queryRequest.getTransactionModule(), this.queryRequest.getTransactionCode(),
                    this.queryRequest.getTransactionVersion(), TransactionRule.class.getName());
        }
        rule.setFlow(pQuery.getProcessname());
        rule.setRule(pQuery.getRulename());
        APPLogger.getLogger().info("INICIO ==> " + rule);
        rule.process(this.queryRequest);
        APPLogger.getLogger().info("FIN ==> " + rule);
    }
}
