package hirondelle.web4j.action; 

import hirondelle.web4j.util.Util;
import java.util.logging.*;

/** 
 Type-safe enumeration for common operations.

 <P>This <a href="http://www.javapractices.com/Topic1.cjp">type-safe enumeration</a> 
 is somewhat unusual, since its elements are not meant to form a strictly distinct 
 set. For example, one might choose to identify the same operation using 
 either {@link #Save} or {@link #Add}, simply according to taste. Elements may be used as 
 desired, but please note that {@link #isDatastoreEdit} and {@link #hasSideEffects()} return
 <tt>true</tt> only for specified items. 
 
 <P>Many {@link Action} implementations can benefit from using a request parameter named 
 <tt>'Operation'</tt>, whose value corresponds to a specific <em>subset</em> of the members of this enumeration.
 For example, {@link hirondelle.web4j.action.ActionTemplateSearch} expects only {@link #Show} and {@link #Search}
 operations. See {@link hirondelle.web4j.action.ActionImpl#getOperation()} as well. 

 <P>Occasionally, you may need to define just such a subset of operations for your own actions. 
 Typically, your code would use the following code snippets. 
 <P>
 Define an <tt>Operation</tt> {@link hirondelle.web4j.request.RequestParameter} in the {@link Action} using :
<PRE>
  {@code
public static final RequestParameter SUPPORTED_OP = RequestParameter.withRegexCheck(
    "Operation", 
    Pattern.compile("(" + Operation.Show + "|" + Operation.Search +  ")") 
  );
  }
</PRE>
  Set the value of a corresponding <tt>Operation</tt> field in the {@link Action} constructor using :
<PRE>
  {@code
fOperation = Operation.valueOf(aRequestParser.toString(SUPPORTED_OP));
  }
</PRE>

 <P>Your {@link Action#execute} method will then branch according to the value of 
 the <tt>fOperation</tt> field.
 
 <P><em>Note regarding forms submitted by hitting the Enter key.</em><br>
 One must exercise care for the possible submission of forms by hitting the Enter key. 
 Browser behavior is not specified exactly by HTML 4, and various browsers exhibit 
 different behaviors. A common workaround is to place  the <tt>Operation</tt> in a 
 <tt>HIDDEN</tt> form item, to ensure that it is always submitted, regardless of 
 the submission mechanism. 
 
 <P>See as well this 
 <a href="http://www.javapractices.com/Topic203.cjp">discussion</a> of <tt>Submit</tt>
 buttons in multilingual applications.
*/
public enum Operation  { 

  /** Add an item to the datastore. */
  Add,
  
  /** Post a change to an item to the datastore.  */
  Change,
  
  /** Delete an item from the datastore.  */
  Delete,

  /**  Delete all items from the datastore.  */
  DeleteAll,

  /** Save an edit to the datastore.  */
  Save,
  
  /** Apply an edit to the datastore.  */
  Apply,

  /**
   Activate an item.
   
   <P>The reverse of {@link #Inactivate}
  */
  Activate,
  
  /**
   Inactivate an item.
   
   <P>Often used to implement an <em>abstract</em> <tt>delete</tt> operation, where 
   an item is nominally removed (perhaps by setting the value of a certain column),
   but no physical deletion of records occurs. 
  */
  Inactivate,
  
  /** 
   Start some process.
    
   <P>The reverse of {@link #Stop}. 
  */
  Start,
   
  /** 
   Stop some process. 
   
   <P>The reverse of {@link #Start}.
  */
  Stop,
  
  /** Retrieve a single item, for read-only display.  */
  Fetch,
  
  /** Fetch a single item, in preparation for editing the item.  */
  FetchForChange,

  /** 
   Fetch items needed before adding a new item.
   An example use case is adding a new item which has a parent of some sort.
   The parent will need to be fetched before showing the form for adding the child.
  */
  FetchForAdd,
  
  /** Retrieve what is usually a list of many items, for read-only display.  */
  List,
  
  /** Retrieve what is usually a list of many items, in preparation for editing the items.  */
  ListForChange,
  
  /** Show an item.  */
  Show,
  
  /** Generate a result.  */
  Generate,
  
  /**  Render a result.  */
  Render,
  
  /**  Display a result.  */
  Display,
  
  /** Search for one or more items.  */
  Search,
  
  /** Fetch the next item in a list.  */
  Next,
  
  /** Fetch the previous item in a list.  */
  Previous,
  
  /** Fetch the first item in a list.  */
  First,
  
  /** Fetch the last item in a list.  */
  Last,
  
  /** Generic operation meant as a catch-all. */
  Do;
  
  /**
   Return <tt>true</tt> only if this <tt>Operation</tt> represents an action which 
   has edited the datastore : {@link #Add}, {@link #Change}, {@link #Delete}, 
   {@link #DeleteAll}, {@link #Save}, {@link #Apply}, {@link #Inactivate}, or {@link #Activate}.
   
   <P>Intended to identify actions which very likely require 
   <a href="http://www.javapractices.com/Topic181.cjp">a redirect instead of a forward</a>. 
  */
  public boolean isDatastoreEdit(){
    return (
      this == Add || this == Change || this == Delete || this == DeleteAll ||
      this == Save || this == Apply || this == Inactivate || this == Activate
    );
  }
  
  /** 
   Returns <tt>true</tt> only if this <tt>Operation</tt> <tt>isDataStoreEdit()</tt>, 
   or is {@link #Start} or {@link #Stop}.
     
   <P>Intended to identify cases that need a <tt>POST</tt> request.
  */
  public boolean hasSideEffects(){
    return isDatastoreEdit() || this == Start || this == Stop;
  }
  
  /**
   Parse a parameter value into an <tt>Operation</tt> (not case-sensitive).
   
   <P>Similar to {@link #valueOf}, but not case-sensitive, and has alternate behavior when a problem is found. 
   If <tt>aOperation</tt> has no content, or has an unknown value, then a message 
   is logged at <tt>SEVERE</tt> level, and <tt>null</tt> is returned.
  */
  public static Operation valueFor(String aOperation)  {
    Operation result = null;
    if ( Util.textHasContent(aOperation) ) {
      for (Operation operation : Operation.values()) {
        if ( operation.name().equalsIgnoreCase(aOperation)) {
          result = operation;
          break;
        }
      }
      if( result == null ) {
        fLogger.severe("'Operation' has an unknown value: " + Util.quote(aOperation));
      }
    }
    else {
      String message = "'Operation' has an empty or null value.";
      fLogger.severe(message);
    }
    return result;
  }
  
  // PRIVATE //
  private static final Logger fLogger = Util.getLogger(Operation.class);
}
