package com.capco.eshell.control;

import java.util.Locale;
import javax.servlet.http.*;
import com.capco.mapper.Mapper;
import com.capco.mapper.Mapping;
import com.capco.mapper.MapperState;
import com.capco.mapper.MappingFailure;
import com.capco.mapper.MappingFailureHandler;
import org.apache.struts.action.*;

/**
 *  EShellActionForm is an extention of the ActionForm.
 *
 *  @author  François Rey (FREY - francois.rey@capco.com)
 *  @author  Eric Bariaux (EBRX - eric.bariaux@capco.com)
 *  @version 1.0
 *  @see org.apache.struts.action.ActionForm
 */
public class EShellActionForm extends ActionForm implements MappingFailureHandler {
	
 	private ActionErrors errors = null;
 	
 	private MapperState mapperState = null;
 	
 	private static final String MAPPER_SUFFIX = "Mapper";
 	
 	public EShellActionForm() {}
 	
 	/**
 	 * Add an error to the list of errors. This operation does a lazy
 	 * initialization of the error list, however this is not thread safe.
 	 */
 	public void addError(String property, ActionError error) {
 		if (errors==null) {
 			errors=new ActionErrors();
 		}
 		errors.add(property, error);
 	}
 	
 	/**
 	 * Return the list of errors or null if no error has been recorded.
 	 */
 	public ActionErrors getErrors() {
 		return errors;
 	}
 	
 	/**
 	 * Set the list of errors.
 	 */
 	public void setErrors(ActionErrors errors) {
 		this.errors=errors;
 	}
 	
 	/**
 	 * Get the mapper for this form, or null if there is none defined.
 	 */
 	public Mapper getMapper(ActionMapping mapping) {
 		EShellActionServlet servlet = getEShellServlet();
 		Mapper mapper = servlet.getMapper(mapping.getName() + MAPPER_SUFFIX);
 		if (mapper!=null) {
 			mapperState = mapper.newMapperState();
 		}
 		return mapper;
 	}
 	
 	/**
 	 * Get the mapper state for this form, or null if none is available.
 	 * This methods must be called after getMapper() has been called first
 	 * (as it is done in the validate method).
 	 */
 	public MapperState getMapperState() {
 		return mapperState;
 	}
 	
	/**
	 * Method for handling mapping failures. Descendants must override this method
	 * with appropriate behavior. The default implementation creates one
         * <CODE>ActionError</CODE> per <CODE>MappingFailure</CODE>, to the conditions
         * that the latter contains a non-null message key and that the mapping
         * that failed has a non-null name. In such case the property name used
         * for registering the <CODE>ActionError</CODE> is the name of the mapping.
	 * @param mapping The mapping which failed.
	 * @param mf The <CODE>MappingFailure</CODE> to be handled.
	 */
	public void handleMappingFailure(Mapping mapping, MappingFailure mf) {
            if (mf==null || mapping==null) return;
            if (mf.getMessageKey()!=null && mapping.getName()!=null)
                this.addError(mapping.getName(), new ActionError(mf.getMessageKey()));
	}

    /**
     * Return the eShell action servlet associated to this form.
     */
    public EShellActionServlet getEShellServlet() {
		return (EShellActionServlet)getServlet();
	}

    /**
     * Return the locale for the given request. If no session is set,
     * or if the session has no locale set, the default locale
     * is returned.
     */
    public Locale getLocale(HttpServletRequest request) {
		Locale result = null;
		HttpSession session = request.getSession();
		if (session!=null) {
			result = (Locale)session.getAttribute(Action.LOCALE_KEY);
			if (result == null) result = Locale.getDefault();
		} else {
			result = Locale.getDefault();
		}
		return result;
	}

    /**
     * Method called just before executing the mapper for this form, if any.
     * The default implementation does nothing. Subclasses can override this
     * method in order to provide further setting of the mapper state before
     * the mapper is invoked.
     *
     * @param mapping The mapping used to select this instance
     * @param request The servlet request we are processing
     * @param state The MapperState instance that will be used for validation.
     * This instance has already been populated with the form, the failure handler,
     * and the locale.
     */
    public void prepareMapperState(ActionMapping mapping,
                                 HttpServletRequest request,
                                 MapperState state) {
    }

    /**
     * Validate the properties that have been set for this HTTP request,
     * and return an <code>ActionErrors</code> object that encapsulates any
     * validation errors that have been found.  If no errors are found,
     * return <code>null</code> or an <code>ActionErrors</code> object with
     * no recorded error messages.
     * <p>
     * The default implementation performs no validation and returns
     * <code>null</code>.  Subclasses must override this method to provide
     * any validation they wish to perform.
     *
     * @param mapping The mapping used to select this instance
     * @param request The servlet request we are processing
     */
    public ActionErrors validate(ActionMapping mapping,
                                 HttpServletRequest request) {
        Mapper mapper = getMapper(mapping);
        if (mapper!=null) {
            mapperState.setObject(mapper, Mapper.SOURCE, this);
            mapperState.setMappingFailureHandler(this);
            mapperState.setLocale(getLocale(request));
            prepareMapperState(mapping, request, mapperState);
            mapper.doValidateConvert(mapperState);
        }
	return getErrors();
    }

    /**
     * Reset all bean properties to their default state.  This method is
     * called before the properties are repopulated by the controller servlet.
     * <p>
     * The methods resets the errors and mapper state. Subclasses should call
     * this method to reset all bean properties to default values in their own
     * reset() method.
     *
     * @param mapping The mapping used to select this instance
     * @param request The servlet request we are processing
     */
    public void reset(ActionMapping mapping, HttpServletRequest request) {
 		if (mapperState!=null) {
 			mapperState.reset();
 		}
 		if (errors!=null) {
 			errors.clear();
 		}
    }

 	
}
