/*
  Copyright (C) 2006 Helge Hess

  This file is part of JOPE.

  JOPE is free software; you can redistribute it and/or modify it under
  the terms of the GNU Lesser General Public License as published by the
  Free Software Foundation; either version 2, or (at your option) any
  later version.

  JOPE 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 Lesser General Public
  License for more details.

  You should have received a copy of the GNU Lesser General Public
  License along with JOPE; see the file COPYING.  If not, write to the
  Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
  02111-1307, USA.
*/

package org.opengroupware.jope.appserver.core;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.opengroupware.jope.appserver.publisher.JoCallable;
import org.opengroupware.jope.appserver.publisher.JoContext;
import org.opengroupware.jope.appserver.publisher.JoObject;

public abstract class WORequestHandler implements JoObject, JoCallable {
  protected static final Log log = LogFactory.getLog("WOApplication");
  
  WOApplication application;
  
  public WORequestHandler(WOApplication _app) {
    this.application = _app;
  }
  
  /* session handling */
  
  public String sessionIDFromRequest(WORequest _rq) {
    // TODO: also check cookie
    return _rq.stringFormValueForKey(WORequest.SessionIDKey);
  }
  
  public boolean restoreSessionsUsingIDs() {
    return true;
  }
  
  public boolean autocreateSession(WOContext _ctx) {
    // TODO: implement
    return false;
  }
  
  /* request handling */
  
  public abstract WOResponse handleRequest
    (WORequest _rq, WOContext _ctx, WOSession _s);
  
  public boolean doesRejectFavicon() {
    return true;
  }

  public WOResponse handleRequest(WORequest _rq) {
    // TODO: this is deprecated, we want to do everything with JoObject
    //       lookups
    WOContext  ctx;
    WOResponse r = null;
    WOSession  session = null;
    String     sessionId;
    boolean    debugOn = log.isDebugEnabled();

    if (debugOn) log.debug("handleRequest: " + _rq);
    
    if (this.doesRejectFavicon()) {
      if ("/favicon.ico".equals(_rq.uri()))
        return null;
    }
    
    ctx = this.application.createContextForRequest(_rq);
    if (debugOn) log.debug("  created context: " + ctx);
    
    /* prepare session */
    
    if ((sessionId = this.sessionIDFromRequest(_rq)) != null) {
      if (sessionId.length() == 0)
        sessionId = null;
      else if (sessionId.equals("nil"))
        sessionId = null;
    }
    if (debugOn) log.debug("  session-id: " + sessionId);
    
    try {
      this.application.awake();
      
      /* restore session */
      
      if (this.restoreSessionsUsingIDs()) {
        if (sessionId != null) {
          if (debugOn) log.debug("  restore session: " + sessionId);
        
          session = this.application.restoreSessionWithID(sessionId, ctx);
          if (session == null) {
            r = this.application.handleSessionRestorationError(ctx);
            sessionId = null;
          }
        }
      
        if (r == null /* no error */ && session == null) {
          /* no error, but session is not here */
          if (this.autocreateSession(ctx)) {
            if (debugOn) log.debug("  autocreate session: " + sessionId);
            
            if (!this.application.refusesNewSessions()) {
              session = this.application.initializeSession(ctx);
              if (session == null)
                r = this.application.handleSessionRestorationError(ctx);
            }
            else {
              // TODO: this already failed once? will it return null again?
              r = this.application.handleSessionRestorationError(ctx);
            }
          }
        }
        
        if (debugOn) {
          if (session != null)
            log.debug("  restored session: " + session);
          else if (sessionId != null)
            log.debug("  did not restore session with id: " + sessionId);
        }
      }
    
      /* run handler specific processing */
      
      if (r == null) {
    
        r = this.handleRequest(_rq, ctx, session);
      }
      
      /* save session */
      
      // TODO: store session cookies
      
      if (ctx.hasSession()) {
        // TODO: ensure that session gets a sleep?
        this.application.saveSessionForContext(ctx);
      }
      else
        log.debug("no session to store ...");
    }
    catch (Exception e) {
      // TODO: call some handler method
      // TODO: ensure that session gets a sleep?
      if (debugOn) log.debug("  handler catched exception", e);
      r = this.application.handleException(e, ctx);
    }
    
    /* deal with missing responses */
    
    if (r == null) {
      log.warn("request handler produced no result.");
      r = ctx.response();
    }
    if (r == null)
      r = new WOResponse(_rq);
    
    /* tear down context */
    // TODO: send sleep() or something to tear it down?
    ctx = null;
    
    return r;
  }

  /* JoObject */

  public Object lookupName(String _name, JoContext _ctx, boolean _aquire) {
    /* we support no subobjects, all remaining handling is done by us */
    return null;
  }
  
  public Object callInContext(Object _object, JoContext _ctx) {
    /* _object will be the WOApplication */
    if (_ctx == null)
      return null;
    
    WOContext wctx = (WOContext)_ctx;
    return this.handleRequest
      (wctx.request(), wctx, wctx.hasSession() ? wctx.session() : null);
  }

  public boolean isCallableInContext(JoContext _ctx) {
    /* we are callable in WOContext's */
    return _ctx != null && _ctx instanceof WOContext;
  }
  
}
