/* ===================================================================
 * RecipeDaoImpl.java
 * 
 * Created Aug 4, 2004 10:36:11 AM
 * 
 * Copyright (c) 2004 Matt Magoffin (spamsqr@msqr.us)
 * 
 * This program is free software; you can redistribute it and/or 
 * modify it under the terms of the GNU General Public License as 
 * published by the Free Software Foundation; either version 2 of 
 * the License, or (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful, 
 * but WITHOUT ANY WARRANTY; without even the implied warranty of 
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 
 * General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License 
 * along with this program; if not, write to the Free Software 
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 
 * 02111-1307 USA
 * ===================================================================
 * $Id: RecipeDaoImpl.java 41 2009-05-06 23:34:29Z msqr $
 * ===================================================================
 */

package magoffin.matt.ieat.dao.hbm;

import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.List;

import magoffin.matt.dao.hbm.GenericHibernateDao;
import magoffin.matt.ieat.biz.DomainObjectFactory;
import magoffin.matt.ieat.dao.RecipeDao;
import magoffin.matt.ieat.domain.Base;
import magoffin.matt.ieat.domain.Course;
import magoffin.matt.ieat.domain.Difficulty;
import magoffin.matt.ieat.domain.Ethnicity;
import magoffin.matt.ieat.domain.PrepTime;
import magoffin.matt.ieat.domain.Recipe;
import magoffin.matt.ieat.domain.RecipeIngredient;
import magoffin.matt.ieat.domain.UiSearchResults;
import magoffin.matt.ieat.domain.impl.RecipeImpl;
import magoffin.matt.util.StringUtil;

import org.hibernate.HibernateException;
import org.hibernate.Query;
import org.hibernate.Session;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowCallbackHandler;
import org.springframework.orm.hibernate3.HibernateCallback;

/**
 * RecipeDao implementation using Hibernate.
 * 
 * @author Matt Magoffin (spamsqr@msqr.us)
 * @version $Revision: 41 $ $Date: 2009-05-07 11:34:29 +1200 (Thu, 07 May 2009) $
 */
public class RecipeDaoImpl extends GenericHibernateDao<Recipe, Long> 
implements RecipeDao {

	/** The query to find all Recipe objects. */
	public static final String FIND_ALL = "RecipeAll";
	
	/** The query to find all Recipe objects. */
	public static final String FIND_USED_AS_INGREDIENT = "RecipeUsedAsIngredient";
	
	/** The query to find the count of all Recipe objects. */
	public static final String FIND_ALL_COUNT = "RecipeAllCount";
	
	private JdbcTemplate jdbcTemplate = null;
	private String sqlIndexAll = null;
	private String sqlReassignBase = null;
	private String sqlReassignCourse = null;
	private String sqlReassignDifficulty = null;
	private String sqlReassignEthnicity = null;
	private String sqlReassignIngredient = null;
	private String sqlReassignPrepTime = null;
	private DomainObjectFactory domainObjectFactory = null;
	
	/**
	 * Default constructor.
	 */
	public RecipeDaoImpl() {
		super(RecipeImpl.class);
	}

	/**
	 * Method to call after all dependency injection has occured.
	 */
	public void init() {
		if ( jdbcTemplate == null ) {
			throw new RuntimeException("jdbcTemplate not configured");
		}
		if ( sqlIndexAll == null ) {
			throw new RuntimeException("sqlIndexAll not configured");
		}
	}

	@Override
	protected Long getPrimaryKey(Recipe domainObject) {
		if ( domainObject == null ) return null;
		return domainObject.getRecipeId();
	}
	
	/* (non-Javadoc)
	 * @see magoffin.matt.ieat.dao.RecipeDao#findRecipesUsingRecipeAsIngredient(java.lang.Long)
	 */
	public List<Recipe> findRecipesUsingRecipeAsIngredient(Long recipeId) {
		return findByNamedQuery(FIND_USED_AS_INGREDIENT, new Object[] {recipeId});
	}

	/* (non-Javadoc)
	 * @see magoffin.matt.ieat.dao.RecipeDao#getAllRecipes(magoffin.matt.ieat.domain.UiPaginationSupport)
	 */
	@SuppressWarnings({ "unchecked" })
	public UiSearchResults getAllRecipes(int pageSize, int pageOffset) {
		UiSearchResults results = domainObjectFactory.getEmbeddedSearchResultsInstance();
		if ( pageSize < 1 ) {
			// return all available
			List<Recipe> allRecipes = findByNamedQuery(FIND_ALL);
			results.getRecipe().addAll(allRecipes);
			results.setTotalResults(allRecipes.size());
			return results;
		}
		
		Integer count = (Integer)getHibernateTemplate().execute(new HibernateCallback() {
			public Object doInHibernate(Session session) throws HibernateException, SQLException {
				Query query = session.getNamedQuery(FIND_ALL_COUNT);
				Number result = (Number)query.iterate().next();
				return result.intValue();
			}
		});
		
		List<Recipe> pagedList = findByNamedQuery(FIND_ALL, (Object[])null, 
				pageOffset, pageSize);
		
		results.getRecipe().addAll(pagedList);
		results.setTotalResults(count);
		return results;
	}

	/* (non-Javadoc)
	 * @see magoffin.matt.ieat.dao.RecipeDao#index(magoffin.matt.ieat.dao.RecipeDao.RecipeIndexCallback)
	 */
	public void index(final RecipeIndexCallback callback) {
		if ( log.isDebugEnabled() ) {
			log.debug("Executing SQL for index all recipes: " +sqlIndexAll);
		}
		final RecipeIndexCallbackDataImpl callbackData = new RecipeIndexCallbackDataImpl();
		jdbcTemplate.query(sqlIndexAll, 
				new RowCallbackHandler() {
					private ResultSet myRs;
					public void processRow(ResultSet rs) throws SQLException {
						if ( myRs == null ) {
							myRs = rs;
						}
						callbackData.recipeId = Long.valueOf(rs.getLong(1));
						callbackData.name = rs.getString(2);
						callbackData.excerpt = rs.getString(3);
						callbackData.directions = rs.getString(4);
						callbackData.step = rs.getString(5);
						int aInt = rs.getInt(6);
						if ( !rs.wasNull() ) {
							callbackData.ingredientId = aInt;
						} else {
							callbackData.ingredientId = null;
						}
						aInt = rs.getInt(7);
						if ( !rs.wasNull() ) {
							short aShort = rs.getShort(8);
							if ( !rs.wasNull() ) {
								callbackData.ratingUserId = aInt;
								callbackData.rating = aShort;
							} else {
								callbackData.ratingUserId = null;
								callbackData.rating = null;
							}
						} else {
							callbackData.ratingUserId = null;
							callbackData.rating = null;
						}
						aInt = rs.getInt(9);
						if ( !rs.wasNull() ) {
							callbackData.ownerId = aInt;
						} else {
							callbackData.ownerId = null;
						}
						callback.handle(callbackData);
					}
				}
				);
		callback.finish();
	}
	
	/* (non-Javadoc)
	 * @see magoffin.matt.ieat.dao.RecipeDao#reassignBase(java.lang.Integer, java.lang.Integer)
	 */
	public void reassignBase(Integer baseId, Integer newBaseId) {
		getHibernateTemplate().flush();
		jdbcTemplate.update(this.sqlReassignBase, new Object[] {newBaseId,baseId});
		try {
			Recipe r = domainObjectFactory.getRecipeInstance();
			getHibernateTemplate().getSessionFactory().evict(r.getClass());
			Base b = domainObjectFactory.getBaseInstance();
			getHibernateTemplate().getSessionFactory().evict(b.getClass());
		} catch ( HibernateException e ) {
			throw new RuntimeException(e);
		}
	}
	
	/* (non-Javadoc)
	 * @see magoffin.matt.ieat.dao.RecipeDao#reassignCourse(java.lang.Integer, java.lang.Integer)
	 */
	public void reassignCourse(Integer courseId, Integer newCourseId) {
		getHibernateTemplate().flush();
		jdbcTemplate.update(this.sqlReassignCourse, new Object[] {newCourseId,courseId});
		try {
			Recipe r = domainObjectFactory.getRecipeInstance();
			getHibernateTemplate().getSessionFactory().evict(r.getClass());
			Course c = domainObjectFactory.getCourseInstance();
			getHibernateTemplate().getSessionFactory().evict(c.getClass());
		} catch ( HibernateException e ) {
			throw new RuntimeException(e);
		}
	}
	
	/* (non-Javadoc)
	 * @see magoffin.matt.ieat.dao.RecipeDao#reassignDifficulty(java.lang.Integer, java.lang.Integer)
	 */
	public void reassignDifficulty(Integer difficultyId, Integer newDifficultyId) {
		getHibernateTemplate().flush();
		jdbcTemplate.update(this.sqlReassignDifficulty, new Object[] {newDifficultyId,difficultyId});
		try {
			Recipe r = domainObjectFactory.getRecipeInstance();
			getHibernateTemplate().getSessionFactory().evict(r.getClass());
			Difficulty d = domainObjectFactory.getDifficultyInstance();
			getHibernateTemplate().getSessionFactory().evict(d.getClass());
		} catch ( HibernateException e ) {
			throw new RuntimeException(e);
		}
	}
	
	/* (non-Javadoc)
	 * @see magoffin.matt.ieat.dao.RecipeDao#reassignEthnicity(java.lang.Integer, java.lang.Integer)
	 */
	public void reassignEthnicity(Integer ethnicityId, Integer newEthnicityId) {
		getHibernateTemplate().flush();
		jdbcTemplate.update(this.sqlReassignEthnicity, new Object[] {newEthnicityId,ethnicityId});
		try {
			Recipe r = domainObjectFactory.getRecipeInstance();
			getHibernateTemplate().getSessionFactory().evict(r.getClass());
			Ethnicity e = domainObjectFactory.getEthnicityInstance();
			getHibernateTemplate().getSessionFactory().evict(e.getClass());
		} catch ( HibernateException e ) {
			throw new RuntimeException(e);
		}
	}
	
	/* (non-Javadoc)
	 * @see magoffin.matt.ieat.dao.RecipeDao#reassignIngredient(java.lang.Integer, java.lang.Integer)
	 */
	public void reassignIngredient(Integer ingredientId, Integer newIngredientId) {
		getHibernateTemplate().flush();
		jdbcTemplate.update(this.sqlReassignIngredient, new Object[] {newIngredientId,ingredientId});
		try {
			Recipe r = domainObjectFactory.getRecipeInstance();
			getHibernateTemplate().getSessionFactory().evict(r.getClass());
			RecipeIngredient i = domainObjectFactory.getRecipeIngredientInstance();
			getHibernateTemplate().getSessionFactory().evict(i.getClass());
		} catch ( HibernateException e ) {
			throw new RuntimeException(e);
		}
	}
	
	/* (non-Javadoc)
	 * @see magoffin.matt.ieat.dao.RecipeDao#reassignPrepTime(java.lang.Integer, java.lang.Integer)
	 */
	public void reassignPrepTime(Integer prepTimeId, Integer newPrepTimeId) {
		getHibernateTemplate().flush();
		jdbcTemplate.update(this.sqlReassignPrepTime, new Object[] {newPrepTimeId,prepTimeId});
		try {
			Recipe r = domainObjectFactory.getRecipeInstance();
			getHibernateTemplate().getSessionFactory().evict(r.getClass());
			PrepTime p = domainObjectFactory.getPrepTimeInstance();
			getHibernateTemplate().getSessionFactory().evict(p.getClass());
		} catch ( HibernateException e ) {
			throw new RuntimeException(e);
		}
	}
	
	private static class RecipeIndexCallbackDataImpl implements RecipeIndexCallbackData {
		private Long recipeId;
		private String name;
		private String excerpt;
		private String directions;
		private String step;
		private Integer ingredientId;
		private Short rating;
		private Integer ratingUserId;
		private String comment;
		private Integer commentUserId;
		private Integer ownerId;
		
		public String getName() {
			return name;
		}
		public Long getRecipeId() {
			return recipeId;
		}
		public String getDirections() {
			return StringUtil.trimToNull(directions);
		}
		public String getExcerpt() {
			return StringUtil.trimToNull(excerpt);
		}
		public String getStep() {
			return StringUtil.trimToNull(step);
		}
		public Integer getIngredientId() {
			return ingredientId;
		}
		public Short getRating() {
			return rating;
		}
		public Integer getRatingUserId() {
			return ratingUserId;
		}
		public String getComment() {
			return comment;
		}
		public Integer getCommentUserId() {
			return commentUserId;
		}
		public Integer getOwnerId() {
			return ownerId;
		}
		@Override
		public String toString() {
			return "RecipeIndexCallbackDataImpl{recipeId=" +recipeId +",name=" +name +"}";
		}
	}
	
	/**
	 * @return the domainObjectFactory
	 */
	public DomainObjectFactory getDomainObjectFactory() {
		return domainObjectFactory;
	}
	
	/**
	 * @param domainObjectFactory the domainObjectFactory to set
	 */
	public void setDomainObjectFactory(DomainObjectFactory domainObjectFactory) {
		this.domainObjectFactory = domainObjectFactory;
	}
	
	/**
	 * @return the jdbcTemplate
	 */
	public JdbcTemplate getJdbcTemplate() {
		return jdbcTemplate;
	}
	
	/**
	 * @param jdbcTemplate the jdbcTemplate to set
	 */
	public void setJdbcTemplate(JdbcTemplate jdbcTemplate) {
		this.jdbcTemplate = jdbcTemplate;
	}
	
	/**
	 * @return the sqlIndexAll
	 */
	public String getSqlIndexAll() {
		return sqlIndexAll;
	}
	
	/**
	 * @param sqlIndexAll the sqlIndexAll to set
	 */
	public void setSqlIndexAll(String sqlIndexAll) {
		this.sqlIndexAll = sqlIndexAll;
	}
	
	/**
	 * @return the sqlReassignBase
	 */
	public String getSqlReassignBase() {
		return sqlReassignBase;
	}
	
	/**
	 * @param sqlReassignBase the sqlReassignBase to set
	 */
	public void setSqlReassignBase(String sqlReassignBase) {
		this.sqlReassignBase = sqlReassignBase;
	}
	
	/**
	 * @return the sqlReassignCourse
	 */
	public String getSqlReassignCourse() {
		return sqlReassignCourse;
	}
	
	/**
	 * @param sqlReassignCourse the sqlReassignCourse to set
	 */
	public void setSqlReassignCourse(String sqlReassignCourse) {
		this.sqlReassignCourse = sqlReassignCourse;
	}
	
	/**
	 * @return the sqlReassignDifficulty
	 */
	public String getSqlReassignDifficulty() {
		return sqlReassignDifficulty;
	}
	
	/**
	 * @param sqlReassignDifficulty the sqlReassignDifficulty to set
	 */
	public void setSqlReassignDifficulty(String sqlReassignDifficulty) {
		this.sqlReassignDifficulty = sqlReassignDifficulty;
	}
	
	/**
	 * @return the sqlReassignEthnicity
	 */
	public String getSqlReassignEthnicity() {
		return sqlReassignEthnicity;
	}
	
	/**
	 * @param sqlReassignEthnicity the sqlReassignEthnicity to set
	 */
	public void setSqlReassignEthnicity(String sqlReassignEthnicity) {
		this.sqlReassignEthnicity = sqlReassignEthnicity;
	}
	
	/**
	 * @return the sqlReassignIngredient
	 */
	public String getSqlReassignIngredient() {
		return sqlReassignIngredient;
	}
	
	/**
	 * @param sqlReassignIngredient the sqlReassignIngredient to set
	 */
	public void setSqlReassignIngredient(String sqlReassignIngredient) {
		this.sqlReassignIngredient = sqlReassignIngredient;
	}
	
	/**
	 * @return the sqlReassignPrepTime
	 */
	public String getSqlReassignPrepTime() {
		return sqlReassignPrepTime;
	}
	
	/**
	 * @param sqlReassignPrepTime the sqlReassignPrepTime to set
	 */
	public void setSqlReassignPrepTime(String sqlReassignPrepTime) {
		this.sqlReassignPrepTime = sqlReassignPrepTime;
	}

}
