package com.uas.platform.b2b.manage.page;

import com.alibaba.fastjson.JSONObject;
import com.uas.platform.b2b.manage.page.exception.IllegalOperatorException;
import com.uas.platform.b2b.manage.page.criteria.CriterionExpression;
import com.uas.platform.b2b.manage.page.criteria.PredicateUtils;
import com.uas.platform.b2b.manage.support.JxlsExcelView;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.domain.Sort.Direction;

import javax.persistence.criteria.*;
import java.util.*;

/**
 * 分页信息
 * 
 * @author yingp
 * 
 */
public class PageInfo implements Pageable {

	public PageInfo() {
	}

	/**
	 * 分页信息
	 * 
	 * @param jsonPageParams
	 */
	public PageInfo(String jsonPageParams) {
		PageParams params = JSONObject.parseObject(jsonPageParams,
				PageParams.class);
		getPageInfo(params);
	}

	public PageInfo(PageParams params) {
		getPageInfo(params);
	}

	private void getPageInfo(PageParams params) {
		this.pageSize = params.getCount();
		// 不传入页面大小，默认页面大小为5
		if (this.pageSize == 0) {
			this.pageSize = 5;
		}
		if (this.pageSize > JxlsExcelView.MAX_SIZE) {
		    throw new IllegalOperatorException("页面大小不可超过" + JxlsExcelView.MAX_SIZE);
        }
		this.pageNumber = params.getPage();
		// 不传入页码，默认为第一页
		if (this.pageNumber == 0) {
			this.pageNumber = 1;
		}
		this.offset = this.pageSize * (this.pageNumber - 1);
		if (params.getFilter() != null) {
			this.filters = JSONObject.parseObject(params.getFilter());
		}
		if (params.getSorting() != null) {
			JSONObject sorting = JSONObject.parseObject(params.getSorting());
			LinkedList<Sort.Order> orders = new LinkedList<Sort.Order>();
			for (Object property : sorting.keySet()) {
				orders.add(new Sort.Order(Direction.fromString(sorting.get(
						property).toString()), property.toString()));
			}
			sort = new Sort(orders);
		}
	}

	/**
	 * 分页信息
	 * 
	 * @param pageNumber
	 *            页码
	 * @param pageSize
	 *            单页条数
	 */
	public PageInfo(int pageNumber, int pageSize) {
		this.pageNumber = pageNumber;
		this.pageSize = pageSize;
		this.offset = this.pageSize * (this.pageNumber - 1);
	}

	/**
	 * 分页信息
	 * 
	 * @param pageNumber
	 *            页码
	 * @param pageSize
	 *            单页条数
	 */
	public PageInfo(int pageNumber, int pageSize, int offset) {
		this.pageNumber = pageNumber;
		this.pageSize = pageSize;
		this.offset = offset;
	}

	public void filter(String key, Object value) {
		if (filters == null) {
			filters = new HashMap<String, Object>();
		}
		filters.put(key, value);
	}

	public void filter(String key, Object value, Boolean ignoreCase) {
		if (filters == null) {
			filters = new HashMap<String, Object>();
		}
		filters.put(key, value);
		if (ignoreCaseMap == null) {
			ignoreCaseMap = new HashMap<String, Boolean>();
		}
		if (ignoreCase) {
			ignoreCaseMap.put(key, ignoreCase);
		}
	}

	public void filterAll(Map<String, Object> filters) {
		if (this.filters == null) {
			this.filters = new HashMap<String, Object>();
		}
		this.filters.putAll(filters);
	}

	/**
	 * 表达式
	 * 
	 * @param expression
	 */
	public void expression(CriterionExpression expression) {
		if (expression != null) {
			if (expressions == null) {
				expressions = new HashSet<CriterionExpression>();
			}
			expressions.add(expression);
		}
	}

	/**
	 * 添加orExpression
	 * 
	 * @param expression
	 */
	public void orExpression(CriterionExpression expression) {
		if (expression != null) {
			if (orExpressions == null) {
				orExpressions = new HashSet<CriterionExpression>();
			}
			orExpressions.add(expression);
		}
	}

	/**
	 * 排序
	 * 
	 * @param property
	 *            需要排序的字段
	 * @param direction
	 *            方向
	 */
	public void sorting(String property, Direction direction) {
		sort = new Sort(direction, property);
	}

	/**
	 * 排序
	 * 
	 * @param direction
	 *            方向
	 * @param properties
	 *            需要排序的字段
	 */
	public void sorting(Direction direction, String... properties) {
		sort = new Sort(direction, properties);
	}

	private int pageNumber;

	private int pageSize;

	private int offset;

	private Sort sort;

	private Map<String, Object> filters;

	private Set<CriterionExpression> expressions; // 这个里面的条件，全部都用and连接

	private Set<CriterionExpression> orExpressions; // 这个里面的条件，全部都用or连接

	private Map<String, Boolean> ignoreCaseMap; // 是否对这个字段 进行忽略大小写的查询

	public Set<CriterionExpression> getOrExpressions() {
		return orExpressions;
	}

	public void setOrExpressions(Set<CriterionExpression> orExpressions) {
		this.orExpressions = orExpressions;
	}

	@Override
	public int getPageNumber() {
		return pageNumber;
	}

	@Override
	public int getPageSize() {
		return pageSize;
	}

	@Override
	public int getOffset() {
		return offset;
	}

	@Override
	public Sort getSort() {
		return sort;
	}

	public void setPageNumber(int pageNumber) {
		this.pageNumber = pageNumber;
	}

	public void setPageSize(int pageSize) {
		this.pageSize = pageSize;
	}

	public void setOffset(int offset) {
		this.offset = offset;
	}

	public void setSort(Sort sort) {
		this.sort = sort;
	}

	public Map<String, Object> getFilters() {
		return filters;
	}

	public void setFilters(Map<String, Object> filters) {
		this.filters = filters;
	}

	public Set<CriterionExpression> getExpressions() {
		return expressions;
	}

	public void setExpressions(Set<CriterionExpression> expressions) {
		this.expressions = expressions;
	}

	@SuppressWarnings("unchecked")
	public Predicate[] getPredicates(Root<?> root, CriteriaQuery<?> query,
			CriteriaBuilder builder) {
		Predicate[] predicates = new Predicate[] {};
		int i = 0;
		if (filters != null && filters.size() > 0) {
			Set<String> keys = filters.keySet();
			predicates = new Predicate[keys.size()];
			Object value = null;
			for (String key : keys) {
				value = filters.get(key);
				if (value instanceof Number) {
					Path<Number> param = PredicateUtils.getPath(root, key,
							Number.class);
					predicates[i++] = builder.equal(param, value);
				} else if (value instanceof Map) {
					predicates[i++] = getPredicate(root, query, builder, key,
							(Map<String, Object>) value);
				} else {
					Path<String> param = PredicateUtils.getPath(root, key,
							String.class);
					if ((ignoreCaseMap == null)
							|| (ignoreCaseMap.get(key) == null)
							|| (!ignoreCaseMap.get(key))) {
						predicates[i++] = builder.like(param,
								"%" + value.toString() + "%");
					} else { // 对数据的大小写忽略
						predicates[i++] = builder.like(builder.upper(param),
								"%" + value.toString().toUpperCase() + "%");
					}
				}
			}
		}
		if (expressions != null && expressions.size() > 0) {
			predicates = Arrays.copyOf(predicates, predicates.length
					+ expressions.size());
			for (CriterionExpression expression : expressions) {
				predicates[i++] = expression.toPredicate(root, query, builder);
			}
		}

		if (orExpressions != null && orExpressions.size() > 0) {
			predicates = Arrays.copyOf(predicates, predicates.length + 1);
			Predicate[] orPredicates = new Predicate[] {};
			orPredicates = new Predicate[orExpressions.size()];
			int j = 0;
			for (CriterionExpression expression : orExpressions) {
				orPredicates[j++] = expression
						.toPredicate(root, query, builder);
			}
			if (orPredicates.length == 1) {
				predicates[i++] = orPredicates[0];
			} else {
				predicates[i++] = builder.or(orPredicates);
			}
		}
		return predicates;
	}

	/**
	 * 传入单个字段的多个过滤条件<br>
	 * 
	 * @example {"filters":{"status":
	 *          200,"date":{"gted":1441269176697,"lted":1441269176698}}}
	 * 
	 * @param root
	 * @param query
	 * @param builder
	 * @param field
	 *            字段名
	 * @param valueMap
	 *            条件map
	 * @return
	 */
	private Predicate getPredicate(Root<?> root, CriteriaQuery<?> query,
                                   CriteriaBuilder builder, String field, Map<String, Object> valueMap) {
		Set<String> keys = valueMap.keySet();
		Object val = null;
		Predicate[] predicates = new Predicate[keys.size()];
		int i = 0;
		for (String valKey : keys) {
			val = valueMap.get(valKey);
			if ("ne".equals(valKey)) {// 不等于 {"ne": "something"}
				Path<String> path = PredicateUtils.getPath(root, field,
						String.class);
				if (val != null) {
					predicates[i++] = builder.notEqual(path,
							String.valueOf(val));
				} else {
					predicates[i++] = builder.isNotNull(path);
				}
			} else if ("eq".equals(valKey)) {// 等于 {"eq": "something"}
				Path<String> path = PredicateUtils.getPath(root, field,
						String.class);
				if (val != null) {
					predicates[i++] = builder.equal(path, String.valueOf(val));
				} else {
					predicates[i++] = builder.isNull(path);
				}
			} else if ("gt".equals(valKey)) {// 大于 {"gt": "something"}
				Path<String> path = PredicateUtils.getPath(root, field,
						String.class);
				predicates[i++] = builder
						.greaterThan(path, String.valueOf(val));
			} else if ("lt".equals(valKey)) {// 小于 {"lt": "something"}
				Path<String> path = PredicateUtils.getPath(root, field,
						String.class);
				predicates[i++] = builder.lessThan(path, String.valueOf(val));
			} else if ("gte".equals(valKey)) {// 大于等于 {"gte": "something"}
				Path<String> path = PredicateUtils.getPath(root, field,
						String.class);
				predicates[i++] = builder.greaterThanOrEqualTo(path,
						String.valueOf(val));
			} else if ("lte".equals(valKey)) {// 小于等于 {"lte": "something"}
				Path<String> path = PredicateUtils.getPath(root, field,
						String.class);
				predicates[i++] = builder.lessThanOrEqualTo(path,
						String.valueOf(val));
			} else if ("gtd".equals(valKey)) {// 日期大于 {"gtd": 1441269176697}
				Path<Date> path = PredicateUtils.getPath(root, field,
						Date.class);
				predicates[i++] = builder.greaterThan(path,
						new Date(Long.parseLong(String.valueOf(val))));
			} else if ("ltd".equals(valKey)) {// 日期小于 {"ltd": 1441269176697}
				Path<Date> path = PredicateUtils.getPath(root, field,
						Date.class);
				predicates[i++] = builder.lessThan(path,
						new Date(Long.parseLong(String.valueOf(val))));
			} else if ("gted".equals(valKey)) {// 日期大于等于 {"gted": 1441269176697}
				Path<Date> path = PredicateUtils.getPath(root, field,
						Date.class);
				predicates[i++] = builder.greaterThanOrEqualTo(path, new Date(
						Long.parseLong(String.valueOf(val))));
			} else if ("lted".equals(valKey)) {// 日期小于等于 {"lted": 1441269176697}
				Path<Date> path = PredicateUtils.getPath(root, field,
						Date.class);
				predicates[i++] = builder.lessThanOrEqualTo(path,
						new Date(Long.parseLong(String.valueOf(val))));
			} else {
				Path<String> path = PredicateUtils.getPath(root, field,
						String.class);
				predicates[i++] = builder.equal(path, String.valueOf(val));
			}
		}
		return builder.and(predicates);
	}

	@Override
	public Pageable next() {
		return null;
	}

	@Override
	public Pageable previousOrFirst() {
		return null;
	}

	@Override
	public Pageable first() {
		return null;
	}

	@Override
	public boolean hasPrevious() {
		return false;
	}

	public Pageable getPageable() {
		final Pageable scope = this;
		return new Pageable() {

			@Override
			public Pageable previousOrFirst() {
				return scope.previousOrFirst();
			}

			@Override
			public Pageable next() {
				return scope.next();
			}

			@Override
			public boolean hasPrevious() {
				return scope.hasPrevious();
			}

			@Override
			public Sort getSort() {
				return scope.getSort();
			}

			@Override
			public int getPageSize() {
				return scope.getPageSize();
			}

			@Override
			public int getPageNumber() {
				return scope.getPageNumber();
			}

			@Override
			public int getOffset() {
				return scope.getOffset();
			}

			@Override
			public Pageable first() {
				return scope.first();
			}
		};
	}

}
