package org.openswing.springframework.web.servlet.handler;

import org.springframework.web.servlet.handler.AbstractHandlerMapping;
import javax.servlet.http.HttpServletRequest;
import org.openswing.swing.message.send.java.Command;
import org.springframework.web.servlet.HandlerExecutionChain;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.ui.ModelMap;
import javax.servlet.http.HttpServletResponse;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
import org.springframework.web.servlet.ModelAndView;
import org.openswing.swing.message.receive.java.Response;
import org.openswing.springframework.web.servlet.view.OpenSwingViewResolver;
import org.openswing.swing.util.server.ObjectReceiver;
import org.openswing.swing.util.server.DefaultObjectReceiver;


/**
 * <p>Title: OpenSwing Framework</p>
 * <p>Description: Handler mapping used when combining OpenSwing client layer to Spring server layer:
 * this handler manages HTTP requests by giving back a bean name according to Command.getMethodName() value.
 * It is similar to ControllerClassNameHandlerMapping.
 * It always includes an Interceptor (in first position) that extract Command object from the request input stream
 * and stores it as request attribute.</p>
 * <p>Copyright: Copyright (C) 2006 Mauro Carniel</p>
 *
 * <p> This file is part of OpenSwing Framework.
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the (LGPL) Lesser General Public
 * License as published by the Free Software Foundation;
 *
 *                GNU LESSER GENERAL PUBLIC LICENSE
 *                 Version 2.1, February 1999
 *
 * This library 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
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library General Public
 * License along with this library; if not, write to the Free
 * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 *       The author may be contacted at:
 *           maurocarniel@tin.it</p>
 *
 * @author Mauro Carniel
 * @version 1.0
 */
public class OpenSwingHandlerMapping extends AbstractHandlerMapping {

  /** attribute name to store as request attribute for the Command object extract from the request input stream; default value = "command"; default value = COMMAND_ATTRIBUTE_NAME */
  public static final String COMMAND_ATTRIBUTE_NAME = "COMMAND_ATTRIBUTE_NAME";

  /** attribute name in ServletContext used to store an HashSet of correctly authenticated users (used in combination with SessionCheckInterceptor) */
  public static final String USERS_AUTHENTICATED = "USERS_AUTHENTICATED";

  /** interceptors cache */
  private HandlerInterceptor[] newinterceptors = null;

  /** receiver class used in combination with "ClientUtils.getData" method to comunicate with a remote client via HTTP; default value: "DefaultObjectReceiver" */
  private ObjectReceiver objectReceiver = new DefaultObjectReceiver();

  /** resolver */
  private OpenSwingViewResolver resolver = new OpenSwingViewResolver();


  /**
   * Set the receiver class used in combination with "ClientUtils.getData" method to comunicate with a remote client via HTTP.
   * @param objectReceiver receiver class to use
   */
  public final void setObjectReceiver(ObjectReceiver objectReceiver) {
    this.objectReceiver = objectReceiver;
    resolver.setObjectReceiver(objectReceiver);
  }


  /**
   * Initialize the specified interceptors, adapting them where necessary.
   * Clear interceptors cache.
   * @see #setInterceptors
   * @see #adaptInterceptor
   */
  protected final void initInterceptors() {
    super.initInterceptors();
    newinterceptors = null;
  }


  /**
   * Build a HandlerExecutionChain for the given handler, including applicable interceptors.
   * <p>The default implementation builds a standard HandlerExecutionChain with
   * the given handler and this handler mapping's common interceptors. Subclasses may
   * override this in order to extend/rearrange the list of interceptors.
   * The first interceptor is always the inner class "CommandInterceptor".
   * Finally fills in the interceptors cache.
   *
   * @param handler the resolved handler instance (never <code>null</code>)
   * @param request current HTTP request
   * @return the HandlerExecutionChain (never <code>null</code>)
   * @see #getAdaptedInterceptors()
   */
  protected final HandlerExecutionChain getHandlerExecutionChain(Object handler, HttpServletRequest request) {
    if (newinterceptors==null) {
      HandlerInterceptor[] interceptors = getAdaptedInterceptors();
      if (interceptors==null)
        interceptors = new HandlerInterceptor[0];
      newinterceptors = interceptors;
      if (interceptors.length==0 || !(interceptors[0] instanceof CommandInterceptor)) {
        newinterceptors = new HandlerInterceptor[interceptors.length+1];
        newinterceptors[0] = new CommandInterceptor();
        System.arraycopy(interceptors,0,newinterceptors,1,interceptors.length);
      }
    }
    return new HandlerExecutionChain(handler,newinterceptors);
  }


  /**
   * Look up a handler for the given request, returning <code>null</code> if no
   * specific one is found. This method is called by <code>getHandler<code>;
   * a <code>null</code> return value will lead to the default handler, if one is set.
   * @param request current HTTP request
   * @return the corresponding handler instance, or <code>null</code> if none found
   * @throws Exception if there is an internal error
   */
  protected final Object getHandlerInternal(HttpServletRequest request) throws Exception {
    // read the client request...
    Command command = (Command)request.getAttribute(COMMAND_ATTRIBUTE_NAME);
    if (command!=null)
      return command.getMethodName();
    else if (request.getMethod().equals("GET") && request.getParameter("docId")!=null) {
      request.setAttribute(COMMAND_ATTRIBUTE_NAME,new Command(request.getParameter("sessionId"),"getDoc",request.getParameter("docId")));
      return command.getMethodName();
    }

    command = objectReceiver.getObjectFromRequest(request);
    request.setAttribute(COMMAND_ATTRIBUTE_NAME,command);
    return command.getMethodName();
  }


  /**
   * <p>Title: OpenSwing Framework</p>
   * <p>Description: Inner class used to include the session identifier in the response.</p>
   * <p>Copyright: Copyright (C) 2006 Mauro Carniel</p>
   * @version 1.0
   */
  class CommandInterceptor extends HandlerInterceptorAdapter {


    /**
     * Set session identifier to the answer.
     */
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView model) throws Exception {
      Command command = (Command)request.getAttribute(COMMAND_ATTRIBUTE_NAME);

      Object answer = model.getModel().get(OpenSwingViewResolver.RESPONSE_PROPERTY_NAME);
      if (answer==null)
        answer = model.getModel().values().iterator().next();
      if (answer!=null && answer instanceof Response) {
        String sessionId = command.getSessionId();
        if (sessionId==null)
          sessionId = ""+System.currentTimeMillis()+Math.random();
        if (((Response)answer).getSessionId()==null)
          ((Response)answer).setSessionId(sessionId);
        model.setView(resolver);
      }

    }


  }


}
