/*
  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 "WODirectActionRequestHandler.h"
#include "WORequestHandler+private.h"
#include "WOContext+private.h"
#include <NGObjWeb/WOApplication.h>
#include <NGObjWeb/WOComponent.h>
#include <NGObjWeb/WODirectAction.h>
#include <NGObjWeb/WORequest.h>
#include <NGObjWeb/WOResponse.h>
#include <NGObjWeb/WOSession.h>
#include <NGObjWeb/WOSessionStore.h>
#include <NGObjWeb/WOStatisticsStore.h>
#include "common.h"

#if APPLE_RUNTIME || NeXT_RUNTIME
#  include <objc/objc-class.h>
#endif

//#define DEBUG_DIRECT_ACTION 1

static BOOL  perflog = NO;
static Class NSDateClass = Nil;

//#define USE_POOLS 1

@implementation WODirectActionRequestHandler

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

  NSDateClass = [NSDate class];
  perflog = [[NSUserDefaults standardUserDefaults]
                             boolForKey:@"WOProfileDirectActionRequestHandler"];
}

- (NSString *)loggingPrefix {
  return @"[da-handler]";
}

/*
  The request handler part of a direct action URI looks like this:

    [actionClass/]actionName[?key=value&key=value&...]
*/

- (BOOL)isComponentClass:(Class)_clazz {
  if (_clazz == Nil) 
    return NO;
  while ((_clazz = _clazz->super_class)) {
    if (_clazz == [WOComponent    class]) return YES;
    if (_clazz == [WODirectAction class]) return NO;
    if (_clazz == [NSObject       class]) return NO;
  }
  return NO;
}

- (id)instantiateObjectForActionClass:(Class)actionClass
  inContext:(WOContext *)context
  application:(WOApplication *)app
{
  WOComponent *component;
  
  if (actionClass == Nil)
    return nil;
  if (![self isComponentClass:actionClass]) {
    /* create direct action object */
    id actionObject;
    
    if (![actionClass instancesRespondToSelector:
			@selector(initWithContext:)]) {
      [self logWithFormat:@"tried to use class '%@' as a direct-action class",
	      NSStringFromClass(actionClass)];
      return nil;
    }
    
    actionObject =
      [(WODirectAction *)[actionClass alloc] initWithContext:context];
    actionObject = [actionObject autorelease];
    return actionObject;
  }
  
  /* special initialization for WOComponents used as direct actions */
    
  component = [app pageWithName:NSStringFromClass(actionClass)
		   inContext:context];
  [context setPage:(id)component];
  
  /* TODO: why was that commented out? */
  if ([component shouldTakeValuesFromRequest:[context request]
		 inContext:context])
    [app takeValuesFromRequest:[context request] inContext:context];
  
  return component;
}

- (WOResponse *)handleRequest:(WORequest *)_request
  inContext:(WOContext *)context
  session:(WOSession *)session
  application:(WOApplication *)app
{
#if USE_POOLS
  NSAutoreleasePool *pool2 = [[NSAutoreleasePool alloc] init];
#endif
  NSString      *actionClassName;
  NSString      *actionName;
  WOResponse    *response;
  NSArray       *handlerPath;
  Class         actionClass = Nil;
  WODirectAction      *actionObject = nil;
  id<WOActionResults> result = nil;
  
  *(&result) = nil;
  *(&response)        = nil;
  *(&actionClassName) = nil;
  *(&actionName)      = nil;
  *(&handlerPath)     = nil;
  
  /* process path */
  
  handlerPath = [_request requestHandlerPathArray];

#if DEBUG_DIRECT_ACTION
  [self debugWithFormat:@"path=%@ array=%@",
          [_request requestHandlerPath], handlerPath];
#endif

  switch ([handlerPath count]) {
    case 0:
      actionClassName = @"DirectAction";
      actionName      = @"default";
      break;
    case 1:
      actionClassName = @"DirectAction";
      actionName      = [handlerPath objectAtIndex:0];
      break;
    case 2:
      actionClassName = [handlerPath objectAtIndex:0];
      actionName      = [handlerPath objectAtIndex:1];
      break;

    default:
      actionClassName = [handlerPath objectAtIndex:0];
      actionName      = [handlerPath objectAtIndex:1];
#if 0
      NSLog(@"invalid direction action URL: %@",
              [_request requestHandlerPath]);
#endif
      break;
  }

  if ([actionName length] == 0)
    actionName = @"default";

  if ((*(&actionClass) = NSClassFromString(actionClassName)) == Nil) {
    [self logWithFormat:@"ERROR: did not find direct action class %@",
            actionClassName];
    actionClass = [WODirectAction class];
  }
  
#if DEBUG_DIRECT_ACTION
  [self debugWithFormat:
          @"[direct action request handler] class=%@ action=%@ ..",
          actionClassName, actionName];
#endif
  
  /* process request */
  
  actionObject = [self instantiateObjectForActionClass:actionClass
		       inContext:context
		       application:app];
  
  if (actionObject == nil) {
    [self logWithFormat:
            @"ERROR: could not create direct action object of class %@",
            actionClassName];
    actionObject = nil;
  }
  else {
    static Class WOComponentClass = Nil;
    
    if (WOComponentClass == Nil)
      WOComponentClass = [WOComponent class];

#if DEBUG_DIRECT_ACTION
    [self debugWithFormat:@"  use action-object: %@", actionObject];
#endif
    
    result = [(id)[actionObject performActionNamed:actionName] retain];
    
    if (result == nil) result = [[context page] retain];
    
    if ([(id)result isKindOfClass:WOComponentClass]) {
      [(id)result _awakeWithContext:context];
      [context setPage:(WOComponent *)result];
      
      response = [self generateResponseForComponent:(WOComponent *)result
                       inContext:context
                       application:app];
      
      if ([context hasSession]) {
        if ([context savePageRequired])
          [[context session] savePage:(WOComponent *)result];
      }
      
      response = [response retain];
    }
    else {
      /* generate response */
      response = [[result generateResponse] retain];
    }
              
    [context sleepComponents];
    
    [(id)result release]; result = nil;
    
    /* check whether a session was created */
    if ((session == nil) && [context hasSession]) {
      session = [[[context session] retain] autorelease];
      [session lock];
    }
    
#if USE_POOLS
    session = [session retain];
    [pool2 release]; pool2 = nil;
    session = [session autorelease];
#endif
    response = [response autorelease];
  }
  
  return response;
}

@end /* WODirectActionRequestHandler */
