package hirondelle.web4j.action;

import java.util.logging.*;
import java.util.regex.Pattern;
import hirondelle.web4j.request.RequestParameter;
import hirondelle.web4j.request.RequestParser;
import hirondelle.web4j.util.Util;
import hirondelle.web4j.model.AppException;

/**
 <b>Template</b> for "first show, then validate and apply" groups of operations.
 
 <P>A good example is a page allowing the user to change their preferences : from the 
 point of view of each user, there is only one set of user preferences. Often, a single form can be used 
 to allow editing of preferences. Here, there is no listing of multiple items. 
 
 <P>There are two operations in such cases :
 <ul>
 <li>show me the form with the current data (if any)
 <li>allow me to post my (validated) changes
 </ul> 
*/
public abstract class ActionTemplateShowAndApply extends ActionImpl {
  
  /**
   Constructor.
    
   @param aForward used for {@link Operation#Show} operations, and also for <em>failed</em> 
   {@link Operation#Apply} operations. This is the default response.
   @param aRedirect used for <em>successful</em> {@link Operation#Apply} operations. 
   @param aRequestParser passed to the superclass constructor.
  */
  protected ActionTemplateShowAndApply(ResponsePage aForward, ResponsePage aRedirect, RequestParser aRequestParser){
    super(aForward, aRequestParser);
    fRedirect = aRedirect;
  }
  
  /**
   The operations supported by this template.
   
   <P>The supported operations are :
   <ul>
   <li> {@link Operation#Show}
   <li> {@link Operation#Apply}
   </ul>
   
   The source of the <tt>Operation</tt> is described by {@link ActionImpl#getOperation()}.
  */
  public static final RequestParameter SUPPORTED_OPERATION = RequestParameter.withRegexCheck(
    "Operation", Pattern.compile("(" + Operation.Show + "|" + Operation.Apply +  ")") 
  );
  
  /**
   <b>Template</b> method.
   
   <P>In order to clearly understand the operation of this method, here is the 
   core of its implementation, with all abstract methods in <em>italics</em> :
   <PRE>
    if ( Operation.Show == getOperation() ){
      <em>show();</em>
    }
    else if ( Operation.Apply == getOperation() ){
      <em>validateUserInput();</em>
      if( ! hasErrors() ){
        <em>apply();</em>
        if ( ! hasErrors() ){
          setResponsePage(fRedirect);
        }
      }
    }
   </PRE>
  */
  @Override public ResponsePage execute() throws AppException {
    if ( Operation.Show == getOperation() ){
      fLogger.fine("'Show' Operation.");
      show();
    }
    else if ( Operation.Apply == getOperation() ){
      fLogger.fine("'Apply' Operation.");
      validateUserInput();
      if( ! hasErrors() ){
        fLogger.fine("Passes validation.");
        apply();
        if ( ! hasErrors() ){
          fLogger.fine("Applied successfully.");
          setResponsePage(fRedirect);
        }
      }
    }
    else {
      throw new AssertionError("Unexpected value for Operation : " + getOperation());
    }
    
    return getResponsePage();
  }

  /**
   Show the input form.
   
   <P>The form may or may not be populated.
  */
  protected abstract void show() throws AppException;

  /**
   Validate items input by the user into a form.
  
   <P>Applied to {@link Operation#Apply}.
    If an error occurs, then <tt>addError</tt> must be called.
  */  
  protected abstract void validateUserInput() throws AppException;
  
  /**
   Validate the user input, and then apply an edit to the database. 
    If an error occurs, then <tt>addError</tt> must be called.
  */
  protected abstract void apply() throws AppException;
  
  // PRIVATE //
  private ResponsePage fRedirect;
  private static final Logger fLogger = Util.getLogger(ActionTemplateShowAndApply.class);
}
