/*
  Copyright (C) 2000-2005 SKYRIX Software AG

  This file is part of SOPE.

  SOPE 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.

  SOPE 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 SOPE; see the file COPYING.  If not, write to the
  Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
  02111-1307, USA.
*/

#include "WOComponentRequestHandler.h"
#include "WORequestHandler+private.h"
#include "WOContext+private.h"
#include <NGObjWeb/WOApplication.h>
#include <NGObjWeb/WORequest.h>
#include <NGObjWeb/WOResponse.h>
#include <NGObjWeb/WOSession.h>
#include <NGObjWeb/WOComponent.h>
#include "common.h"

@interface WOApplication(Privates)
- (WOSession *)_initializeSessionInContext:(WOContext *)_ctx;
- (void)_setCurrentContext:(WOContext *)_ctx;
@end

@implementation WOComponentRequestHandler

+ (int)version {
  return [super version] + 0 /* 2 */;
}
+ (void)initialize {
  NSAssert2([super version] == 2,
            @"invalid superclass (%@) version %i !",
            NSStringFromClass([self superclass]), [super version]);
}

- (WOResponse *)restoreSessionWithID:(NSString *)_sid
  inContext:(WOContext *)_ctx
{
  WOApplication *app = [WOApplication application];
  WOResponse *response = nil;
  
  if (_sid == nil) {
    // invalid session ID (or no session-ID ?!, which is no error ?) */
    response = [app handleSessionRestorationErrorInContext:_ctx];
  }
  else {
    WOSession *session = nil;
    
    if ((session = [app restoreSessionWithID:_sid inContext:_ctx])) {
      // awake restored session
      [_ctx setSession:session];
      [session _awakeWithContext:_ctx];
      
      [session awake];
      response = nil;
    }
    else {
      response = [app handleSessionRestorationErrorInContext:_ctx];
    }
  }
  return response;
}

/*
  The request handler path of a component URI looks like this:

    sessionID/componentName/contextID/elementID/instance/server
*/

- (WOResponse *)handleRequest:(WORequest *)_request {
  NSString      *sessionID        = nil;
  WOApplication *application      = nil;
  WOContext     *context          = nil;
  WOResponse    *response         = nil;
  WOSession     *session          = nil;
  WOComponent   *component        = nil;
  BOOL          isLocked          = NO;
  NSString      *handlerPath      = nil;

  if (_request == nil) return nil;

  application = [WOApplication application];
  handlerPath = [_request requestHandlerPath];

#if 0
  NSLog(@"[component request handler] path=%@ ..", handlerPath);
#endif

  if (![application allowsConcurrentRequestHandling]) {
    [application lockRequestHandling];
    isLocked = YES;
  }
  
  context = [WOContext contextWithRequest:_request];
  [application _setCurrentContext:context];
  
  /*
    parse handler path (URL)

    The format is:

      session/context.element-id
  */
  if ([handlerPath length] > 0) {
    NSArray *spath = [_request requestHandlerPathArray];

    if ([spath count] > 1)
      [context setRequestSenderID:[spath objectAtIndex:1]];
    if ([spath count] > 0)
      sessionID = [spath objectAtIndex:0];
  }
  
  if ([sessionID length] == 0)
    sessionID = [application sessionIDFromRequest:_request];
  
#if 0
  NSLog(@"%s: made context %@ (cid=%@, sn=%@) ..", __PRETTY_FUNCTION__
        context, [context contextID], sessionID);
#endif
  
  [application awake];
  
  /* restore or create session */
  if (sessionID) {
    response = [self restoreSessionWithID:sessionID inContext:context];
    session = response ? nil : [context session];
    
    if (session) {
      /* awake stored page */
      component = [session restorePageForContextID:[context currentElementID]];
      
      if (component == nil)
        response = [application handlePageRestorationErrorInContext:context];
#if DEBUG
      else {
        NSLog(@"%s: restored request component %@", __PRETTY_FUNCTION__,
              component);
      }
#endif
    }
    else if (response == nil) {
      [[WOApplication application]
                      logWithFormat:
                        @"WARNING: got no session restoration error, "
                        @"but missing session !"];
    }
  }
  else {
    /* create new session */
    session = [application _initializeSessionInContext:context];
    if (session) {
      /* awake created session */
      [session awake];
      component = [application pageWithName:nil inContext:context];
    }
    else
      response = [application handleSessionCreationErrorInContext:context];
  }
  
  if ((session != nil) && (component != nil) && (response == nil)) {
    WOComponent *newPage = nil;

    [[session retain] autorelease];
    
#if DEBUG
    NSAssert(application, @"missing application object ..");
    NSAssert(session,     @"missing session object ..");
#endif
    
    /* set request page in context */
    [context setPage:component];
    
    /* run take-values phase */
    [application takeValuesFromRequest:_request inContext:context];
    
    /* run invoke-action phase */
    newPage = [application invokeActionForRequest:_request inContext:context];

    /* process resulting page */
    if (newPage == nil) {
      if ((newPage = [context page]) == nil) {
        newPage = [application pageWithName:nil inContext:context];
        [context setPage:newPage];
      }
    }
    else if ([newPage isKindOfClass:[WOComponent class]])
      [context setPage:newPage];

#if DEBUG
    [self debugWithFormat:@"%s: new page: %@", __PRETTY_FUNCTION__, newPage];
#endif
    
    /* generate response */
#if 1 /* new code, ensure that _fixupResponse is called */
    response = [self generateResponseForComponent:[context page]
		     inContext:context
		     application:application];
#else /* old code */
    response = [context response];
    [application appendToResponse:response inContext:context];
#endif
  }
  else {
    NSLog(@"WARNING(%s): did not enter request/response transaction ...",
          __PRETTY_FUNCTION__);
  }

  /* tear down */

  /* sleep objects */
  [context sleepComponents];
  [session sleep];
  
  /* save objects */
  if (session) {
    if ([context savePageRequired])
      [session savePage:[context page]];
    
    NSLog(@"saving session %@", [session sessionID]);
    [application saveSessionForContext:context];
  }
  
  [application sleep];
  
  /* locking */
  
  if (isLocked) {
    [application unlockRequestHandling];
    isLocked = NO;
  }
  
  [application _setCurrentContext:nil];
  return response;
}

@end /* WOComponentRequestHandler */
