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

  This file is part of OGo

  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 "WETableCell.h"

@interface WETableData : WETableCell
{
  // WODynamicElement: extraAttributes
  // WODynamicElement: otherTagString
@protected
  WOAssociation *title;   // title
  WOAssociation *align;   // align
  WOAssociation *valign;  // valign
  WOAssociation *isGroup; // show data content?
}

- (id)_initWithName:(NSString *)_name
  associations:(NSDictionary *)_config
  template:(WOElement *)_c;

@end /* WETableData */

@interface _WEComplexTableData : WETableData
{
  WOAssociation *string;       // string content
  WOAssociation *value;        // object content (can be formatted)
  WOAssociation *numberformat; // string
  WOAssociation *dateformat;   // string
  WOAssociation *formatter;    // WO4: NSFormatter object
  WOAssociation *action;       // 
}

@end

#include "WETableView.h"
#import "common.h"
#import <Foundation/NSNumberFormatter.h>
#import <Foundation/NSDateFormatter.h>

@implementation WETableData

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

- (id)_initWithName:(NSString *)_name
  associations:(NSDictionary *)_config
  template:(WOElement *)_c
{
  if ((self = [super initWithName:_name associations:_config template:_c])) {
    self->isGroup = WOExtGetProperty(_config, @"isGroup");
    self->title   = WOExtGetProperty(_config, @"title");
    self->align   = WOExtGetProperty(_config, @"align");
    self->valign  = WOExtGetProperty(_config, @"valign");
    if (self->valign == nil) {
      self->valign = [WOAssociation associationWithValue:@"TOP"];
      RETAIN(self->valign);
    }
  }
  return self;
}
- (id)initWithName:(NSString *)_name
  associations:(NSDictionary *)_config
  template:(WOElement *)_c
{
  /* cluster */
  if (([_config objectForKey:@"string"] != nil) ||
      ([_config objectForKey:@"value"] != nil)) {
    RELEASE(self);
    return [[_WEComplexTableData alloc] _initWithName:_name
                                        associations:_config
                                        template:_c];
  }
  else
    return [self _initWithName:_name associations:_config template:_c];
}

- (void)dealloc {
  RELEASE(self->title);
  RELEASE(self->isGroup);
  RELEASE(self->align);
  RELEASE(self->valign);
  [super dealloc];
}

- (void)_collectDataInContext:(WOContext *)_ctx {
  WOComponent      *cmp;
  NSMutableArray   *infos;
  WETableViewInfo  *info;
  NSString         *key;
  NSString         *sortedKey;

  cmp       = [_ctx component];
  infos     = [_ctx objectForKey:WETableView_INFOS];
  key       = [self->sortKey valueInComponent:cmp];
  sortedKey = [_ctx objectForKey:WETableView_SORTEDKEY];


  if (infos == nil) {
    infos = [NSMutableArray array];
    [_ctx setObject:infos forKey:WETableView_INFOS];
  }
  info = [[WETableViewInfo allocWithZone:[self zone]] init];
  info->rowSpan  = 1;
  info->isGroup  = [self->isGroup boolValueInComponent:cmp];
  info->isSorted = (key != nil && sortedKey != nil && [key isEqual:sortedKey]);

  [infos addObject:info];
  AUTORELEASE(info);
}

- (void)_appendHeader:(WOResponse *)_response inContext:(WOContext *)_ctx {
  WOComponent *cmp;
  NSString    *bg; // bgcolor
  NSString    *t;  // title
  NSString    *tC, *tF, *tS; // text font attrtibutes
  NSString    *a;  // align
  BOOL        hasFont;

  cmp = [_ctx component];
  
  tC  = [_ctx objectForKey:WETableView_fontColor];
  tF  = [_ctx objectForKey:WETableView_fontFace];
  tS  = [_ctx objectForKey:WETableView_fontSize];
  a   = [self->align stringValueInComponent:cmp];

  hasFont = (tC || tF || tS) ? YES : NO;

  bg = [_ctx objectForKey:WETableView_headerColor];
  t  = [self->title stringValueInComponent:cmp];

  WEAppendTD(_response, a, nil, bg);                     // <td...>
  [_response appendContentString:@"<nobr />"];
  
  [self appendSortIcon:_response inContext:_ctx];

  if (t) {
    if (hasFont)
      WEAppendFont(_response, tC, tF, tS);               //   <font...>
   
    [_response appendContentString:@" <b>"];
    [_response appendContentString:t];
    [_response appendContentString:@"</b>"];

    if (hasFont)
      [_response appendContentString:@"</font>"];        //   </font>
  }
  [_response appendContentString:@"</nobr>"];
  [_response appendContentString:@"</td>"];              // </td>
}

- (void)_appendStringContentToResponse:(WOResponse *)_response
  inContext:(WOContext *)_ctx
{
}

- (void)_appendData:(WOResponse *)_response
  inContext:(WOContext *)_ctx
  info:(WETableViewInfo *)_info
{
  if (!_info->isGroup) {
    WOComponent *cmp = [_ctx component];
    NSString    *bg  = [self->bgColor stringValueInComponent:cmp];
    NSString    *a   = [self->align   stringValueInComponent:cmp];
    NSString    *va  = [self->valign  stringValueInComponent:cmp];
    NSString    *tC, *tF, *tS; // text font attrtibutes
    BOOL        hasFont;
    
    tC  = [_ctx objectForKey:WETableView_fontColor];
    tF  = [_ctx objectForKey:WETableView_fontFace];
    tS  = [_ctx objectForKey:WETableView_fontSize];
    hasFont = (tC || tF || tS) ? YES : NO;
    
    if (bg == nil) {
      bg = (_info->isEven)
        ? [_ctx objectForKey:WETableView_evenColor]
        : [_ctx objectForKey:WETableView_oddColor];
    }
    
    [_response appendContentString:@"<td "];          // <td...>
    if (bg) {
      [_response appendContentString:@" bgcolor=\""];
      [_response appendContentString:bg];
      [_response appendContentCharacter:'"'];
    }
    if (_info->rowSpan > 1) {
      switch (_info->rowSpan) {
      case 0:
        [_response appendContentString:@" rowspan=\"0\""];
        break;
      case 1:
        [_response appendContentString:@" rowspan=\"1\""];
        break;
      case 2:
        [_response appendContentString:@" rowspan=\"2\""];
        break;
      default: {
        NSString *s;
        s = [[NSString alloc] initWithFormat:@"%i", _info->rowSpan];
        [_response appendContentString:@" rowspan=\""];
        [_response appendContentString:s];
        [_response appendContentCharacter:'"'];
        [s release];
        break; }
      }
    }
    if (a) {
      [_response appendContentString:@" align=\""];
      [_response appendContentString:a];
      [_response appendContentCharacter:'"'];
    }
    if (va) {
      [_response appendContentString:@" valign=\""];
      [_response appendContentString:va];
      [_response appendContentCharacter:'"'];
    }
    [self appendExtraAttributesToResponse:_response inContext:_ctx];
    [_response appendContentCharacter:'>'];
    
    if (hasFont)
      WEAppendFont(_response, tC, tF, tS);                      //   <font...>
    [self->template appendToResponse:_response inContext:_ctx];

    [self _appendStringContentToResponse:_response inContext:_ctx];
    if (hasFont)
      [_response appendContentString:@"</font>"];                 // </font>

    [_response appendContentString:@"</td>"];                  // </td>
  }
}

/* responder */

- (void)appendToResponse:(WOResponse *)_response inContext:(WOContext *)_ctx {
  if ([_ctx objectForKey:WETableView_CollectMode]) {
    [self _collectDataInContext:_ctx];
    return;
  }
  
  if ([_ctx objectForKey:WETableView_HeaderMode] && self->title) {
    [self _appendHeader:_response inContext:_ctx];
  }
  else if ([[_ctx objectForKey:WETableView_DataMode] boolValue]) {
    NSMutableArray *infos = nil;

    infos  = [_ctx objectForKey:WETableView_INFOS];

    if (infos != nil && [infos count] > 0) {
      [self _appendData:_response inContext:_ctx info:[infos objectAtIndex:0]];
      [infos removeObjectAtIndex:0];
    }
  }
}

- (void)takeValuesFromRequest:(WORequest *)_request
                    inContext:(WOContext *)_ctx
{
  if ([[_ctx objectForKey:WETableView_DataMode] boolValue] ||
      [[_ctx objectForKey:WETableView_HeaderMode] boolValue]) {
    [super takeValuesFromRequest:_request inContext:_ctx];
  }
}

@end /* WETableData */

@implementation _WEComplexTableData

- (id)_initWithName:(NSString *)_name
  associations:(NSDictionary *)_config
  template:(WOElement *)_c
{
  if ((self = [super _initWithName:_name associations:_config template:_c])) {
    self->string       = WOExtGetProperty(_config, @"string");
    self->value        = WOExtGetProperty(_config, @"value");
    self->formatter    = WOExtGetProperty(_config, @"formatter");
    self->numberformat = WOExtGetProperty(_config, @"numberformat");
    self->dateformat   = WOExtGetProperty(_config, @"dateformat");
    self->action       = WOExtGetProperty(_config, @"action");
    
    /* check formats */
    {
      int num = 0;
      if (self->formatter)    num++;
      if (self->numberformat) num++;
      if (self->dateformat)   num++;
      if (num > 1)
        NSLog(@"WARNING: more than one formats specified in element %@", self);
    }
  }
  return self;
}

- (void)dealloc {
  RELEASE(self->formatter);
  RELEASE(self->numberformat);
  RELEASE(self->dateformat);
  RELEASE(self->string);
  RELEASE(self->value);
  RELEASE(self->action);
  [super dealloc];
}

- (void)_appendStringContentToResponse:(WOResponse *)_response
  inContext:(WOContext *)_ctx
{
  WOComponent *cmp;
  NSString    *s;
  NSString    *tC, *tF, *tS; // text font attrtibutes
  BOOL        hasFont;
  
  cmp = [_ctx component];

  tC  = [_ctx objectForKey:WETableView_fontColor];
  tF  = [_ctx objectForKey:WETableView_fontFace];
  tS  = [_ctx objectForKey:WETableView_fontSize];
  hasFont = (tC || tF || tS) ? YES : NO;
  
  if (self->action != nil) {
    [_ctx appendElementIDComponent:@"act"];
    [_response appendContentString:@"<a href=\""];
    [_response appendContentString:[_ctx componentActionURL]];
    [_response appendContentString:@"\">"];
  }
  if (hasFont)
    WEAppendFont(_response, tC, tF, tS);                      //   <FONT...>
  
  /* add value */
  
  if (self->value) {
    NSFormatter *fmt = nil;
    id          obj;
    
    obj = [self->value valueInComponent:cmp];

    if (self->numberformat) {
      fmt = AUTORELEASE([[NSNumberFormatter alloc] init]);
      [(NSNumberFormatter *)fmt setFormat:
                            [self->numberformat valueInComponent:cmp]];
    }
    else if (self->dateformat) {
      fmt = [[NSDateFormatter alloc]
                              initWithDateFormat:
                              [self->dateformat valueInComponent:
                                   cmp]
                              allowNaturalLanguage:NO];
      fmt = AUTORELEASE(fmt);
    }
    else if (self->formatter) {
      fmt = [self->formatter valueInComponent:cmp];
#ifdef DEBUG
      if (fmt && ![fmt respondsToSelector:@selector(stringForObjectValue:)]) {
        [cmp logWithFormat:
                    @"invalid formatter determined by keypath %@: %@",
                    self->formatter,
                    fmt];
      }
#endif
    }
    
    if (fmt)
      obj = [fmt stringForObjectValue:obj];
    
    s = [obj stringValue];
    
    if (s) [_response appendContentHTMLString:s];
  }
  
  /* add string */
  
  if (self->string) {
    s = [self->string stringValueInComponent:cmp];
    [_response appendContentHTMLString:s];
  }
  if (hasFont)
    [_response appendContentString:@"</font>"];                 // </font>
  
  if (self->action) {
    [_ctx deleteLastElementIDComponent]; // delete 'act'
    [_response appendContentString:@"</a>"];
  }
}

- (id)invokeActionForRequest:(WORequest *)_request inContext:(WOContext *)_ctx
{
  NSString *eid;

  eid = [_ctx currentElementID];

  return ([eid isEqualToString:@"act"])
    ? [self->action valueInComponent:[_ctx component]]
    : [super invokeActionForRequest:_request inContext:_ctx];
}

@end /* _WEComplexTableData */
