/*
  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 "WOValueAssociation.h"
#include "common.h"

// TODO: check whether it makes sense to precalculate int/bool/?? values
//       from the object value (add counters on how often these are called)

@implementation WOValueAssociation

static Class StrClass = Nil;

+ (void)initialize {
  StrClass = [NSString class];
}

+ (WOAssociation *)associationWithValue:(id)_value {
  return [[[WOValueAssociation alloc] initWithValue:_value] autorelease];
}

- (id)init {
  return [self initWithValue:nil];
}
- (id)initWithValue:(id)_value {
  if ((self = [super init])) {
    self->value = [_value retain]; /* do not use copy, can be full objects */
  }
  return self;
}
- (id)initWithString:(NSString *)_s {
  return [self initWithValue:_s];
}

- (void)dealloc {
  [self->value release];
  [super dealloc];
}

/* value */

- (void)setValue:(id)_value inComponent:(WOComponent *)_component {
  // not settable
  [NSException raise:@"AssociationException"
               format:@"association value is not settable !"];
}
- (id)valueInComponent:(WOComponent *)_component {
  return self->value;
}

- (BOOL)isValueConstant {
  return YES;
}
- (BOOL)isValueSettable {
  return NO;
}

/* special values */

- (unsigned int)unsignedIntValueInComponent:(WOComponent *)_component {
  register unsigned int val;
  
  if (self->cacheFlags.hasSmallValue)
    return self->cacheFlags.smallValue;
  
  val = [self->value unsignedIntValue];
  if (self->cacheFlags.hasNoSmallValue)
    return val;
  
  if (val < 65536) {
    self->cacheFlags.smallValue = val;
    self->cacheFlags.hasSmallValue = 1;
  }
  else
    self->cacheFlags.hasNoSmallValue = 1;
  return val;
}
- (int)intValueInComponent:(WOComponent *)_component {
  register int val;
  
  if (self->cacheFlags.hasSmallValue)
    return self->cacheFlags.smallValue;
  
  val = [self->value intValue];
  if (self->cacheFlags.hasNoSmallValue)
    return val;
  
  if (val >= 0 && val < 65536) {
    self->cacheFlags.smallValue = val;
    self->cacheFlags.hasSmallValue = 1;
  }
  else
    self->cacheFlags.hasNoSmallValue = 1;
  return val;
}
- (BOOL)boolValueInComponent:(WOComponent *)_component {
  switch (self->cacheFlags.boolValue) {
  case 1: return YES;
  case 2: return NO;
  default:
    if (self->value == nil)
      self->cacheFlags.boolValue = 2; /* false */
    else
      self->cacheFlags.boolValue = [self->value boolValue] ? 1 : 2;
    return self->cacheFlags.boolValue == 1 ? YES : NO;
  }
}
- (NSString *)stringValueInComponent:(WOComponent *)_component {
  return [self->value stringValue];
}

/* NSCoding */

- (void)encodeWithCoder:(NSCoder *)_coder {
  [_coder encodeObject:self->value];
}
- (id)initWithCoder:(NSCoder *)_coder {
  if ((self = [super init])) {
    self->value = [[_coder decodeObject] retain];
  }
  return self;
}

/* NSCopying */

- (id)copyWithZone:(NSZone *)_zone {
  return [self retain];
}

/* description */

- (NSString *)description {
  NSMutableString *str;

  str = [NSMutableString stringWithCapacity:64];
  [str appendFormat:@"<0x%p[%@]:", self, NSStringFromClass([self class])];
  [str appendString:@" value="];
  if ([self->value isKindOfClass:StrClass]) {
    NSString *v = self->value;
    
    [str appendString:@"\""];
    if ([self->value length] > 10) {
      v = [v substringToIndex:9];
      v = [v stringByApplyingCEscaping];
      [str appendString:v];
      [str appendFormat:@"...[len=%i]", (int)[self->value length]];
    }
    else {
      v = [v stringByApplyingCEscaping];
      [str appendString:v];
    }
    [str appendString:@"\""];
  }
  else {
    [str appendString:[self->value description]];
    [str appendFormat:@"(%@)", NSStringFromClass([self->value class])];
  }
  [str appendString:@">"];

  return str;
}

@end /* WOValueAssociation */

@implementation _WOBoolValueAssociation

static _WOBoolValueAssociation *yesAssoc = nil;
static _WOBoolValueAssociation *noAssoc  = nil;
static NSNumber *yesNum = nil;
static NSNumber *noNum  = nil;

+ (void)initialize {
  if (yesNum == nil) yesNum = [[NSNumber numberWithBool:YES] retain];
  if (noNum  == nil) noNum  = [[NSNumber numberWithBool:NO] retain];
}

+ (WOAssociation *)associationWithBool:(BOOL)_value {
  if (_value) {
    if (yesAssoc == nil) {
      yesAssoc = [[_WOBoolValueAssociation alloc] init];
      yesAssoc->value = YES;
    }
    return yesAssoc;
  }
  else {
    if (noAssoc == nil) {
      noAssoc = [[_WOBoolValueAssociation alloc] init];
      noAssoc->value = NO;
    }
    return noAssoc;
  }
}
+ (WOAssociation *)associationWithValue:(id)_value {
  return [self associationWithBool:[_value boolValue]];
}

/* value */

- (void)setValue:(id)_value inComponent:(WOComponent *)_component {
  // not settable
  [NSException raise:@"AssociationException"
               format:@"association value is not settable !"];
}
- (id)valueInComponent:(WOComponent *)_component {
  return self->value ? yesNum : noNum;
}

- (BOOL)isValueConstant {
  return YES;
}
- (BOOL)isValueSettable {
  return NO;
}

/* special values */

- (unsigned int)unsignedIntValueInComponent:(WOComponent *)_component {
  return self->value ? 1 : 0;
}
- (int)intValueInComponent:(WOComponent *)_component {
  return self->value ? 1 : 0;
}
- (BOOL)boolValueInComponent:(WOComponent *)_component {
  return self->value;
}
- (NSString *)stringValueInComponent:(WOComponent *)_component {
  return self->value ? @"YES" : @"NO";
}

/* NSCopying */

- (id)copyWithZone:(NSZone *)_zone {
  return [self retain];
}

/* description */

- (NSString *)description {
  return [NSString stringWithFormat:@"<%@[0x%p]: %s",
                     NSStringFromClass([self class]), self,
                     self->value ? "YES" : "NO"];
}

@end /* _WOBoolValueAssociation */
