package com.fp.bpm.query;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.hibernate.Criteria;
import org.hibernate.ScrollMode;
import org.hibernate.ScrollableResults;
import org.hibernate.criterion.Criterion;
import org.hibernate.criterion.Order;
import org.hibernate.criterion.Property;
import org.hibernate.criterion.Restrictions;

import com.fp.common.helper.BeanManager;
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.PersistenceHelper;
import com.fp.persistence.commondb.exception.CommondbException;

public class QueryProcessCriteria {
	public QueryProcessCriteria() {
	}

	public boolean process(QueryRequest pReq, Response pRes) throws CommondbException, ClassNotFoundException, InstantiationException, IllegalAccessException {
		Map<String, QueryBean> bean = pReq.getBeans();
		for (String k : bean.keySet()) {
			QueryBean qbean = bean.get(k);
			pRes.put(k, this.process(qbean));
		}
		return true;
	}

	private Object process(QueryBean pQuery) throws CommondbException, ClassNotFoundException, InstantiationException, IllegalAccessException {
		Criteria cri = this.prepareCriteria(pQuery);
		return this.fillData(cri, pQuery.isMultirecord());
	}

	private Object fillData(Criteria pCriteria, boolean pMultirecord) {

		if (pMultirecord) {
			List<Object> data = new ArrayList<Object>();
			ScrollableResults scroll = pCriteria.scroll(ScrollMode.FORWARD_ONLY);
			while (scroll.next()) {
				data.add(this.fillRecord(scroll.get()));
			}
			return data;
		} else {
			return pCriteria.uniqueResult();
		}

	}

	private Object fillRecord(Object[] pRecord) {
		if (pRecord == null) {
			return null;
		}
		if (pRecord.length == 1) {
			return pRecord[0];
		} else {
			Map<String, Object> m = new HashMap<String, Object>();
			return m;
		}
	}

	@SuppressWarnings("unchecked")
	private Criteria prepareCriteria(QueryBean pQuery) throws CommondbException, ClassNotFoundException, InstantiationException, IllegalAccessException {
    Object beanReference=Class.forName(pQuery.getBeanName()).newInstance();
		Criteria c = PersistenceHelper.getSession().createCriteria(pQuery.getBeanName());

		for (QueryCriteria criteria : pQuery.getCriteria()) {
      Class type=null;
      try{
        type=BeanManager.getBeanGetterMethod(beanReference, criteria.getProperty()).getReturnType();
      }catch(Exception e1){
      }
			c.add(this.prepareCriterion(criteria,type));
		}
		for (QueryCriteria order : pQuery.getOrder(true)) {
			c.addOrder(Order.asc(order.getProperty()));
		}
		for (QueryCriteria order : pQuery.getOrder(false)) {
			c.addOrder(Order.desc(order.getProperty()));
		}
		if (pQuery.getPage() > 0 && pQuery.getPageSize() > 0) {
			c.setMaxResults(pQuery.getPageSize());
			c.setFirstResult((pQuery.getPage() - 1) * pQuery.getPageSize());
		}

		return c;
	}

	@SuppressWarnings("unchecked")
	private Criterion prepareCriterion(QueryCriteria pCriteria,Class pType) throws CommondbException {
		Property prop = Property.forName(pCriteria.getProperty());
    Object value=pCriteria.getValue();
    try{
        value=BeanManager.convertObject(pCriteria.getValue(),pType);
    }catch(Exception e){}
		if (pCriteria.getOperation().compareTo("<") == 0) {
			return prop.lt(value);
		}
		if (pCriteria.getOperation().compareTo(">") == 0) {
			return prop.gt(value);
		}
		if (pCriteria.getOperation().compareTo("<=") == 0) {
			return prop.le(value);
		}
		if (pCriteria.getOperation().compareTo(">=") == 0) {
			return prop.ge(value);
		}
		if (pCriteria.getOperation().compareTo("=") == 0) {
			return prop.eq(value);
		}
		if (pCriteria.getOperation().compareTo("<>") == 0) {
			return prop.ne(value);
		}
		if (pCriteria.getOperation().compareTo("like") == 0) {
			return prop.like(value);
		}
		if (pCriteria.getOperation().compareTo("isNull") == 0) {
			return prop.isNull();
		}
		if (pCriteria.getOperation().compareTo("isNotNull") == 0) {
			return prop.isNotNull();
		}
		if (pCriteria.getOperation().compareTo("in") == 0) {
			return prop.in((Collection) pCriteria.getValue());
		}
		if (pCriteria.getOperation().compareTo("between") == 0) {
			Object[] param = ((Collection) pCriteria.getValue()).toArray();
			return prop.between(param[0], param[1]);
		}
		if (pCriteria.getOperation().compareTo("sql") == 0) {
			return Restrictions.sqlRestriction((String) value);
		}
		throw new CommondbException("QRY001", "OPERADOR {0} NO DISPONIBLE", pCriteria.getOperation());
	}
}
