package +zoccolo+.+rail+;

import +zoccolo+.generic.GenericCrud;

import java.io.Serializable;
import java.lang.reflect.ParameterizedType;
import java.util.ArrayList;
import java.util.List;

import org.hibernate.Criteria;
import org.hibernate.HibernateException;
import org.hibernate.LockMode;
import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.criterion.Criterion;
import org.hibernate.criterion.Example;
import org.hibernate.criterion.Order;
import org.hibernate.criterion.Projections;
import org.hibernate.impl.SessionFactoryImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.dao.DataAccessException;
import org.springframework.orm.hibernate3.HibernateTemplate;
import org.springframework.transaction.annotation.Transactional;

@Transactional("+rail+TxManager")
public abstract class Generic+Rail+DaoImpl<T, ID extends Serializable>
	implements GenericCrud<T, ID> {
	
	private final Class<T> persistentClass;

	@Autowired(required = true)
	@Qualifier("+rail+Template")
	protected HibernateTemplate +rail+Template;

	@SuppressWarnings("unchecked")
	public Generic+Rail+DaoImpl() {
		this.persistentClass = (Class<T>) ((ParameterizedType) getClass()
				.getGenericSuperclass()).getActualTypeArguments()[0];
	}

	public void clear() throws DataAccessException {
		+rail+Template.clear();
	}

	public void delete(T entity) throws DataAccessException {
		+rail+Template.delete(entity);
	}

	public void deleteById(ID id) throws DataAccessException {
		T entity = +rail+Template.get(persistentClass, id);
		+rail+Template.delete(entity);
	}

	public List<T> findAll() throws HibernateException {
		return findByCriteria();
	}

	@SuppressWarnings("unchecked")
	public List<T> findAll(Order order) throws HibernateException {
		Criteria crit = +rail+Template.getSessionFactory().getCurrentSession()
				.createCriteria(persistentClass);
		crit.addOrder(order);
		return crit.list();
	}
	
	@SuppressWarnings("unchecked")
	public List<T> findByCriteria(Order order, Criterion... criterion)
			throws HibernateException {
		Criteria criteria = +rail+Template.getSessionFactory().getCurrentSession()
				.createCriteria(persistentClass).addOrder(order);
		for (Criterion c : criterion) {
			criteria.add(c);
		}
		return criteria.list();
	}
	
	@SuppressWarnings("unchecked")
	public List<T> findByCriteria(Criterion... criterion)
			throws HibernateException {
		Criteria criteria = +rail+Template.getSessionFactory()
			.getCurrentSession().createCriteria(persistentClass);
		for (Criterion c : criterion) {
			criteria.add(c);
		}
		return criteria.list();
	}

	@SuppressWarnings("unchecked")
	public String getDialect()

			throws HibernateException {

		SessionFactoryImpl sfi = (SessionFactoryImpl) +rail+Template
				.getSessionFactory();
		return sfi.getDialect().toString();
	}

	@SuppressWarnings("unchecked")
	public List<Object> oneColumnQuery(final String q)
	
			throws HibernateException {
		
		Session session = +rail+Template.getSessionFactory()
				.getCurrentSession();
		Query query = session.createQuery(q);
		return (List<Object>) query.list();
	}

	@SuppressWarnings("unchecked")
	public List<Object[]> manyColumnQuery(final String q)
	
			throws HibernateException {
		
		Session session = +rail+Template.getSessionFactory()
				.getCurrentSession();
		Query query = session.createQuery(q);
		return (List<Object[]>) query.list();
	}

	@Override
	public Integer howManyRows(Criterion... criterion)
			
			throws DataAccessException {
		
		Criteria criteria = +rail+Template.getSessionFactory().getCurrentSession()
    			.createCriteria(persistentClass)
    			.setProjection(Projections.rowCount());
		for (Criterion c : criterion) {
			criteria.add(c);
		}
		return (Integer)criteria.uniqueResult();
	}

	@Override
	public Integer howManyRowsAlias(String p, String alias, Criterion... criterion)
			
			throws DataAccessException {
		
		Criteria criteria = +rail+Template.getSessionFactory().getCurrentSession()
    			.createCriteria(persistentClass)
    			.setProjection(Projections.rowCount());
		criteria.createAlias(p, alias);
		for (Criterion c : criterion) {
			criteria.add(c);
		}
		return (Integer)criteria.uniqueResult();
	}

	@Override
	public Integer howManyRows() throws DataAccessException {
		Criteria criteria = +rail+Template.getSessionFactory().getCurrentSession()
    			.createCriteria(persistentClass)
    			.setProjection(Projections.rowCount());
		return (Integer)criteria.uniqueResult();
	}
	
	@SuppressWarnings("unchecked")
	public T findUniqueByCriteria(Criterion... criterion)
			throws HibernateException {
		Criteria crit = +rail+Template.getSessionFactory().getCurrentSession()
				.createCriteria(persistentClass);
		for (Criterion c : criterion) {
			crit.add(c);
		}
		return (T) crit.uniqueResult();
	}

	@SuppressWarnings("unchecked")
	public List<T> findByExample(T exampleInstance, String[] excludeProperty)
			throws HibernateException {
		Criteria crit = +rail+Template.getSessionFactory().getCurrentSession()
				.createCriteria(persistentClass);
		Example example = Example.create(exampleInstance);
		for (String exclude : excludeProperty) {
			example.excludeProperty(exclude);
		}
		crit.add(example);
		return crit.list();
	}

	public T findById(ID id, boolean lock) throws DataAccessException {
		T entity;
		if (lock)
			entity = +rail+Template.load(persistentClass, id, LockMode.UPGRADE);
		else
			entity = +rail+Template.load(persistentClass, id);

		return entity;
	}

	public void flush() throws DataAccessException {
		+rail+Template.flush();
	}

	public T makePersistent(T entity) throws DataAccessException {
		+rail+Template.saveOrUpdate(entity);
		return entity;
	}

	public void makeTransient(T entity) throws DataAccessException {
		+rail+Template.delete(entity);
	}

	public void save(T entity) throws DataAccessException {
		+rail+Template.save(entity);
	}

	public void saveOrUpdate(T entity) throws DataAccessException {
		+rail+Template.saveOrUpdate(entity);
	}

	public void update(T entity) throws DataAccessException {
		+rail+Template.update(entity);
	}

	public void merge(T entity) throws DataAccessException {
		+rail+Template.merge(entity);
	}
	/* screwdriver_knife */
}
