package org.jboss.seam.util;

import java.beans.Introspector;
import java.lang.reflect.Field;
import java.lang.reflect.Method;

public class TypedBeanProperty
{
   private Field propertyField;
   private Method propertyGetter;
   private Method propertySetter;
   
   private String name;
   
   private boolean isFieldProperty;
   private boolean set = false;
   
   public TypedBeanProperty(Class<?> cls, Class type)
   {      
      // First check declared fields
      for (Field f : cls.getDeclaredFields())
      {
         if (f.getGenericType().equals(type)) 
         {
            setupFieldProperty(f);           
            set = true;
            return;
         }
      }      
      
      // Then check public fields, in case it's inherited
      for (Field f : cls.getFields())
      {
         if (f.getGenericType().equals(type)) 
         {
            setupFieldProperty(f);
            set = true;
            return;
         }
      }
      
      // Then check public methods (we ignore private methods)
      for (Method m : cls.getMethods())
      {
         if (m.getGenericReturnType().equals(type))
         {
            String methodName = m.getName();
            
            if ( m.getName().startsWith("get") )
            {
               this.name = Introspector.decapitalize( m.getName().substring(3) );
            }
            else if ( methodName.startsWith("is") )
            {
               this.name = Introspector.decapitalize( m.getName().substring(2) );
            }            
            
            if (this.name != null)
            {
               this.propertyGetter = Reflections.getGetterMethod(cls, this.name);
               this.propertySetter = Reflections.getSetterMethod(cls, this.name);
               isFieldProperty = false;               
               set = true;
            }
            else
            {
               throw new IllegalStateException("Invalid accessor method, must start with 'get' or 'is'.  " +
                     "Method: " + m + " in class: " + cls);
            }
         }
      }      
   }
   
   private void setupFieldProperty(Field propertyField)
   {
      this.propertyField = propertyField;
      isFieldProperty = true;
      this.name = propertyField.getName();
   }   
   
   public void setValue(Object bean, Object value)
   {
      if (isFieldProperty)
      {
         Reflections.setAndWrap(propertyField, bean, value);         
      }
      else
      {
         Reflections.invokeAndWrap(propertySetter, bean, value);
      }
   }
   
   public Object getValue(Object bean)
   {
      if (isFieldProperty)
      {
         return Reflections.getAndWrap(propertyField, bean);  
      }
      else
      {
         return Reflections.invokeAndWrap(propertyGetter, bean);
      }
   }   
   
   public String getName()
   {
      return name;
   }
   
   public boolean isSet()
   {
      return set;
   }
}
