/*
  Copyright (C) 2002-2004 SKYRIX Software AG

  This file is part of OpenGroupware.org.

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

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

#include "SoObjectMethodDispatcher.h"
#include "SoObject.h"
#include "SoClass.h"
#include "SoObjectRequestHandler.h"
#include "WOContext+SoObjects.h"
#include <NGObjWeb/WORequest.h>
#include <NGObjWeb/WOResponse.h>
#include <NGObjWeb/WOContext.h>
#include <NGObjWeb/WOElement.h>
#include "common.h"

@implementation SoObjectMethodDispatcher

static BOOL debugOn = NO;

+ (void)initialize {
  NSUserDefaults *ud = [NSUserDefaults standardUserDefaults];
  static BOOL didInit = NO;
  if (didInit) return;
  didInit = YES;

  debugOn = [ud boolForKey:@"SoObjectMethodDispatcherDebugEnabled"];
}

- (id)initWithObject:(id)_object {
  if ((self = [super init])) {
    self->object = [_object retain];
  }
  return self;
}
- (void)dealloc {
  [self->object release];
  [super dealloc];
}

/* perform dispatch */

- (id)dispatchInContext:(WOContext *)_ctx {
  NSAutoreleasePool *pool;
  WORequest *rq;
  id clientObject;
  id methodObject;
  id resultObject;
  
  pool = [[NSAutoreleasePool alloc] init];
  rq   = [_ctx request];
  
  /* find client object */
  
  if ((clientObject = [_ctx clientObject])) {
    if (debugOn)
      [self debugWithFormat:@"client object set in ctx: %@", clientObject];
  }
  else if ((clientObject = [self->object clientObject])) {
    if (debugOn)
      [self debugWithFormat:@"setting client object: %@", clientObject];
    [_ctx setClientObject:clientObject];
  }
  
  // TODO: should check XML-RPC !!! 
  //       (hm, why ? XML-RPC is handled by other dispatcher ?)
  
  /* find callable (method) object */
  
  if ([self->object isCallable]) {
    if (debugOn)
      [self debugWithFormat:@"traversed object is callable: %@", self->object];
    methodObject = self->object;
  }
  else if ([[self->object soClass] hasKey:[rq method] inContext:_ctx]) {
    // TODO: I'm not sure whether this step is correct
    /* the class has a GET method */
    methodObject = [self->object lookupName:[rq method] 
				 inContext:_ctx
				 acquire:NO];
  }
  else {
    // TODO: should we replace the methodObject with a redirect to the
    //       default method name? This would ensure proper URLs
    methodObject = [self->object lookupDefaultMethod];
    if (debugOn)
      [self debugWithFormat:@"using default method: %@", methodObject];
  }
  
  /* apply arguments */
    
  if ([methodObject respondsToSelector:
  		      @selector(takeValuesFromRequest:inContext:)]) {
    if (debugOn)
      [self debugWithFormat:@"applying values from request ..."];
    [methodObject takeValuesFromRequest:rq inContext:_ctx];
  }

  /* perform call */
    
  if (methodObject == nil || ![methodObject isCallable]) {
    /*
      The object is neither callable nor does it have a default method,
      so we just pass it through.
    */
    resultObject = self->object;
    if (debugOn) {
      [self debugWithFormat:@"got no method, using object as result: %@", 
	      resultObject];
    }
  }
  else {
    resultObject = [methodObject callOnObject:[_ctx clientObject]
				 inContext:_ctx];
    if (debugOn) {
      if ([resultObject isKindOfClass:[WOResponse class]]) {
	[self debugWithFormat:@"call produced response: 0x%08X (code=%i)", 
	        resultObject, [(WOResponse *)resultObject status]];
      }
      else
	[self debugWithFormat:@"call produced result: %@", resultObject];
    }
  }
  
  resultObject = [resultObject retain];
  [pool release];
  
  /* deliver result */
  return [resultObject autorelease];
}

/* logging */

- (NSString *)loggingPrefix {
  return @"[obj-mth-dispatch]";
}
- (BOOL)isDebuggingEnabled {
  return debugOn ? YES : NO;
}

/* description */

- (NSString *)description {
  NSMutableString *ms;
  
  ms = [NSMutableString stringWithCapacity:64];
  [ms appendFormat:@"<0x%08X[%@]:", self,
        NSStringFromClass((Class)*(void**)self)];
  
  if (self->object)
    [ms appendFormat:@" object=%@", self->object];
  else
    [ms appendString:@" <no object>"];
  
  [ms appendString:@">"];
  return ms;
}

@end /* SoObjectMethodDispatcher */
