/*
 * 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.core.orm.dao;

import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

import nuts.core.beans.PropertyUtils;
import nuts.core.collections.CollectionUtils;

/**
 * AbstractModelDAO
 * @param <T> model type
 * @param <E> model example type
 */
public abstract class AbstractModelDAO<T, E extends QueryParameter> implements ModelDAO<T, E> {
	private DataAccessSession dataAccessSession;
	
	/**
	 * Constructor
	 */
	public AbstractModelDAO() {
	}
	
	/**
	 * Constructor
	 *
	 * @param dataAccessSession the dataAccessSession to set
	 */
	public AbstractModelDAO(DataAccessSession dataAccessSession) {
		setDataAccessSession(dataAccessSession);
	}

	/**
	 * @return the dataAccessClient
	 */
	public DataAccessClient getDataAccessClient() {
		return dataAccessSession.getDataAccessClient();
	}

	/**
	 * @return the dataAccessSession
	 */
	public DataAccessSession getDataAccessSession() {
		return dataAccessSession;
	}

	/**
	 * @param dataAccessSession the dataAccessSession to set
	 */
	public void setDataAccessSession(DataAccessSession dataAccessSession) {
		this.dataAccessSession = dataAccessSession;
	}
	
	/**
	 * selectOneByExample
	 * 
	 * @param example E
	 * @return T 
	 * @throws DataAccessException if a data access error occurs
	 */ 
	public T selectOneByExample(E example) throws DataAccessException {
		example.setStart(0);
		example.setLimit(1);
		List<T> list = selectByExample(example);
		if (CollectionUtils.isNotEmpty(list)) {
			return list.get(0);
		}
		else {
			return null;
		}
	}

	/**
	 * selectAll
	 * 
	 * @return list of T 
	 * @throws DataAccessException if a data access error occurs
	 */ 
	public List<T> selectAll() throws DataAccessException {
		return selectByExample(createExample());
	}

	/**
	 * convert list to map
	 * @param list list of T
	 * @param keyProp The property to be used as the key in the Map.
	 * @return map of T 
	 */
	protected Map<Object, T> convertListToMap(List<T> list, String keyProp) {
		Map<Object, T> map = new LinkedHashMap<Object, T>();
		for (T data : list) {
			try {
				Object key = PropertyUtils.getProperty(data, keyProp);
				map.put(key, data);
			}
			catch (Exception e) {
				throw new RuntimeException(e);
			}
		}
		return map;
	}
	
	/**
	 * selectMapByExample
	 * 
	 * @param example E
	 * @param keyProp The property to be used as the key in the Map.
	 * @return map of T
	 * @throws DataAccessException if a data access error occurs
	 */ 
	public Map<?, T> selectMapByExample(E example, String keyProp) throws DataAccessException {
		List<T> list = selectByExample(example);
		return convertListToMap(list, keyProp);
	}
	
	/**
	 * selectMapAll
	 * 
	 * @param keyProp The property to be used as the key in the Map.
	 * @return map of T
	 * @throws DataAccessException if a data access error occurs
	 */ 
	public Map<?, T> selectMapAll(String keyProp) throws DataAccessException {
		List<T> list = selectByExample(createExample());
		return convertListToMap(list, keyProp);
	}

	/**
	 * selectMapByExample
	 * 
	 * @param example E
	 * @param keyProp The property to be used as the key in the Map.
	 * @param valProp The property to be used as the value in the Map.
	 * @return map
	 * @throws DataAccessException if a data access error occurs
	 */ 
	public Map<?, ?> selectMapByExample(E example, String keyProp, String valProp) throws DataAccessException {
		List<T> list = selectByExample(example);
		return convertListToMap(list, keyProp, valProp);
	}

	/**
	 * selectMapAll
	 * 
	 * @param keyProp The property to be used as the key in the Map.
	 * @param valProp The property to be used as the value in the Map.
	 * @return map
	 * @throws DataAccessException if a data access error occurs
	 */ 
	public Map<?, ?> selectMapAll(String keyProp, String valProp) throws DataAccessException {
		List<T> list = selectByExample(createExample());
		return convertListToMap(list, keyProp, valProp);
	}

	/**
	 * convert list to map
	 * @param list list of T
	 * @param keyProp The property to be used as the key in the Map.
	 * @param valProp The property to be used as the value in the Map.
	 * @return map
	 */
	protected Map<Object, Object> convertListToMap(List<T> list, String keyProp, String valProp) {
		Map<Object, Object> map = new LinkedHashMap<Object, Object>();
		for (T data : list) {
			try {
				Object key = PropertyUtils.getProperty(data, keyProp);
				Object val = PropertyUtils.getProperty(data, valProp);
				map.put(key, val);
			}
			catch (Exception e) {
				throw new RuntimeException(e);
			}
		}
		return map;
	}

	/**
	 * deleteAll
	 * 
	 * @return count of deleted records
	 * @throws DataAccessException if a data access error occurs
	 */ 
	public int deleteAll() throws DataAccessException {
		return deleteByExample(createExample());
	}
}
