/* ===================================================================
 * WebUtil.java
 * 
 * Created Oct 4, 2004 12:22:10 PM
 * 
 * Copyright (c) 2004 Matt Magoffin (spamsqr@msqr.us)
 * 
 * This program is free software; you can redistribute it and/or 
 * modify it under the terms of the GNU General Public License as 
 * published by the Free Software Foundation; either version 2 of 
 * the License, or (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful, 
 * but WITHOUT ANY WARRANTY; without even the implied warranty of 
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 
 * General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License 
 * along with this program; if not, write to the Free Software 
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 
 * 02111-1307 USA
 * ===================================================================
 * $Id: WebUtil.java 74 2009-05-15 04:44:37Z msqr $
 * ===================================================================
 */

package magoffin.matt.ieat.web;

import java.util.List;
import java.util.Map;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;

import magoffin.matt.ieat.ApplicationNotConfiguredException;
import magoffin.matt.ieat.IllegalActionException;
import magoffin.matt.ieat.biz.BizContext;
import magoffin.matt.ieat.biz.DomainObjectFactory;
import magoffin.matt.ieat.biz.RecipeSearchBiz;
import magoffin.matt.ieat.biz.UserBiz;
import magoffin.matt.ieat.domain.Meal;
import magoffin.matt.ieat.domain.Recipe;
import magoffin.matt.ieat.domain.RecipeSearchCriteria;
import magoffin.matt.ieat.domain.UiEdit;
import magoffin.matt.ieat.domain.UiIndex;
import magoffin.matt.ieat.domain.UiSearchResults;
import magoffin.matt.ieat.domain.UiSessionData;
import magoffin.matt.ieat.domain.User;
import magoffin.matt.ieat.util.MealCommand;
import magoffin.matt.ieat.util.RecipeCommand;
import magoffin.matt.ieat.util.UserCommand;
import magoffin.matt.ieat.web.util.WebBizContext;
import magoffin.matt.xweb.util.AppContextSupport;

import org.springframework.validation.ObjectError;

/**
 * Utility methods for web layer.
 * 
 * @author Matt Magoffin (spamsqr@msqr.us)
 * @version $Revision: 74 $ $Date: 2009-05-15 16:44:37 +1200 (Fri, 15 May 2009) $
 */

public final class WebUtil {
	
	/**
	 * Get the application-scoped AppContextSupport object.
	 * 
	 * @param request the current request
	 * @return AppContextSupport, or <em>null</em> if not available
	 */
	public static AppContextSupport getAppContextSupport(HttpServletRequest request) {
		Object o = request.getSession().getServletContext().getAttribute(
				WebConstants.APP_KEY_APP_CONTEXT);
		if ( o instanceof AppContextSupport ) {
			return (AppContextSupport)o;
		}
		return null;
	}
	
	/**
	 * Get a BizContext for the current request.
	 * 
	 * <p>This method will check the {@link BizContext#FEATURE_ANONYMOUS} setting 
	 * and throw an {@link IllegalActionException} if the current request is 
	 * for an anonymous user and anonymous features are disabled. Likewise if 
	 * the <code>userRequired</code> parameter is <em>true</em> then an 
	 * <code>IllegalActionException</code> will be thrown if the current request 
	 * is for an anonymous user.</p>
	 * 
	 * @param request the current request
	 * @param userRequired if <em>true</em> then a logged-in user must be on the current
	 * request, otherwise an {@link IllegalActionException} will be thrown
	 * @return the BizContext
	 * @throws IllegalActionException if <var>userRequired</var> is <em>true</em> and 
	 * no user exists on the current request
	 */
	public static BizContext getBizContext(HttpServletRequest request, boolean userRequired) {
		BizContext context = getAnonymousBizContext(request);
		if ( context.getActingUser() == null && (userRequired || 
				!(context.getAppContextSupport().isParameterTrue(
						BizContext.FEATURE_ANONYMOUS))) ) {
			// save request URL
			throw new IllegalActionException();
		}
		
		// verify app is set up
		AppContextSupport appContextSupport = getAppContextSupport(request);
		if ( !appContextSupport.isParameterTrue(WebConstants.SETTING_KEY_SETUP_COMPLETE) ) {
			throw new ApplicationNotConfiguredException();
		}
		
		String baseUrl = "http://" +request.getServerName() +
			(request.getServerPort() == 80 || request.getServerPort() == 443 
					? "" : ":"+String.valueOf(request.getServerPort()))
			+request.getContextPath();
		context.setAttribute(WebBizContext.URL_BASE,baseUrl);
		return context;
	}
	
	/**
	 * Get a BizContext for the current request that can be an anonymous user 
	 * even if anonymous features are disabled.
	 * 
	 * <p>This can be used by actions that can be performed even if anonymous 
	 * features are disabled, such as self registration.</p>
	 * 
	 * @param request the current request
	 * @return a BizContext
	 */
	public static BizContext getAnonymousBizContext(HttpServletRequest request) {
		AppContextSupport appContextSupport = getAppContextSupport(request);
		WebBizContext context = new WebBizContext(appContextSupport);
		UiSessionData usd = getUserSessionData(request);
		if ( usd != null && usd.getActingUser() != null ) {
			context.setActingUser(usd.getActingUser());
		}
		String baseUrl = "http://" +request.getServerName() +
			(request.getServerPort() == 80 || request.getServerPort() == 443 
					? "" : ":"+String.valueOf(request.getServerPort()))
			+request.getContextPath();
		context.setAttribute(WebBizContext.URL_BASE,baseUrl);
		return context;
	}
	
	/**
	 * Get a BizContext for the current request that must be for an admin user.
	 * @param request the current request
	 * @param userBiz the UserBiz instance
	 * @return the context, only if the current user is logged in as an admin user
	 * (with {@link UserBiz#ACCESS_ADMIN} access level)
	 * @throws IllegalActionException if no user is logged in as an admin for the given request
	 */
	public static BizContext getAdminBizContext(HttpServletRequest request, UserBiz userBiz) {
		BizContext context = getBizContext(request,true);
		if ( !userBiz.hasAccessLevel(context.getActingUser(),UserBiz.ACCESS_ADMIN) ) {
			throw new IllegalActionException("Must be an admin user.");
		}
		return context;
	}
	
	/**
	 * Remove the user session data.
	 * 
	 * @param request the current reqeust
	 */
	public static void clearUserSessionData(HttpServletRequest request) {
		HttpSession session = request.getSession(false);
		if ( session != null && session.getAttribute(
				WebConstants.SES_KEY_USER_SESSION_DATA) != null ) {
			session.removeAttribute(WebConstants.SES_KEY_USER_SESSION_DATA);
		}
	}
	
	/**
	 * Save the action user to session.
	 * 
	 * <p>This method will save the <code>user</code> instance to session, allowing 
	 * the application to maintain a logged-in state for the user. The user is 
	 * added to a {@link UiSessionData} instance as the <code>actingUser</code>
	 * property. If the user has the {@link UserBiz#ACCESS_ADMIN} permission bit set 
	 * the <code>admin</code> property of the <code>UiSessionData</code> will be set 
	 * to <em>true</em>.</p>
	 * 
	 * @param request the current request
	 * @param dof the DomainObjectFactory
	 * @param user the User to save in session
	 */
	public static void saveUserSessionData(HttpServletRequest request, DomainObjectFactory dof, User user) {
		UiSessionData usd = dof.getUiSessionDataInstance();
		usd.setActingUser(user);
		if ( user.getAccessLevel() != null ) {
			int level = user.getAccessLevel().intValue();
			if ( (level & UserBiz.ACCESS_ADMIN) == 1 ) {
				usd.setAdmin(true);
			}
		}
		HttpSession session = request.getSession(true);
		session.setAttribute(WebConstants.SES_KEY_USER_SESSION_DATA,usd);
	}
	
	/**
	 * Get the <code>UiSessionData</code> from the current session.
	 * 
	 * @param request the current request
	 * @return the UiSessionData instance, or <em>null</em> if not available
	 */
	public static UiSessionData getUserSessionData(HttpServletRequest request) {
		HttpSession session = request.getSession(false);
		if ( session == null ) return null;
		return (UiSessionData)session.getAttribute(
				WebConstants.SES_KEY_USER_SESSION_DATA);
	}
	
	/**
	 * Initialize a search result object from search criteria objects.
	 * @param sr the search result object to populate
	 * @param indexCriteria the index criteria (may be <em>null</em>)
	 * @param recipeCriteria the recipe criteria (may be <em>null</em>)
	 * @param dof the domain object factory
	 */
	@SuppressWarnings("unchecked")
	public static void initSearchResults(UiSearchResults sr, 
			RecipeSearchBiz.IndexCriteria indexCriteria, 
			RecipeSearchCriteria recipeCriteria, 
			DomainObjectFactory dof) {
		if ( indexCriteria != null ) {
			sr.setPageSize(indexCriteria.getPageSize());
			sr.setPageStart(indexCriteria.getPageStart());
			UiIndex idx = dof.getIndexInstance();
			UiIndex.IndexSectionType sec = dof.getIndexSelectionTypeInstance();
			sec.setIndexKey(indexCriteria.getDisplaySection());
			sec.setSelected(true);
			idx.getIndexSection().add(sec);
			sr.setUiIndex(idx);
		}
		if ( recipeCriteria != null ) {
			sr.setRecipeCriteria(recipeCriteria);
		}
	}
	
	/**
	 * Save RecipeCommand data into a model Map.
	 * 
	 * <p>This is mostly used to prepare the model for redirecting to a Recipe view.</p>
	 * 
	 * @param model the model
	 * @param recipeCommand the RecipeCommand to copy data from
	 */
	public static void saveRecipeCommand(Map<String, Object> model, RecipeCommand recipeCommand) {
		if ( recipeCommand == null ) return;
		model.put(WebConstants.REQ_PARAM_RECIPE_ID,recipeCommand.getRecipeId());
		saveIndexCriteriaCommand(model,recipeCommand.getIndex());
		if ( recipeCommand.getCriteria() != null ) {
			RecipeSearchCriteria criteria = recipeCommand.getCriteria();
			if ( criteria.getName() != null && criteria.getName().length() > 0 ) {
				model.put("criteria.recipeName",criteria.getName());
			}
			if ( "simple".equals(criteria.getMode()) ) {
				model.put("criteria.mode",criteria.getMode());
				if ( criteria.getSimpleQuery() != null && criteria.getSimpleQuery().length() > 0 ) {
					model.put("criteria.simpleQuery",criteria.getSimpleQuery());
				}
			}
		}
	}
	
	/**
	 * Save UserCommand data into a model Map.
	 * 
	 * <p>This is mostly used to prepare the model for redirecting to a User view.</p>
	 * 
	 * @param model the model
	 * @param userCommand the UserCommand to copy data from
	 */
	public static void saveUserCommand(Map<String, Object> model, UserCommand userCommand) {
		if ( userCommand == null ) return;
		model.put(WebConstants.REQ_PARAM_USER_ID,userCommand.getUserId());
		saveIndexCriteriaCommand(model,userCommand.getIndex());
	}
	
	private static void saveIndexCriteriaCommand(Map<String, Object> model, RecipeSearchBiz.IndexCriteria criteria) {
		if ( criteria == null ) return;
		if ( criteria.getDisplaySection() != null && criteria.getDisplaySection().length() > 0 ) {
			model.put("index.displaySection",criteria.getDisplaySection());
		}
		if ( criteria.getPageSize() > 0 ) {
			model.put("index.pageSize", criteria.getPageSize());
			model.put("index.pageStart", criteria.getPageStart());
		}
	}
	
	
	/**
	 * Save MealCommand data into a model Map.
	 * 
	 * <p>This is mostly used to prepare the model for redirecting to a Meal view.</p>
	 * 
	 * @param model the model
	 * @param mealCommand the MealCommand to copy data from
	 */
	public static void saveMealCommand(Map<String, Object> model, MealCommand mealCommand) {
		if ( mealCommand == null ) return;
		model.put(WebConstants.REQ_PARAM_MEAL_ID,mealCommand.getMealId());
	}
	
	/**
	 * Save Meal data into a model Map.
	 * 
	 * @param model the model
	 * @param edit the edit data, assumed to have a Meal object in it
	 */
	public static void saveMealEdit(Map<String, Object> model, UiEdit edit) {
		Meal m = edit.getMeal();
		if ( m == null ) return;
		model.put(WebConstants.REQ_PARAM_MEAL_ID,m.getMealId());
	}
	
	/**
	 * Save recipe navigation data into the model object.
	 * 
	 * @param model the model to save the recipe navigation data into
	 * @param edit the edit data, assumed to have a Recipe object in it
	 */
	public static void saveRecipeEdit(Map<String, Object> model, UiEdit edit) {
		Recipe r = edit.getRecipe();
		if ( r == null ) {
			return;
		}
		model.put(WebConstants.REQ_PARAM_RECIPE_ID,r.getRecipeId());
		saveSearchResults(model,edit.getSearchResults());
	}
	
	/**
	 * Save recipe navigation data into the model object.
	 * 
	 * @param model the model to save the recipe navigation data into
	 * @param edit the edit data, assumed to have a Recipe object in it
	 */
	public static void saveUserEdit(Map<String, Object> model, UiEdit edit) {
		User u = edit.getUser();
		if ( u == null ) {
			return;
		}
		model.put(WebConstants.REQ_PARAM_USER_ID,u.getUserId());
		saveSearchResults(model,edit.getSearchResults());
	}
	
	@SuppressWarnings("unchecked")
	private static void saveSearchResults(Map<String, Object> model, UiSearchResults sr) {
		if ( sr != null ) {
			if ( sr.getPageSize() != null ) {
				model.put("index.pageSize",sr.getPageSize());
			}
			if ( sr.getPageStart() != null ) {
				model.put("index.pageStart",sr.getPageStart());
			}
			if ( sr.getUiIndex() != null ) {
				List<UiIndex.IndexSectionType> l = sr.getUiIndex().getIndexSection();
				for ( UiIndex.IndexSectionType sec : l ) {
					if ( sec.isSelected() ) {
						model.put("index.displaySection",sec.getIndexKey());
						break;
					}
				}
			}
			if ( sr.getRecipeCriteria() != null ) {
				RecipeSearchCriteria criteria = sr.getRecipeCriteria();
				if ( criteria.getMode() != null ) {
					model.put("criteria.mode",criteria.getMode());
				}
				if ( criteria.getSimpleQuery() != null ) {
					model.put("criteria.simpleQuery",criteria.getSimpleQuery());
				}
			}
		}
	}
	
	/**
	 * Set a flag indicating the saved session URL should not be cleared
	 * on this request.
	 * @param request the current request
	 */
	public static final void setSaveRequestURLFlag(HttpServletRequest request) {
		request.setAttribute(WebConstants.REQ_KEY_SAVE_SAVED_URL,Boolean.TRUE);
	}
	
	/**
	 * Return <em>true</em> if the save request URL flag is currently set.
	 * @param request the current request
	 * @return <em>true</em> if the save request URL flag is currently set
	 */
	public static final boolean isSaveRequestURL(HttpServletRequest request) {
		if ( request.getAttribute(WebConstants.REQ_KEY_SAVE_SAVED_URL) != null ) {
			return true;
		}
		return false;
	}
	
	/**
	 * Clear the flag indicating the saved session URL should not be cleared
	 * on this request.
	 * @param request the current request
	 */
	public static final void clearSaveRequestURLFlag(HttpServletRequest request) {
		request.removeAttribute(WebConstants.REQ_KEY_SAVE_SAVED_URL);
	}
	
	/**
	 * Remove the saved session URL.
	 * 
	 * @param request the current request
	 */
	public static final void clearSavedRequestURL(HttpServletRequest request) {
		request.getSession().removeAttribute(WebConstants.SES_KEY_SAVED_URL);
		request.removeAttribute(WebConstants.REQ_KEY_SAVE_SAVED_URL);
	}

	/**
	 * Save an error to session.
	 * 
	 * @param request the current request
	 * @param error the error to save
	 */
	public static final void saveError(HttpServletRequest request, ObjectError error) {
		request.getSession().setAttribute(WebConstants.SES_KEY_SAVED_MESSAGE,error);
	}
	
	/**
	 * Get the saved error from session.
	 * 
	 * @param request the current request
	 * @return the error, or <em>null</em> if not available
	 */
	public static final ObjectError getSavedError(HttpServletRequest request) {
		Object o = request.getSession().getAttribute(WebConstants.SES_KEY_SAVED_MESSAGE);
		if ( o instanceof ObjectError ) {
			return (ObjectError)o;
		}
		return null;
	}
	
	/**
	 * Remove the saved session URL.
	 * 
	 * @param request the current request
	 */
	public static final void clearSavedError(HttpServletRequest request) {
		request.getSession().removeAttribute(WebConstants.SES_KEY_SAVED_MESSAGE);
	}


}
