package org.seasar.framework.beans.impl;

import java.lang.reflect.Method;
import java.math.BigDecimal;
import java.sql.Time;
import java.sql.Timestamp;

import org.seasar.framework.beans.BeanDesc;
import org.seasar.framework.beans.IllegalPropertyRuntimeException;
import org.seasar.framework.beans.PropertyDesc;
import org.seasar.framework.util.BigDecimalConversionUtil;
import org.seasar.framework.util.BooleanConversionUtil;
import org.seasar.framework.util.DateConversionUtil;
import org.seasar.framework.util.DoubleConversionUtil;
import org.seasar.framework.util.FloatConversionUtil;
import org.seasar.framework.util.IntegerConversionUtil;
import org.seasar.framework.util.LongConversionUtil;
import org.seasar.framework.util.MethodUtil;
import org.seasar.framework.util.ShortConversionUtil;
import org.seasar.framework.util.SqlDateConversionUtil;
import org.seasar.framework.util.TimeConversionUtil;
import org.seasar.framework.util.TimestampConversionUtil;
import org.seasar.framework.exception.EmptyRuntimeException;

public final class PropertyDescImpl implements PropertyDesc {

	private String propertyName_;
	private Class propertyType_;
	private Method readMethod_;
	private Method writeMethod_;
	private BeanDesc beanDesc_;
	
	public PropertyDescImpl(String propertyName, Class propertyType,
			Method readMethod, Method writeMethod, BeanDesc beanDesc) {
				
		if (propertyName == null) {
			throw new EmptyRuntimeException("propertyName");
		}
		if (propertyType == null) {
			throw new EmptyRuntimeException("propertyType");
		}
		
		propertyName_ = propertyName;
		propertyType_ = propertyType;
		readMethod_ = readMethod;
		writeMethod_ = writeMethod;
		beanDesc_ = beanDesc;
	}
	
	public final String getPropertyName() {
		return propertyName_;
	}

	public final Class getPropertyType() {
		return propertyType_;
	}
	
	public final Method getReadMethod() {
		return readMethod_;
	}
	
	public final void setReadMethod(Method readMethod) {
		readMethod_ = readMethod;
	}
	
	public final boolean hasReadMethod() {
		return readMethod_ != null;
	}
	
	public final Method getWriteMethod() {
		return writeMethod_;
	}
	
	public final void setWriteMethod(Method writeMethod) {
		writeMethod_ = writeMethod;
	}
	
	public final boolean hasWriteMethod() {
		return writeMethod_ != null;
	}
	
	public final Object getValue(Object target) {
		return MethodUtil.invoke(readMethod_, target, null);
	}
	
	public final void setValue(Object target, Object value) {
		try {
			MethodUtil.invoke(writeMethod_, target, new Object[]{
				convertIfNeed(value)});
		} catch (Throwable t) {
			throw new IllegalPropertyRuntimeException(
					beanDesc_.getBeanClass(), propertyName_, t);
		}
	}
	
	public final BeanDesc getBeanDesc() {
		return beanDesc_;
	}
	
	public final String toString() {
		StringBuffer buf = new StringBuffer();
		buf.append("propertyName=");
		buf.append(propertyName_);
		buf.append(",propertyType=");
		buf.append(propertyType_.getName());
		buf.append(",readMethod=");
		buf.append(readMethod_ != null ?readMethod_.getName() : "null");
		buf.append(",writeMethod=");
		buf.append(writeMethod_ != null ?writeMethod_.getName() : "null");
		return buf.toString();
	}
	
	public Object convertIfNeed(Object arg) {
		if (propertyType_.isPrimitive()) {
			if (propertyType_ == int.class) {
				Integer i = IntegerConversionUtil.toInteger(arg);; 
				if (i != null) {
					return i;
				}
				return new Integer(0);
			} else if (propertyType_ == double.class) {
				Double d = DoubleConversionUtil.toDouble(arg);
				if (d != null) {
					return d;
				}
				return new Double(0);
			} else if (propertyType_ == long.class) {
				Long l = LongConversionUtil.toLong(arg);
				if (l != null) {
					return l;
				}
				return new Long(0);
			} else if (propertyType_ == float.class) {
				Float f = FloatConversionUtil.toFloat(arg);
				if (f != null) {
					return f;
				}
				return new Float(0);
			} else if (propertyType_ == short.class) {
				Short s = ShortConversionUtil.toShort(arg);
				if (s != null) {
					return s;
				}
				return new Short((short) 0);
			} else if (propertyType_ == boolean.class) {
				Boolean b = BooleanConversionUtil.toBoolean(arg);
				if (b != null) {
					return b;
				}
				return Boolean.FALSE;
			}
		} else if (Number.class.isAssignableFrom(propertyType_)) {
			if (propertyType_ == Integer.class) {
				return IntegerConversionUtil.toInteger(arg);
			} else if (propertyType_ == Double.class) {
				return DoubleConversionUtil.toDouble(arg);
			} else if (propertyType_ == Long.class) {
				return LongConversionUtil.toLong(arg);
			} else if (propertyType_ == Float.class) {
				return FloatConversionUtil.toFloat(arg);
			} else if (propertyType_ == Short.class) {
				return ShortConversionUtil.toShort(arg);
			} else if (propertyType_ == BigDecimal.class) {
				return BigDecimalConversionUtil.toBigDecimal(arg);
			}
		} else if (java.util.Date.class.isAssignableFrom(propertyType_)) {
			if (propertyType_ == java.util.Date.class) {
				return DateConversionUtil.toDate(arg);
			} else if (propertyType_ == Timestamp.class) {
				return TimestampConversionUtil.toTimestamp(arg);
			} else if (propertyType_ == java.sql.Date.class) {
				return SqlDateConversionUtil.toDate(arg);
			} else if (propertyType_ == Time.class) {
				return TimeConversionUtil.toTime(arg);
			}
		}
		return arg;
	}
}
