package gov.va.med.fw.ui.struts;

// Java classes
import gov.va.med.fw.model.AbstractEntity;
import gov.va.med.fw.validation.FormValidation;
import gov.va.med.fw.validation.Validatable;

import java.util.List;

import javax.servlet.http.HttpServletRequest;

import org.apache.commons.lang.builder.EqualsBuilder;
import org.apache.commons.lang.builder.HashCodeBuilder;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.struts.Globals;
import org.apache.struts.action.ActionMapping;
import org.apache.struts.action.ActionMessages;
import org.apache.struts.util.MessageResources;
import org.apache.struts.validator.ValidatorForm;

/**
 * A base class for all struts action form.  This class serves as a data buffer 
 * and a wrapper around a value object that purely contains data.
 * 
 * @author DNS   LEV
 * 
 * @author Muddaiah Ranga  //Implemented FormValidation and Validatable interfaces
 * @version 3.0
 */
public abstract class AbstractForm extends ValidatorForm implements FormValidation, Validatable
{
    private static final long serialVersionUID = -1370346746640850300L;
    /*
    * An instance of a base value object 
    */
   private AbstractEntity vo = null;
   private boolean readOnly = false;
   private boolean addAPerson = false;

   /**
    * An instance of a logger for logging information
    */
   protected Log logger = LogFactory.getLog( getClass() );

   protected AbstractForm() {
      super();
      setVO( createValueObject() );
   }
   
   /**
    * Sets an instance of a value object 
    * @param input
    */
   public void setVO( AbstractEntity input ) {
      vo = input;
   }
   
   /**
    * Returns a value object
    * @return A base class for all value objects 
    */
   public AbstractEntity getVO() {
      return vo;
   }
   
   public boolean isReadOnly() {
      return readOnly;
   }

   public void setReadOnly(boolean readOnly) {
      this.readOnly = readOnly;
   }

/**
    * Gets the validation mode. An implementation for Validatable interface.
    */
	public String getValidationMode()
	{
		return Validatable.EXHAUSTIVE;
	}
	
   /**
    * Performs Form(custom) validation. This is the place to perform additional validation which
    * couldn't be performed declaratively. To perform custom validation, override this method
    * and add validation logic. The default implementation always returns true (meaning no errors).
    * 
    * @param mapping the action maping
    * @param request the servlet request
    * 
    * @return return validation error messages.
    */
   public ActionMessages validateForm(ActionMapping mapping, HttpServletRequest request)
   {
   		return null;
   }
   
   
   /**
    * Gets the ActionMessages if found, otherwise creates new.
    * 
    * @param request the request object.
    */
   public ActionMessages getActionMessages(HttpServletRequest request)
   {
       ActionMessages actionMessages = (ActionMessages)request.getAttribute(Globals.ERROR_KEY);
       if(actionMessages == null)
       {
           actionMessages = new ActionMessages();
           request.setAttribute(Globals.ERROR_KEY,actionMessages);
       }
       return actionMessages;
   }
   
   /**
    * Gets the property value from the resource bundle.
    * 
    * @param request the request object
    * @param key the property key
    */
   public String getMessageFromBundle(HttpServletRequest request, String key) 
   {
       MessageResources resources = getResources(request);
       return resources == null ? key : resources.getMessage(key);
   }
   
   /**
    * <p>Return the default message resources for the current module.</p>
    *
    * @param request The servlet request we are processing
    */
   public MessageResources getResources(HttpServletRequest request) 
   {
       return ((MessageResources)request.getAttribute(Globals.MESSAGES_KEY));
   }
   
   /**
    * Sets an element at the specific index. If the index is greater than the size of the list, 
    * adds nulls to the list to make up the size.
    * 
    * @param list the list object
    * @param index the index
    * @param value the value to be set
    */
   public void setElement(List list, int index, Object value)
   {
       for(int i=list.size(); i<=index; i++)
       {
           list.add(null);
       }
       list.set(index,value);
   }
   
   /**
    * Increases the size of the list to the index value specified. 
    * If the index is greater than the size of the list, 
    * adds specified object value to it to make up the size.
    * 
    * @param list the list object
    * @param index the index    
    */
   public void setSize(List list, int index, Object value)
   {
       for(int i=list.size(); i<=index; i++)
       {
           list.add(value);
       }
   }   
   
   /**
    * Indicates whether some other object is "equal to" this one.
    * @param o the reference object with which to compare
    * @return Returns true if the reference object is equal to this one. False otherwise
    */
   public boolean equals(Object o) {
       return EqualsBuilder.reflectionEquals(this, o);
   }

   /**
    * Returns a hash code value for the object. This method is supported for 
    * the benefit of hashtables such as those provided by java.util.Hashtable
    * @return Returns a hash code value for the object
    */
   public int hashCode() {
       return HashCodeBuilder.reflectionHashCode(this);
   }
   
   /**
    * Returns a string representation of the object in multiple line.
    * @return A contextual string representation of the object
    */
   public String toString() {
      AbstractEntity vo = getVO();
      return (vo != null) ? vo.toString() : null;
   }

   public void reset(ActionMapping arg0, HttpServletRequest arg1) {
       super.reset(arg0, arg1);
       readOnly = false;    
   }

/**
    * Overriden by subclasses to provide specific value object implementation
    * @return An abstract entity
    */
   protected AbstractEntity createValueObject() 
   {
      return null;
   }

	public boolean isAddAPerson() {
		return addAPerson;
	}
	
	public void setAddAPerson(boolean addAPerson) {
		this.addAPerson = addAPerson;
	}
}