/*
  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 "WOForm.h"
#include "WOElement+private.h"
#include "WOInput.h"
#include "WOContext+private.h"
#include <NGObjWeb/WOApplication.h>
#include "common.h"

@implementation WOForm

static int debugTakeValues = -1;

+ (int)version {
  return 4;
}

- (id)initWithName:(NSString *)_name
  associations:(NSDictionary *)_config
  template:(WOElement *)_c
{
  if (debugTakeValues == -1) {
    debugTakeValues = 
      [[NSUserDefaults standardUserDefaults] boolForKey:@"WODebugTakeValues"]
      ? 1 : 0;
    if (debugTakeValues) NSLog(@"WOForm: WODebugTakeValues on.");
  }
  
  if ((self = [super initWithName:_name associations:_config template:_c])) {
    WOAssociation *sidInUrlAssoc;

    self->containsForm = YES;
    
    sidInUrlAssoc          = OWGetProperty(_config, @"?wosid");
    self->action           = OWGetProperty(_config, @"action");
    self->href             = OWGetProperty(_config, @"href");
    self->pageName         = OWGetProperty(_config, @"pageName");
    self->queryDictionary  = OWGetProperty(_config, @"queryDictionary");
    self->queryParameters  = OWExtractQueryParameters(_config);
    self->actionClass      = OWGetProperty(_config, @"actionClass");
    self->directActionName = OWGetProperty(_config, @"directActionName");
    self->method           = OWGetProperty(_config, @"method");
    
    self->sidInUrl = (sidInUrlAssoc != nil)
      ? [sidInUrlAssoc boolValueInComponent:nil]
      : YES;
    
    self->template = [_c retain];
  }
  return self;
}

- (void)dealloc {
  [self->method           release];
  [self->template         release];
  [self->actionClass      release];
  [self->directActionName release];
  [self->queryDictionary  release];
  [self->queryParameters  release];
  [self->action           release];
  [self->pageName         release];
  [self->href             release];
  [super dealloc];
}

/* handle active form elements */

- (WOElement *)template {
  return self->template;
}

// ******************** responder ********************

- (void)takeValuesFromRequest:(WORequest *)_request
  inContext:(WOContext *)_ctx
{
  static int alwaysPassIn = -1;

  if (alwaysPassIn == -1) {
    alwaysPassIn = [[[NSUserDefaults standardUserDefaults]
                                     objectForKey:@"WOFormAlwaysPassDown"]
                                     boolValue] ? 1 : 0;
  }
  
  if ([_ctx isInForm]) {
    NSLog(@"ERROR(%s): another form is already active in context !",
          __PRETTY_FUNCTION__);
  }
  
  [_ctx setInForm:YES];
  {
    WOComponent *sComponent = [_ctx component];
    BOOL doTakeValues = NO;
    
    if (self->queryParameters) {
      /* apply values to ?style parameters */
      NSEnumerator *keys;
      NSString     *key;

      keys = [self->queryParameters keyEnumerator];
      while ((key = [keys nextObject])) {
        WOAssociation *assoc;
        id value;
        
        assoc = [self->queryParameters objectForKey:key];
        value = [_request formValueForKey:key];

        [assoc setValue:value inComponent:sComponent];
      }
    }
    
    if ([[self->href stringValueInComponent:sComponent] 
	  isEqualToString:[_request uri]]) {
      if (debugTakeValues) {
	NSArray *formValues = [_request formValueKeys];
	NSLog(@"%s: we are uri active (uri=%@): %@ ..", __PRETTY_FUNCTION__,
	      [_request uri], formValues);
      }
      doTakeValues = YES;
    }
    else if ([[_ctx elementID] isEqualToString:[_ctx senderID]]) {
      if (debugTakeValues) {
	NSArray *formValues = [_request formValueKeys];
	NSLog(@"%s: we are elem active (eid=%@): %@ ..", __PRETTY_FUNCTION__,
	      [_ctx elementID], formValues);
      }
      doTakeValues = YES;
    }
    else if (alwaysPassIn) {
      if (debugTakeValues)
	NSLog(@"%s: taking values from foreign request ",__PRETTY_FUNCTION__);
      doTakeValues = YES;
    }
    else {
      /* finally, let the component decide */
      doTakeValues = [sComponent shouldTakeValuesFromRequest:_request 
				 inContext:_ctx];
      if (debugTakeValues) {
	NSLog(@"%s: component should take values: %s ", __PRETTY_FUNCTION__,
	      doTakeValues ? "yes" : "no");
      }
    }
    
    if (doTakeValues) {
      if (debugTakeValues) 
	NSLog(@"%s: taking values ...", __PRETTY_FUNCTION__);
      
      [self->template takeValuesFromRequest:_request inContext:_ctx];

      if (debugTakeValues) 
	NSLog(@"%s: did take values.", __PRETTY_FUNCTION__);
    }
    else if (debugTakeValues) {
      [sComponent
             debugWithFormat:
               @"WOForm: *not* taking values from foreign request "
               @"(id='%@' vs sid='%@') ...",
               [_ctx elementID], [_ctx senderID]];
    }
  }
  
  if (![_ctx isInForm]) {
    [[_ctx component]
           logWithFormat:@"ERROR(%s:%d): -isInForm is NO !!!",
             __PRETTY_FUNCTION__, __LINE__];
  }
  else
    [_ctx setInForm:NO];
}

- (id)invokeActionForRequest:(WORequest *)_request
  inContext:(WOContext *)_ctx
{
  id result = nil;
  
  [_ctx setInForm:YES];

  if ([_ctx currentElementID] == nil) {
    WOElement *element;
    
    if ((element = [_ctx activeFormElement])) {
#if 1
      result = [self->template invokeActionForRequest:_request inContext:_ctx];
      RETAIN(result);
#else
      /* wrong - need to setup correct component stack */
      result = [[element invokeActionForRequest:_request
                         inContext:_ctx]
                         retain];
#endif
    }
    else if (self->action) {
      result = [self executeAction:self->action inContext:_ctx];
    }
    else if (self->pageName) {
      NSString    *pname = nil;
      WOComponent *page = nil;

      pname = [self->pageName stringValueInComponent:[_ctx component]];
      page  = [[_ctx application] pageWithName:pname inContext:_ctx];

      if (page == nil) {
        [[_ctx session] logWithFormat:
                          @"%@[0x%08X]: did not find page with name %@ !",
                          NSStringFromClass([self class]), self, pname];
      }
      NSLog(@"showing page %@", page);
      result = page;
    }
  }
  else
    result = [self->template invokeActionForRequest:_request inContext:_ctx];

  [_ctx setInForm:NO];

  return result;
}

- (NSString *)_addHrefToResponse:(WOResponse *)_r inContext:(WOContext *)_ctx {
  /* post to a fixed hyperlink */
  WOComponent *sComponent = [_ctx component];
  NSString     *s;
  NSDictionary *d;

  s = [self->href stringValueInComponent:sComponent];
  d = [self->queryDictionary valueInComponent:sComponent];
  
  WOResponse_AddString(_r, s);
  
  return [self queryStringForQueryDictionary:d
	       andQueryParameters:self->queryParameters
	       inContext:_ctx];
}

- (NSString *)_addActionToResponse:(WOResponse *)_r inContext:(WOContext *)_c {
  /* post to a component action */
  NSDictionary *d;
        
  WOResponse_AddString(_r, [_c componentActionURL]);

  d = [self->queryDictionary valueInComponent:[_c component]];
  return [self queryStringForQueryDictionary:d
	       andQueryParameters:self->queryParameters
	       inContext:_c];
}

- (void)_addDirectActionToResponse:(WOResponse *)_r inContext:(WOContext *)_c {
  /* a direct action link */
  WOComponent *sComponent;
  NSString            *daClass = nil;
  NSString            *daName  = nil;
  NSMutableDictionary *qd;
  NSDictionary        *tmp;
  NSString            *uri;
          
  sComponent = [_c component];
  daClass = [self->actionClass stringValueInComponent:sComponent];
  daName  = [self->directActionName stringValueInComponent:sComponent];
  
  if (daClass) {
    if (daName) {
      if (![daClass isEqualToString:@"DirectAction"])
	daName = [NSString stringWithFormat:@"%@/%@", daClass, daName];
    }
    else
      daName = daClass;
  }
  
  qd = [NSMutableDictionary dictionaryWithCapacity:16];
  
  /* add query dictionary */
        
  if (self->queryDictionary) {
    if ((tmp = [self->queryDictionary valueInComponent:sComponent]))
      [qd addEntriesFromDictionary:tmp];
  }
        
  /* add ?style parameters */
  
  if (self->queryParameters) {
    NSEnumerator *keys;
    NSString     *key;
  
    keys = [self->queryParameters keyEnumerator];
    while ((key = [keys nextObject])) {
      id assoc, value;
  
      assoc = [self->queryParameters objectForKey:key];
      value = [assoc stringValueInComponent:sComponent];
            
      [qd setObject:(value ? value : @"") forKey:key];
    }
  }
        
  /* add session ID */
  if (self->sidInUrl && [_c hasSession]) {
    WOSession *sn;

    sn = [_c session];
    [qd setObject:[sn sessionID] forKey:WORequestValueSessionID];
            
    if (![sn isDistributionEnabled]) {
      [qd setObject:[[WOApplication application] number]
	  forKey:WORequestValueInstance];
    }
  }
  else if (self->sidInUrl) {
    /* Note: this is not a problem! Eg this occurs on the OGo Main component */
    [self debugWithFormat:
	    @"Note: session-id is requested, but no session is active?"];
  }
  
  uri = [_c directActionURLForActionNamed:daName queryDictionary:qd];
  WOResponse_AddString(_r, uri);
}

- (void)appendToResponse:(WOResponse *)_response inContext:(WOContext *)_ctx {
  WOComponent *sComponent;
  NSString    *queryString = nil;
  
  if ([[_ctx request] isFromClientComponent]) {
    [self->template appendToResponse:_response inContext:_ctx];
    return;
  }
  
  [_ctx setInForm:YES];
  sComponent  = [_ctx component];

  WOResponse_AddCString(_response, "<form action=\"");
      
  if (self->href) 
    queryString = [self _addHrefToResponse:_response inContext:_ctx];
  else if (self->directActionName != nil || self->actionClass != nil)
    [self _addDirectActionToResponse:_response inContext:_ctx];
  else
    queryString = [self _addActionToResponse:_response inContext:_ctx];
  
  if (queryString) {
    [_response appendContentCharacter:'?'];
    WOResponse_AddString(_response, queryString);
  }
  if (self->method) {
    WOResponse_AddCString(_response, "\" method=\"");
    WOResponse_AddString(_response, 
			 [self->method stringValueInComponent:sComponent]);
    WOResponse_AddCString(_response, "\"");
  }
  else
    WOResponse_AddCString(_response, "\" method=\"post\"");
      
  [self appendExtraAttributesToResponse:_response inContext:_ctx];
  
  if (self->otherTagString) {
    WOResponse_AddChar(_response, ' ');
    WOResponse_AddString(_response,
                         [self->otherTagString stringValueInComponent:
                           sComponent]);
  }
  WOResponse_AddChar(_response, '>');
    
  [self->template appendToResponse:_response inContext:_ctx];
  
  WOResponse_AddCString(_response, "</form>");
  [_ctx setInForm:NO];
}

/* description */

- (NSString *)associationDescription {
  NSMutableString *str = [NSMutableString stringWithCapacity:64];
  //[str appendString:[super associationDescription]];

  if (self->action)   [str appendFormat:@" action=%@", self->action];
  if (self->href)     [str appendFormat:@" href=%@", self->href];
  if (self->pageName) [str appendFormat:@" page=%@", self->pageName];

  if (self->actionClass)
    [str appendFormat:@" actionClass=%@", self->actionClass];
  if (self->directActionName)
    [str appendFormat:@" directAction=%@", self->directActionName];
  if (self->template)
    [str appendFormat:@" template=%@", self->template];
  
  return str;
}

@end /* WOForm */
