/*
 * This file is part of Nuts Framework.
 * Copyright(C) 2009-2012 Nuts Develop Team.
 *
 * Nuts Framework 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 3 of the License any later version.
 * 
 * Nuts Framework 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 Nuts Framework. If not, see <http://www.gnu.org/licenses/>.
 */
package nuts.exts.ibatis;

import nuts.core.dao.AbstractModelDAO;
import nuts.core.dao.DaoException;
import nuts.core.dao.DaoSession;
import nuts.core.dao.DataHandler;
import nuts.core.dao.sql.SqlDataAccessSession;
import nuts.core.dao.sql.criteria.SqlQueryParameter;
import nuts.core.dao.sql.criteria.SqlUpdateParameter;
import nuts.exts.mybatis.SqlMapConstants;

import java.util.List;

import com.ibatis.sqlmap.client.SqlMapClient;

/**
 * @param <T> model type
 * @param <E> model example type
 */
public abstract class IBatisModelDAO<T, E extends SqlQueryParameter> extends AbstractModelDAO<T, E> {
	/**
	 * Constructor
	 */
	public IBatisModelDAO() {
		super();
	}
	
	/**
	 * Constructor
	 *
	 * @param dataAccessSession the dataAccessSession to set
	 */
	public IBatisModelDAO(DaoSession dataAccessSession) {
		super(dataAccessSession);
	}

	/**
	 * @return the sqlMapClient
	 */
	public SqlMapClient getSqlMapClient() {
		return ((IBatisDataAccessClient)getDataAccessClient()).getSqlMapClient();
	}

	/**
	 * @return the SqlDataAccessSession
	 */
	protected SqlDataAccessSession getSqlMapDataAccessSession() {
		return ((SqlDataAccessSession)getDataAccessSession());
	}

	/**
	 * @return full sql id
	 */
	protected abstract String getFullSqlId(String id);

	/**
	 * exists
	 * 
	 * @param key T
	 * @return Album
	 * @throws DaoException if a data access error occurs
	 */ 
	public boolean exists(T key) throws DaoException {
		Integer count = (Integer)getSqlMapDataAccessSession().selectOne(getFullSqlId(SqlMapConstants.COUNT_BY_PRIMARY_KEY), key);
		return count > 0;
	}
	
	/**
	 * countByExample
	 * 
	 * @param example E
	 * @return count
	 * @throws DaoException if a data access error occurs
	 */ 
	public int count(E example) throws DaoException {
		Integer count = (Integer)getSqlMapDataAccessSession().selectOne(getFullSqlId(SqlMapConstants.COUNT_BY_EXAMPLE), example);
		return count;
	}

	/**
	 * selectByPrimaryKey
	 * 
	 * @param key T
	 * @return T
	 * @throws DaoException if a data access error occurs
	 */ 
	@SuppressWarnings("unchecked")
	public T fetch(T key) throws DaoException {
		T data = (T)getSqlMapDataAccessSession().selectOne(getFullSqlId(SqlMapConstants.SELECT_BY_PRIMARY_KEY), key);
		return data;
	}

	/**
	 * selectByExampleWithDataHandler
	 * 
	 * @param example E
	 * @param dataHandler data handler
	 * @return data count
	 * @throws DaoException if a data access error occurs
	 */ 
	public int selectWithDataHandler(E example, DataHandler<T> dataHandler) throws DaoException {
		int cnt;
		if (example.getStart() != null || example.getLimit() != null) {
			cnt = getSqlMapDataAccessSession().selectWithDataHandler(
					getFullSqlId(SqlMapConstants.SELECT_BY_EXAMPLE), 
					example,
					example.getStart() == null ? 0 : example.getStart(),
					example.getLimit() == null ? Integer.MAX_VALUE : example.getLimit(),
					dataHandler);
		}
		else {
			cnt = getSqlMapDataAccessSession().selectWithDataHandler(
				getFullSqlId(SqlMapConstants.SELECT_BY_EXAMPLE), example, dataHandler);
		}
		return cnt;
	}

	/**
	 * selectByExample
	 * 
	 * @param example E
	 * @return list of T 
	 * @throws DaoException if a data access error occurs
	 */ 
	@SuppressWarnings("unchecked")
	public List<T> selectList(E example) throws DaoException {
		List<T> list;
		if (example.getStart() != null || example.getLimit() != null) {
			list = (List<T>) getSqlMapDataAccessSession().selectList(
					getFullSqlId(SqlMapConstants.SELECT_BY_EXAMPLE), example, 
					example.getStart() == null ? 0 : example.getStart(),
					example.getLimit() == null ? Integer.MAX_VALUE : example.getLimit());
		}
		else {
			list = (List<T>) getSqlMapDataAccessSession().selectList(
				getFullSqlId(SqlMapConstants.SELECT_BY_EXAMPLE), example);
		}
		return list;
	}

	/**
	 * if (exists(data)) { updateByPrimaryKey(data); } else { insert(data}; }
	 * 
	 * @param data T
	 * @throws DaoException if a data access error occurs
	 */ 
	public void save(T data) throws DaoException {
		if (exists(data)) { 
			update(data); 
		}
		else {
			insert(data);
		}
	}
	
	/**
	 * insert
	 * 
	 * @param data T
	 * @throws DaoException if a data access error occurs
	 */ 
	public void insert(T data) throws DaoException {
		getSqlMapDataAccessSession().insert(getFullSqlId(SqlMapConstants.INSERT), data);
	}


	/**
	 * deleteByPrimaryKey
	 * 
	 * @param key Album
	 * @return count of deleted records
	 * @throws DaoException if a data access error occurs
	 */ 
	public int delete(T key) throws DaoException {
		int rows = getSqlMapDataAccessSession().delete(getFullSqlId(SqlMapConstants.DELETE_BY_PRIMARY_KEY), key);
		return rows;
	}

	/**
	 * deleteByExample
	 * 
	 * @param example E
	 * @return count of deleted records
	 * @throws DaoException if a data access error occurs
	 */ 
	public int deleteByExample(E example) throws DaoException {
		int rows = getSqlMapDataAccessSession().delete(getFullSqlId(SqlMapConstants.DELETE_BY_EXAMPLE), example);
		return rows;
	}

	/**
	 * updateByPrimaryKey
	 * 
	 * @param data T
	 * @return count of updated records
	 * @throws DaoException if a data access error occurs
	 */ 
	public int update(T data) throws DaoException {
		int rows = getSqlMapDataAccessSession().update(getFullSqlId(SqlMapConstants.UPDATE_BY_PRIMARY_KEY), data);
		return rows;
	}

	/**
	 * updateByPrimaryKeySelective (ignore null properties)
	 * 
	 * @param data T
	 * @return count of updated records
	 * @throws DaoException if a data access error occurs
	 */ 
	public int updateIgnoreNull(T data) throws DaoException {
		int rows = getSqlMapDataAccessSession().update(getFullSqlId(SqlMapConstants.UPDATE_BY_PRIMARY_KEY_SELECTIVE), data);
		return rows;
	}

	/**
	 * updateByExample
	 * 
	 * @param data T
	 * @param example E
	 * @return count of updated records
	 * @throws DaoException if a data access error occurs
	 */ 
	public int update(T data, E example) throws DaoException {
		SqlUpdateParameter up = new SqlUpdateParameter(data, example);
		int rows = getSqlMapDataAccessSession().update(getFullSqlId(SqlMapConstants.UPDATE_BY_EXAMPLE), up);
		return rows;
	}

	/**
	 * updateByExampleSelective (ignore null properties)
	 * 
	 * @param data T
	 * @param example E
	 * @return count of updated records
	 * @throws DaoException if a data access error occurs
	 */ 
	public int updateIgnoreNull(T data, E example) throws DaoException {
		SqlUpdateParameter up = new SqlUpdateParameter(data, example);
		int rows = getSqlMapDataAccessSession().update(getFullSqlId(SqlMapConstants.UPDATE_BY_EXAMPLE_SELECTIVE), up);
		return rows;
	}
}
