package com.chenyang.nse.bussiness.dao;

import java.io.Serializable;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.Collection;
import java.util.List;
import javax.persistence.EntityManagerFactory;
import org.hibernate.Criteria;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.criterion.Criterion;
import org.hibernate.criterion.Order;
import org.hibernate.criterion.Projections;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.ObjectUtils;

@Transactional
public class BaseDao<T, PK extends Serializable> implements IBaseDao<T, PK> {
   @Autowired
   public SessionFactory sessionFactory;
   public Session session;
   protected Class<T> entityClass = null;
   @Autowired
   private EntityManagerFactory entityManagerFactory;

   public BaseDao() {
      Class c = this.getClass();
      Type t = c.getGenericSuperclass();
      if (t instanceof ParameterizedType) {
         Type[] p = ((ParameterizedType)t).getActualTypeArguments();
         this.entityClass = (Class)p[0];
      }

   }

   public Session getSession() {
      return ((SessionFactory)this.entityManagerFactory.unwrap(SessionFactory.class)).getCurrentSession();
   }

   public T get(PK id) {
      return (T)this.getSession().get(this.entityClass, id);
   }

   public void save(T o) {
      this.getSession().save(o);
   }

   public void saveOrUpdate(T o) {
      this.getSession().saveOrUpdate(o);
   }

   public void removeById(PK id) {
      T o = (T)this.get(id);
      this.remove(o);
   }

   public void remove(T o) {
      this.getSession().delete(o);
   }

   public void remove(Collection<T> entities) {
      if (entities != null) {
         for(T entity : entities) {
            this.remove(entity);
         }

      }
   }

   public void remove(Criterion... criterion) {
      List<T> list = this.queryAll(criterion);
      this.remove(list);
   }

   public boolean exists(PK id) {
      return this.get(id) != null;
   }

   public long count() {
      long count = (Long)this.createCriteria().setProjection(Projections.rowCount()).uniqueResult();
      return count;
   }

   public long count(Criteria criteria) {
      return ObjectUtils.isEmpty(criteria.setProjection(Projections.rowCount()).uniqueResult()) ? 0L : (Long)criteria.setProjection(Projections.rowCount()).uniqueResult();
   }

   public long count(Criterion... criterion) {
      return (Long)this.createCriteria(criterion).setProjection(Projections.rowCount()).uniqueResult();
   }

   public long count(String hql) {
      return (Long)this.getSession().createQuery(hql).getSingleResult();
   }

   @Transactional
   public Criteria createCriteria() {
      return this.getSession().createCriteria(this.entityClass);
   }

   public Criteria createCriteria(Criterion... criterions) {
      Criteria criteria = this.createCriteria();

      for(Criterion c : criterions) {
         criteria.add(c);
      }

      return criteria;
   }

   public Criteria addOrders(Criteria criteria, List<Order> orders) {
      for(Order c : orders) {
         criteria.addOrder(c);
      }

      return criteria;
   }

   @Transactional
   public List<T> queryAll() {
      return this.createCriteria().list();
   }

   public List<T> queryAll(Criterion... criterion) {
      Criteria criteria = this.createCriteria(criterion);
      return this.queryAll(criteria);
   }

   public List<T> queryAll(Criteria criteria) {
      return criteria.list();
   }

   public List<T> queryAll(Order order, Criterion... criterions) {
      return this.createCriteria(criterions).addOrder(order).list();
   }

   public List<T> queryAll(Order order1, Order order2, Criterion... criterions) {
      return this.createCriteria(criterions).addOrder(order1).addOrder(order2).list();
   }

   public List<T> queryAll(String orderBy, boolean isAsc) {
      return isAsc ? this.createCriteria().addOrder(Order.asc(orderBy)).list() : this.createCriteria().addOrder(Order.desc(orderBy)).list();
   }

   public List<T> queryAll(String orderBy, boolean isAsc, Criterion... criterion) {
      Criteria criteria = this.createCriteria(criterion);
      return isAsc ? criteria.addOrder(Order.asc(orderBy)).list() : criteria.addOrder(Order.desc(orderBy)).list();
   }

   public List<T> queryByPageInfo(PageInfo pageinfo) {
      pageinfo.setCount(this.count());
      return this.createCriteria().setFirstResult(pageinfo.getOffset()).setMaxResults(pageinfo.getPagesize()).list();
   }

   public List<T> queryByPageInfo(PageInfo pageinfo, Order order) {
      long count = this.count();
      pageinfo.setCount(count);
      List<T> list = this.createCriteria().addOrder(order).setFirstResult(pageinfo.getOffset()).setMaxResults(pageinfo.getPagesize()).list();
      return list;
   }

   public List<T> queryByPageInfo(PageInfo pageinfo, Criterion... criterion) {
      Criteria criteria = this.createCriteria(criterion);
      long count = this.count(criteria);
      pageinfo.setCount(count);
      criteria = this.createCriteria(criterion);
      List<T> list = criteria.setFirstResult(pageinfo.getOffset()).setMaxResults(pageinfo.getPagesize()).list();
      return list;
   }

   public List<T> queryByPageInfo(PageInfo pageinfo, Order order, Criterion... criterion) {
      Criteria criteria = this.createCriteria(criterion);
      long count = this.count(criteria);
      pageinfo.setCount(count);
      criteria = this.createCriteria(criterion);
      criteria.setCacheable(false);
      List<T> list = criteria.addOrder(order).setFirstResult(pageinfo.getOffset()).setMaxResults(pageinfo.getPagesize()).list();
      return list;
   }

   public List<T> queryByPageInfo1(PageInfo pageinfo, Order order, Order order1, Criterion... criterion) {
      Criteria criteria = this.createCriteria(criterion);
      long count = this.count(criteria);
      pageinfo.setCount(count);
      criteria = this.createCriteria(criterion);
      List<T> list = criteria.addOrder(order).addOrder(order1).setFirstResult(pageinfo.getOffset()).setMaxResults(pageinfo.getPagesize()).list();
      return list;
   }

   public List<T> queryByPageInfoByOrders(PageInfo pageinfo, List<Order> orders, Criterion... criterion) {
      Criteria criteria = this.createCriteria(criterion);
      long count = this.count(criteria);
      pageinfo.setCount(count);
      criteria = this.createCriteria(criterion);
      this.addOrders(criteria, orders);
      List<T> list = criteria.setFirstResult(pageinfo.getOffset()).setMaxResults(pageinfo.getPagesize()).list();
      return list;
   }

   public T queryUnique(Criterion... criterion) {
      Criteria criteria = this.createCriteria(criterion);
      return (T)criteria.uniqueResult();
   }

   public Session openSession() {
      return this.sessionFactory.openSession();
   }

   public void update(T t) {
      this.getSession().update(t);
   }

   public List queryGroup(Order order, String groupBy, Criterion... criterions) {
      return this.createCriteria(criterions).addOrder(order).setProjection(Projections.groupProperty(groupBy)).list();
   }
}
