package cn.com.liandisys.ycjt.ggcx.admin.utils;

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

import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaBuilder.In;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Path;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;

import org.apache.commons.lang3.StringUtils;
import org.springframework.data.jpa.domain.Specification;

/**
 * Create Specification in the sense of Domain Driven Design
 * 
 * @author 袁晓冬
 * 
 */
public class SpecificationCreater {

    /**
     * 根据PredicateModel集合构造查询
     * 
     * @param pms
     * @param clazz
     * @return
     */
    public static <T> Specification<T> searchByPredicateModels(Collection<PredicateModel> pms) {

        return new ModelSpecification<T>(pms);
    }

    /**
     * 可以处理PredicateModel模型的Specification
     * 
     * @author 袁晓冬
     * 
     * @param <T>
     */
    public static class ModelSpecification<T> implements Specification<T> {

        private PredicateModel predicateModel;

        public ModelSpecification(Collection<PredicateModel> pms) {

            this.predicateModel = new PredicateModel(PredicateModel.JoinType.AND, pms);
        }

        @Override
        public Predicate toPredicate(Root<T> root, CriteriaQuery<?> query, CriteriaBuilder cb) {

            if (predicateModel == null
                    || (predicateModel.getFieldName() == null && !predicateModel.hasSubPredicateModel())) {
                // 无查询条件
                return cb.conjunction();
            }
            return toPredicateFromModel(predicateModel, root, cb);
        }

        @SuppressWarnings({ "unchecked", "rawtypes" })
        private Predicate toPredicateFromModel(PredicateModel pm, Root<T> root, CriteriaBuilder cb) {

            if (pm.hasSubPredicateModel()) {// 处理下级模型
                List<Predicate> predicateList = new ArrayList<Predicate>();
                for (PredicateModel p : pm.getSubPms()) {
                    predicateList.add(toPredicateFromModel(p, root, cb));
                }
                Predicate[] predicates = new Predicate[predicateList.size()];
                predicateList.toArray(predicates);
                switch (pm.getJoinType()) {
                    case AND:
                        return cb.and(predicates);
                    case OR:
                        return cb.or(predicates);
                    default:
                        return cb.and(predicates);
                }
            } else {// 无下级模型时
                // nested path translate, 如Task的名为"user.name"的filedName,
                // 转换为Task.user.name属性
                String[] names = StringUtils.split(pm.getFieldName(), ".");
                Path expression = root.get(names[0]);
                for (int i = 1; i < names.length; i++) {
                    expression = expression.get(names[i]);
                }
                // logic operator
                // 将查询条件中的%和_转译，并在like查询时用escape条件过滤，使得%和_在like查询是不再当做特殊字符处理
                String pattern = "";
                if (null != pm.getValue()) {
                    pattern = pm.getValue().toString().toUpperCase();
                    pattern = pattern.replaceAll("%", "\\\\%");
                    pattern = pattern.replaceAll("_", "\\\\_");
                }
                switch (pm.getOperator()) {
                    case IN:
                        In<Object> in = cb.in(expression);
                        for (Object id : (Iterable<Object>) pm.getValue()) {
                            in.value(id);
                        }
                        return in;
                    case EQ:
                        return cb.equal(expression, pm.getValue());
                    case LIKE:
                        return cb.like(cb.upper(expression), "%" + pattern + "%", '\\');
                    case LIKE_L:
                        return cb.like(cb.upper(expression), "%" + pattern, '\\');
                    case LIKE_R:
                        return cb.like(cb.upper(expression), pattern + "%", '\\');
                    case GT:
                        return cb.greaterThan(expression, (Comparable) pm.getValue());
                    case LT:
                        return cb.lessThan(expression, (Comparable) pm.getValue());
                    case GTE:
                        return cb.greaterThanOrEqualTo(expression, (Comparable) pm.getValue());
                    case LTE:
                        return cb.lessThanOrEqualTo(expression, (Comparable) pm.getValue());
                    case NL:
                        return cb.isNull(expression);
                    case NNL:
                        return cb.isNotNull(expression);
                    case NEQ:
                        return cb.notEqual(expression, pm.getValue());
                    default:
                        return cb.equal(expression, pm.getValue());
                }
            }
        }
    }
}
