package jp.sf.amateras.mirage;

import java.lang.reflect.Constructor;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import jp.sf.amateras.mirage.bean.BeanDesc;
import jp.sf.amateras.mirage.bean.PropertyDesc;
import jp.sf.amateras.mirage.dialect.Dialect;
import jp.sf.amateras.mirage.naming.NameConverter;
import jp.sf.amateras.mirage.type.ValueType;
import jp.sf.amateras.mirage.util.MirageUtil;

public class DefaultResultEntityCreator implements ResultEntityCreator {

	/**
	 * Creates and returns one entity instance from the ResultSet.
	 *
	 * @param <T> the type parameter of entity class
	 * @param clazz the entity class
	 * @param rs the ResultSet
	 * @param meta the ResultSetMetaData
	 * @param columnCount the column count
	 * @param beanDesc the BeanDesc of the entity class
	 * @param dialect the Dialect
	 * @param valueTypes the list of ValueTypes
	 * @param nameConverter the NameConverter
	 * @return the instanceo of entity class or Map
	 * @throws InstantiationException
	 * @throws IllegalAccessException
	 * @throws SQLException
	 */
	@SuppressWarnings("unchecked")
	public <T> T createEntity(Class<T> clazz, ResultSet rs,
			ResultSetMetaData meta, int columnCount, BeanDesc beanDesc,
			Dialect dialect, List<ValueType> valueTypes, NameConverter nameConverter)
			throws Exception {

		if(dialect.getValueType() != null){
			ValueType valueType = dialect.getValueType();
			if(valueType.isSupport(clazz)){
				return (T) valueType.get(clazz, rs, 1);
			}
		}

		for(ValueType valueType: valueTypes){
			if(valueType.isSupport(clazz)){
				return (T) valueType.get(clazz, rs, 1);
			}
		}

		T entity = null;

		if(clazz == Map.class){
			entity = (T) new HashMap<String, Object>();
		} else {
			Constructor<T> constructor = clazz.getConstructor(new Class<?>[0]);
			entity = constructor.newInstance();
		}

		for(int i = 0; i < columnCount; i++){
			String columnName = meta.getColumnName(i + 1);
			String propertyName = nameConverter.columnToProperty(columnName);

			PropertyDesc pd = beanDesc.getPropertyDesc(propertyName);

			if(pd != null){
				Class<?> fieldType = pd.getPropertyType();
				ValueType valueType = MirageUtil.getValueType(fieldType, dialect, valueTypes);
				if(valueType != null){
					pd.setValue(entity, valueType.get(fieldType, rs, columnName));
				}
			}
		}

		return entity;
	}

}
