package jp.sf.amateras.mirage.bean;

import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;

import jp.sf.amateras.mirage.annotation.Transient;
import jp.sf.amateras.mirage.exception.BeanDescException;
import jp.sf.amateras.mirage.util.ReflectionUtil;

public class PropertyDescImpl implements PropertyDesc {

	private BeanDesc parent;
	private String name;
	private Class<?> type;
	private Method getterMethod;
	private Method setterMethod;
	private Field field;

	public PropertyDescImpl(BeanDesc parent, String name, Class<?> type,
			Field field, Method getterMethod, Method setterMethod){
		this.parent = parent;
		this.name = name;
		this.type = type;
		this.field = field;
		this.getterMethod = getterMethod;
		this.setterMethod = setterMethod;
	}

//	@Override
	public Object getValue(Object obj){
		try {
			if(getterMethod != null){
				return getterMethod.invoke(obj, (Object[]) null);
			} else {
				return field.get(obj);
			}
		} catch (IllegalAccessException ex) {
			throw new BeanDescException(ex);
			
		} catch (InvocationTargetException ex) {
			throw new BeanDescException(ex);
			
		}
	}

//	@Override
	public void setValue(Object obj, Object value){
		try {
			if(setterMethod != null){
				setterMethod.invoke(obj, value);
			} else {
				field.set(obj, value);
			}
		} catch (IllegalAccessException ex) {
			throw new BeanDescException(ex);
			
		} catch (InvocationTargetException ex) {
			throw new BeanDescException(ex);
			
		}
	}

//	@Override
	public boolean isReadable(){
		if(getterMethod != null || field != null){
			return true;
		}
		return false;
	}

//	@Override
	public boolean isWritable(){
		if(setterMethod != null || field != null){
			return true;
		}
		return false;
	}

//	@Override
	public Class<?> getPropertyType(){
		return type;
	}

//	@Override
	public String getPropertyName(){
		return name;
	}

//	@Override
	public Field getField(){
		return field;
	}

//	@Override
	public <T extends Annotation> T getAnnotation(Class<T> type){
		T ann = null;
		if(field != null){
			ann = field.getAnnotation(type);
		}
		if(ann == null && setterMethod != null){
			ann = setterMethod.getAnnotation(type);
		}
		if(ann == null && getterMethod != null){
			ann = getterMethod.getAnnotation(type);
		}
		if(ann == null && field == null){
			Field field = ReflectionUtil.getField(parent.getType(), getPropertyName());
			if(field != null){
				ann = field.getAnnotation(type);
			}
		}
		return ann;
	}

//	@Override
	public boolean isTransient() {
		Transient ann = getAnnotation(Transient.class);
		if(ann != null){
			return true;
		}

		if(field != null){
			return Modifier.isTransient(field.getModifiers());
		}

		Field field = ReflectionUtil.getField(parent.getType(), getPropertyName());
		if(field != null){
			return Modifier.isTransient(field.getModifiers());
		}

		return false;
	}

	@Override
	public String toString() {
		return new StringBuilder(super.toString())
			.append("[").append(name).append(":").append(type == null ? null : type.getSimpleName()).append("]")
			.toString();
	}
}
