/*
  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 <NGObjWeb/WODisplayGroup.h>
#import <EOControl/EOControl.h>
#import <Foundation/Foundation.h>
#import <Foundation/NSNotification.h>
#include "common.h"

@interface EODataSource(DGQualifierSetting)
- (void)setAuxiliaryQualifier:(EOQualifier *)_q;
- (void)setQualifier:(EOQualifier *)_q;
- (void)setQualifierBindings:(NSDictionary *)_bindings;
@end

#if APPLE_FOUNDATION_LIBRARY || NeXT_Foundation_LIBRARY
@interface NSObject(Miss)
- (void)notImplemented:(SEL)cmd;
@end
#endif

@implementation WODisplayGroup

static NSNumber *uint0 = nil;
static NSArray  *uint0Array = nil;

+ (void)initialize {
  if (uint0 == nil)
    uint0 = [[NSNumber alloc] initWithUnsignedInt:0];
  if (uint0Array == nil)
    uint0Array = [[NSArray alloc] initWithObjects:&uint0 count:1];
}

- (id)init {
  if ((self = [super init])) {
    [self setDefaultStringMatchFormat:
            [[self class] globalDefaultStringMatchFormat]];
    [self setDefaultStringMatchOperator:
            [[self class] globalDefaultStringMatchOperator]];
    self->currentBatchIndex = 1;
  }
  return self;
}

- (void)dealloc {
  [[NSNotificationCenter defaultCenter] removeObserver:self];
  [self setDataSource:nil];

  RELEASE(self->_queryMatch);
  RELEASE(self->_queryMin);
  RELEASE(self->_queryMax);
  RELEASE(self->_queryOperator);
  RELEASE(self->_queryBindings);
  RELEASE(self->defaultStringMatchFormat);
  RELEASE(self->defaultStringMatchOperator);
  RELEASE(self->qualifier);
  RELEASE(self->objects);
  RELEASE(self->displayObjects);
  RELEASE(self->selectionIndexes);
  RELEASE(self->sortOrderings);
  RELEASE(self->insertedObjectDefaults);
  [super dealloc];
}

/* notifications */

- (void)_objectsChangedInEC:(NSNotification *)_notification {
  id d;
  BOOL doRedisplay;

  doRedisplay = YES;
  if ((d = [self delegate])) {
    if ([d respondsToSelector:
           @selector(displayGroup:shouldRedisplayForChangesInEditingContext:)]) {
      doRedisplay = [d displayGroup:self
                       shouldRedisplayForEditingContextChangeNotification:
                         _notification];
    }
  }

  if (doRedisplay)
    [self redisplay];
}

/* display */

- (void)redisplay {
  /* contents changed notification ??? */
}

/* accessors */

- (void)setDelegate:(id)_delegate {
  self->delegate = _delegate;
}
- (id)delegate {
  return self->delegate;
}

- (void)setDataSource:(EODataSource *)_ds {
  if (_ds != self->dataSource) {
#if WITH_EC
    NSNotificationCenter *nc = [NSNotificationCenter defaultCenter];

    id ec;

    if ((ec = [self->dataSource editingContext])) {
      [ec removeEditor:self];
      if ([ec messageHandler] == self)
        [ec setMessageHandler:nil];
    
      [nc removeObserver:self
          name:@"EOObjectsChangedInEditingContext"
          object:ec];
    }
#endif
    
    ASSIGN(self->dataSource, _ds);

#if WITH_EC
    if ((ec = [_ds editingContext])) {
      [ec addEditor:self];
      if ([ec messageHandler] == nil)
        [ec setMessageHandler:self];
      
      [nc addObserver:self
          selector:@selector(_objectsChangedInEC:)
          name:@"EOObjectsChangedInEditingContext"
          object:ec];
    }
#endif
    
    if ([self->delegate respondsToSelector:
               @selector(displayGroupDidChangeDataSource:)])
      [self->delegate displayGroupDidChangeDataSource:self];
  }
}
- (EODataSource *)dataSource {
  return self->dataSource;
}

- (void)setSortOrderings:(NSArray *)_orderings {
  id tmp = self->sortOrderings;
  self->sortOrderings = [_orderings copy];
  RELEASE(tmp);
}
- (NSArray *)sortOrderings {
  return self->sortOrderings;
}

- (void)setFetchesOnLoad:(BOOL)_flag {
  self->flags.fetchesOnLoad = _flag ? 1 : 0;
}
- (BOOL)fetchesOnLoad {
  return self->flags.fetchesOnLoad ? YES : NO;
}

- (void)setInsertedObjectDefaultValues:(NSDictionary *)_values {
  id tmp = self->insertedObjectDefaults;
  self->insertedObjectDefaults = [_values copy];
  RELEASE(tmp);
}
- (NSDictionary *)insertedObjectDefaultValues {
  return self->insertedObjectDefaults;
}

- (void)setNumberOfObjectsPerBatch:(unsigned)_count {
  self->numberOfObjectsPerBatch = _count;
}
- (unsigned)numberOfObjectsPerBatch {
  return self->numberOfObjectsPerBatch;
}

- (void)setSelectsFirstObjectAfterFetch:(BOOL)_flag {
  self->flags.selectFirstAfterFetch = _flag ? 1 : 0;
}
- (BOOL)selectsFirstObjectAfterFetch {
  return self->flags.selectFirstAfterFetch ? YES : NO;
}

- (void)setValidatesChangesImmediatly:(BOOL)_flag {
  self->flags.validatesChangesImmediatly = _flag ? 1 : 0;
}
- (BOOL)validatesChangesImmediatly {
  return self->flags.validatesChangesImmediatly ? YES : NO;
}

/* batches */

- (BOOL)hasMultipleBatches {
  return [self batchCount] > 1 ? YES : NO;
}
- (unsigned)batchCount {
  unsigned doc, nob;
  
  doc = [[self allObjects] count];
  nob = [self numberOfObjectsPerBatch];
  
  return (nob == 0)
    ? 1
    : doc / nob + ((doc % nob) ? 1 : 0) ;
}

- (void)setCurrentBatchIndex:(unsigned)_index {
  self->currentBatchIndex = (_index <= [self batchCount]) ? _index : 1;
}
- (unsigned)currentBatchIndex {
  if (self->currentBatchIndex > [self batchCount])
    self->currentBatchIndex = 1;
  return self->currentBatchIndex;
}

- (unsigned)indexOfFirstDisplayedObject {
  return ([self currentBatchIndex] - 1) * [self numberOfObjectsPerBatch];
}

- (unsigned)indexOfLastDisplayedObject {
  unsigned nob = [self numberOfObjectsPerBatch];
  unsigned cnt = [[self allObjects] count];

  if (nob == 0)
    return cnt-1;
  else
    return (([self indexOfFirstDisplayedObject] + nob) < cnt)
      ? ([self indexOfFirstDisplayedObject] + nob) - 1
      : cnt-1;
}

- (id)displayNextBatch {
  [self clearSelection];
  
  self->currentBatchIndex++;
  if (self->currentBatchIndex > [self batchCount])
    self->currentBatchIndex = 1;

  [self updateDisplayedObjects];
  
  return nil;
}
- (id)displayPreviousBatch {
  [self clearSelection];

  self->currentBatchIndex--;
  if ([self currentBatchIndex] <= 0)
    self->currentBatchIndex = [self batchCount];
  
  [self updateDisplayedObjects];
  
  return nil;
}
- (id)displayBatchContainingSelectedObject {
  NSLog(@"WARNING: %s not implemenented", __PRETTY_FUNCTION__);
  [self updateDisplayedObjects];
  return nil;
}

/* selection */

- (BOOL)setSelectionIndexes:(NSArray *)_selection {
  BOOL ok;
  id   d;
  NSSet *before, *after;

  ok = YES;
  if ((d = [self delegate])) {
    if ([d respondsToSelector:
             @selector(displayGroup:shouldChangeSelectionToIndexes:)]) {
      ok = [d displayGroup:self shouldChangeSelectionToIndexes:_selection];
    }
  }
  if (!ok)
    return NO;
  
  /* apply selection */

  before = [NSSet setWithArray:self->selectionIndexes];
  after  = [NSSet setWithArray:_selection];
  
  ASSIGN(self->selectionIndexes, _selection);
  
  if (![before isEqual:after]) {
    [d displayGroupDidChangeSelection:self];
    [d displayGroupDidChangeSelectedObjects:self];
  }
  return YES;
}
- (NSArray *)selectionIndexes {
  return self->selectionIndexes;
}

- (BOOL)clearSelection {
  static NSArray *emptyArray = nil;
  if (emptyArray == nil) emptyArray = [[NSArray alloc] init];
  return [self setSelectionIndexes:emptyArray];
}

- (id)selectNext {
  unsigned int idx;
  
  if ([self->displayObjects count] == 0)
    return nil;
  
  if ([self->selectionIndexes count] == 0) {
    [self setSelectionIndexes:uint0Array];
    return nil;
  }
  
  idx = [[self->selectionIndexes lastObject] unsignedIntValue];
  if (idx >= ([self->displayObjects count] - 1)) {
    /* last object is already selected, select first one */
    [self setSelectionIndexes:uint0Array];
    return nil;
  }
  
  /* select next object .. */
  [self setSelectionIndexes:
          [NSArray arrayWithObject:[NSNumber numberWithUnsignedInt:(idx + 1)]]];
  return nil;
}

- (id)selectPrevious {
  unsigned int idx;
  
  if ([self->displayObjects count] == 0)
    return nil;
  
  if ([self->selectionIndexes count] == 0) {
    [self setSelectionIndexes:uint0Array];
    return nil;
  }
  
  idx = [[self->selectionIndexes objectAtIndex:0] unsignedIntValue];
  if (idx == 0) {
    /* first object is selected, now select last one */
    NSNumber *sidx;
    sidx = [NSNumber numberWithUnsignedInt:([self->displayObjects count] - 1)];
    [self setSelectionIndexes:[NSArray arrayWithObject:sidx]];
  }
  
  /* select previous object .. */
  [self setSelectionIndexes:
          [NSArray arrayWithObject:[NSNumber numberWithUnsignedInt:(idx - 1)]]];
  return nil;
}

- (void)setSelectedObject:(id)_obj {
  NSLog(@"WARNING: %s not implemented.", __PRETTY_FUNCTION__);
}
- (id)selectedObject {
  return nil;
}

- (void)setSelectedObjects:(NSArray *)_objs {
  [self selectObjectsIdenticalTo:_objs];
  //  NSLog(@"WARNING: %s not implemented.", __PRETTY_FUNCTION__);
}
- (NSArray *)selectedObjects {
  NSMutableArray *result;
  unsigned int i, sCount, oCount;

  sCount = [self->selectionIndexes count];
  oCount = [self->objects count];
  result = [NSMutableArray arrayWithCapacity:sCount];
  
  for (i=0; i<sCount; i++) {
    unsigned int idx;

    idx = [[self->selectionIndexes objectAtIndex:i] unsignedIntValue];
    if (idx < oCount)
      [result addObject:[self->objects objectAtIndex:idx]];
  }
  return result;
}

/* returns YES if displayedObjects contains _obj otherwise NO */
- (BOOL)selectObject:(id)_obj {
  NSNumber *idxNumber;
  unsigned idx;
  
  if (![self->displayObjects containsObject:_obj])
    return NO;

  idx = [self->displayObjects indexOfObject:_obj];
  idxNumber = [NSNumber numberWithUnsignedInt:idx];
  
  if ([self->selectionIndexes containsObject:idxNumber])
    return YES;
  else {
    NSMutableArray *tmp;

    tmp = [NSMutableArray arrayWithObjects:self->selectionIndexes];
    [tmp addObject:idxNumber];
    [self setSelectionIndexes:tmp];
    return YES;
  }
}


/* returns YES if at least one obj matches otherwise NO */
- (BOOL)selectObjectsIdenticalTo:(NSArray *)_objs {
  NSMutableArray *newIndexes;
  unsigned       i, cnt;
  BOOL           ok = NO;

  cnt = [_objs count];
  
  if (cnt == 0)
    return NO;

  newIndexes = [NSMutableArray arrayWithCapacity:cnt];
  
  for (i=0; i<cnt; i++) {
    NSNumber *idxNumber;
    unsigned idx;
    id       obj;

    obj = [_objs objectAtIndex:i];
    if (![self->objects containsObject:obj])
      continue;

    ok = YES;
    idx = [self->objects indexOfObject:obj];
    idxNumber = [NSNumber numberWithUnsignedInt:idx];
    
    if ([self->selectionIndexes containsObject:idxNumber])
      continue;

    [newIndexes addObject:idxNumber];
  }
  if (!ok)
    return NO;

  [newIndexes addObjectsFromArray:self->selectionIndexes];
  [self setSelectionIndexes:newIndexes];
  
  return YES;
}

- (BOOL)selectObjectsIdenticalTo:(NSArray *)_objs
            selectFirstOnNoMatch:(BOOL)_flag
{
  if ([self selectObjectsIdenticalTo:_objs])
    return YES;
  
  if (_flag)
    return [self selectObject:[self->displayObjects objectAtIndex:0]];
  else
    return NO;
}

/* objects */

- (void)setObjectArray:(NSArray *)_objects {
  ASSIGN(self->objects, _objects);
  
  /* should try to restore selection */
  [self clearSelection];
  if ([_objects count] > 0 && [self selectsFirstObjectAfterFetch]) {
    [self setSelectionIndexes:uint0Array];
  }
}
 
- (NSArray *)allObjects {
  return self->objects;
}

- (NSArray *)displayedObjects {
  return self->displayObjects;
}

- (id)fetch {
  NSArray *objs;
  
  if ([self->delegate respondsToSelector:@selector(displayGroupShouldFetch:)]) {
    if (![self->delegate displayGroupShouldFetch:self])
      /* delegate rejected fetch-request */
      return nil;
  }

  objs = [[self dataSource] fetchObjects];

  [self setObjectArray:objs];

  if ([self->delegate respondsToSelector:
           @selector(displayGroup:didFetchObjects:)]) {
    [self->delegate displayGroup:self didFetchObjects:objs];
  }

  [self updateDisplayedObjects];

  if ([self selectsFirstObjectAfterFetch]) {
    [self clearSelection];
    
    if ([objs count] > 0)
      [self setSelectedObject:[objs objectAtIndex:0]];
  }  
  return nil;
}
- (void)updateDisplayedObjects {
  NSArray *darray; // display  objects
  NSArray *sarray; // selected objects

  sarray = [self selectedObjects];
  
  if ([self->delegate respondsToSelector:
           @selector(displayGroup:displayArrayForObjects:)]) {
    darray = [self->delegate displayGroup:self
                             displayArrayForObjects:[self allObjects]];

    darray = [darray copy];
    RELEASE(self->displayObjects);
    self->displayObjects = darray;

    return;
  }
  else {
//    EOQualifier *q;
    NSArray     *so, *ao;
    
    ao = [self allObjects];

    /* apply qualifier */
#if 0
    if ((q = [self qualifier]))
      ao = [ao filteredArrayUsingQualifier:q];
#endif // should be done in qualifyDisplayGroup

    /* apply sort orderings */
    if ((so = [self sortOrderings]))
      ao = [ao sortedArrayUsingKeyOrderArray:so];

    if (ao != self->objects)
      [self setObjectArray:ao];

    darray = ao;

    /* apply batch */
    if ([self batchCount] > 1) {
      unsigned first = [self indexOfFirstDisplayedObject];
      unsigned last  = [self indexOfLastDisplayedObject];

      darray = [darray subarrayWithRange:NSMakeRange(first, last-first+1)];
    }
  }
  
  darray = [darray copy];
  RELEASE(self->displayObjects);
  self->displayObjects = darray;

  [self selectObjectsIdenticalTo:sarray];
}

/* query */

- (void)setInQueryMode:(BOOL)_flag {
  self->flags.inQueryMode = _flag ? 1 : 0;
}
- (BOOL)inQueryMode {
  return self->flags.inQueryMode ? YES : NO;
}

- (EOQualifier *)qualifierFromQueryValues {
  NSMutableDictionary *qm, *qmin, *qmax, *qop;
  NSMutableArray *quals;
  NSEnumerator   *keys;
  NSString       *key;
  
  qm   = [self queryMatch];
  qmin = [self queryMin];
  qmax = [self queryMax];
  qop  = [self queryOperator];
  
  quals = [NSMutableArray arrayWithCapacity:[qm count]];
  
  /* construct qualifier for all query-match entries */
  
  keys = [qm keyEnumerator];
  while ((key = [keys nextObject])) {
    NSString *op;
    SEL      ops;
    id       value;
    EOQualifier *q;
    
    value = [qm objectForKey:key];
    
    if ((op = [qop objectForKey:key]) == nil) {
      /* default operator is equality */
      op  = @"=";
      ops = EOQualifierOperatorEqual;
    }
    else if ([value isKindOfClass:[NSString class]]) {
      /* strings are treated in a special way */
      NSString *fmt;

      fmt = [self defaultStringMatchFormat];
      op  = [self defaultStringMatchOperator];
      ops = [EOQualifier operatorSelectorForString:op];
      
      value = [NSString stringWithFormat:fmt, value];
    }
    else {
      ops = [EOQualifier operatorSelectorForString:op];
    }

    q = [[EOKeyValueQualifier alloc]
                              initWithKey:key
                              operatorSelector:ops
                              value:value];
    [quals addObject:q];
    RELEASE(q); q = nil;
  }
  
  /* construct min qualifiers */

  keys = [qmin keyEnumerator];
  while ((key = [keys nextObject])) {
    EOQualifier *q;
    id value;
    
    value = [qmin objectForKey:key];

    q = [[EOKeyValueQualifier alloc]
                              initWithKey:key
                              operatorSelector:EOQualifierOperatorGreaterThan
                              value:value];
    [quals addObject:q];
    RELEASE(q);
  }

  /* construct max qualifiers */

  keys = [qmax keyEnumerator];
  while ((key = [keys nextObject])) {
    EOQualifier *q;
    id value;
    
    value = [qmin objectForKey:key];

    q = [[EOKeyValueQualifier alloc]
                              initWithKey:key
                              operatorSelector:EOQualifierOperatorLessThan
                              value:value];
    [quals addObject:q];
    RELEASE(q);
  }

  if ([quals count] == 0)
    return nil;
  else if ([quals count] == 1)
    return [quals objectAtIndex:0];
  else
    return AUTORELEASE([[EOAndQualifier alloc] initWithQualifierArray:quals]);
}

- (NSMutableDictionary *)queryBindings {
  if (self->_queryBindings == nil)
    self->_queryBindings = [[NSMutableDictionary alloc] init];
  return self->_queryBindings;
}
- (NSMutableDictionary *)queryMatch {
  if (self->_queryMatch == nil)
    self->_queryMatch = [[NSMutableDictionary alloc] init];
  return self->_queryMatch;
}
- (NSMutableDictionary *)queryMin {
  if (self->_queryMin == nil)
    self->_queryMin = [[NSMutableDictionary alloc] init];
  return self->_queryMin;
}
- (NSMutableDictionary *)queryMax {
  if (self->_queryMax == nil)
    self->_queryMax = [[NSMutableDictionary alloc] init];
  return self->_queryMax;
}
- (NSMutableDictionary *)queryOperator {
  if (self->_queryOperator == nil)
    self->_queryOperator = [[NSMutableDictionary alloc] init];
  return self->_queryOperator;
}

- (void)setDefaultStringMatchFormat:(NSString *)_tmp {
  ASSIGN(self->defaultStringMatchFormat, _tmp);
}
- (NSString *)defaultStringMatchFormat {
  return self->defaultStringMatchFormat;
}
- (void)setDefaultStringMatchOperator:(NSString *)_tmp {
  ASSIGN(self->defaultStringMatchOperator, _tmp);
}
- (NSString *)defaultStringMatchOperator {
  return self->defaultStringMatchOperator;
}
+ (NSString *)globalDefaultStringMatchFormat {
  return @"%@*";
}
+ (NSString *)globalDefaultStringMatchOperator {
  return @"caseInsensitiveLike";
}


/* qualfiers */

- (void)setQualifier:(EOQualifier *)_q {
  ASSIGN(self->qualifier, _q);
}
- (EOQualifier *)qualifier {
  return self->qualifier;
}

- (NSArray *)allQualifierOperators {
  static NSArray *quals = nil;
  if (quals == nil) {
    quals = [[NSArray alloc] initWithObjects:
                               @"=", @"!=", @"<", @"<=", @">", @">=",
                               @"like", @"caseInsensitiveLike", nil];
  }
  return quals;
}
- (NSArray *)stringQualifierOperators {
  static NSArray *quals = nil;
  if (quals == nil) {
    quals = [[NSArray alloc] initWithObjects:
                               @"starts with",
                               @"contains",
                               @"ends with",
                               @"is",
                               @"like",
                               nil];
  }
  return quals;
}
- (NSArray *)relationalQualifierOperators {
  static NSArray *quals = nil;
  if (quals == nil) {
    quals = [[NSArray alloc] initWithObjects:
                               @"=", @"!=", @"<", @"<=", @">", @">=", nil];
  }
  return quals;
}

- (void)qualifyDisplayGroup {
  EOQualifier *q;

  if ((q = [self qualifierFromQueryValues]))
    [self setQualifier:q];
  
  [self updateDisplayedObjects];
  
  if ([self inQueryMode])
    [self setInQueryMode:NO];
}

- (void)qualifyDataSource {
  EODataSource *ds;
  EOQualifier  *q;

  ds = [self dataSource];
  
  if ((q = [self qualifierFromQueryValues]))
    [self setQualifier:q];

  if ([ds respondsToSelector:@selector(setAuxiliaryQualifier:)])
    [ds setAuxiliaryQualifier:[self qualifier]];
  else if ([ds respondsToSelector:@selector(setQualifier:)])
    [ds setQualifier:[self qualifier]];
  else {
    /* could not qualify ds */
  }
  
  if ([ds respondsToSelector:@selector(setQualifierBindings:)])
    [ds setQualifierBindings:[self queryBindings]];
  
  [self fetch];
  
  if ([self inQueryMode])
    [self setInQueryMode:NO];
}

/* KVC */

- (void)takeValue:(id)_value forKeyPath:(NSString *)_keyPath {
  if([_keyPath hasPrefix:@"queryMatch."]) {
    [[self queryMatch] takeValue:_value 
		       forKey:[_keyPath substringFromIndex:11]];
  }
  else if([_keyPath hasPrefix:@"queryMax."])
    [[self queryMax] takeValue:_value forKey:[_keyPath substringFromIndex:9]];
  else if([_keyPath hasPrefix:@"queryMin."])
    [[self queryMin] takeValue:_value forKey:[_keyPath substringFromIndex:9]];
  else if([_keyPath hasPrefix:@"queryOperator."]) {
    [[self queryOperator] takeValue:_value 
			  forKey:[_keyPath substringFromIndex:14]];
  }
  else
    [super takeValue:_value forKeyPath:_keyPath];
}
- (id)valueForKeyPath:(NSString *)_keyPath {
  if ([_keyPath hasPrefix:@"queryMatch."])
    return [[self queryMatch] valueForKey:[_keyPath substringFromIndex:11]];
  if ([_keyPath hasPrefix:@"queryMax."])
    return [[self queryMax] valueForKey:[_keyPath substringFromIndex:9]];
  if ([_keyPath hasPrefix:@"queryMin."])
    return [[self queryMin] valueForKey:[_keyPath substringFromIndex:9]];
  if ([_keyPath hasPrefix:@"queryOperator."])
    return [[self queryOperator] valueForKey:[_keyPath substringFromIndex:14]];

  return [super valueForKeyPath:_keyPath];
}

/* NSCoding */

- (id)initWithCoder:(NSCoder *)_coder {
  self->dataSource                 = [[_coder decodeObject] retain];
  self->delegate                   = [_coder decodeObject];
  self->sortOrderings              = [[_coder decodeObject] copy];
  self->insertedObjectDefaults     = [[_coder decodeObject] copy];
  self->qualifier                  = [[_coder decodeObject] copy];
  self->defaultStringMatchFormat   = [[_coder decodeObject] copy];
  self->defaultStringMatchOperator = [[_coder decodeObject] copy];
  self->_queryBindings             = [[_coder decodeObject] copy];
  self->_queryMatch                = [[_coder decodeObject] copy];
  self->_queryMin                  = [[_coder decodeObject] copy];
  self->_queryMax                  = [[_coder decodeObject] copy];
  self->_queryOperator             = [[_coder decodeObject] copy];
  
  return self;
}

- (void)encodeWithCoder:(NSCoder *)_coder {
  [_coder encodeObject:self->dataSource];
  [_coder encodeObject:self->delegate];
  [_coder encodeObject:self->sortOrderings];
  [_coder encodeObject:self->insertedObjectDefaults];
  [_coder encodeObject:self->qualifier];
  [_coder encodeObject:self->defaultStringMatchFormat];
  [_coder encodeObject:self->defaultStringMatchOperator];
  [_coder encodeObject:self->_queryBindings];
  [_coder encodeObject:self->_queryMatch];
  [_coder encodeObject:self->_queryMin];
  [_coder encodeObject:self->_queryMax];
  [_coder encodeObject:self->_queryOperator];
  
  [self notImplemented:_cmd];
}

/* description */

- (NSString *)description {
  return [NSString stringWithFormat:@"<0x%08X %@: ds=%@>",
                     self, NSStringFromClass([self class]),
                     [self dataSource]];
}

@end /* WODisplayGroup */

@implementation WODisplayGroup(KVCArchiving)

- (id)initWithKeyValueUnarchiver:(EOKeyValueUnarchiver *)_unarchiver {
  if ((self = [self init])) {
    id tmp;
    
    if ((tmp = [_unarchiver decodeObjectForKey:@"formatForLikeQualifier"]))
      [self setDefaultStringMatchFormat:tmp];
    
    if ((tmp = [_unarchiver decodeObjectForKey:@"dataSource"]))
      [self setDataSource:tmp];

    if ((tmp = [_unarchiver decodeObjectForKey:@"numberOfObjectsPerBatch"]))
      [self setNumberOfObjectsPerBatch:[tmp intValue]];
    
    [self setFetchesOnLoad:[_unarchiver decodeBoolForKey:@"fetchesOnLoad"]];
    [self setSelectsFirstObjectAfterFetch:
          [_unarchiver decodeBoolForKey:@"selectsFirstObjectAfterFetch"]];
  }
  return self;
}

- (void)encodeWithKeyValueArchiver:(EOKeyValueArchiver *)_archiver {
  [_archiver encodeObject:[self defaultStringMatchFormat]
             forKey:@"formatForLikeQualifier"];
  [_archiver encodeObject:[self dataSource]
             forKey:@"dataSource"];
  [_archiver encodeObject:
               [NSNumber numberWithUnsignedInt:[self numberOfObjectsPerBatch]]
             forKey:@"numberOfObjectsPerBatch"];
  [_archiver encodeBool:[self fetchesOnLoad]
             forKey:@"fetchesOnLoad"];
  [_archiver encodeBool:[self selectsFirstObjectAfterFetch]
             forKey:@"selectFirstAfterFetch"];
}

@end /* KVCArchiving */

@implementation WODisplayGroup(EOEditorsImpl)

#if 0

- (void)editingContextWillSaveChanges:(id)_ec {
}
- (BOOL)editorHasChangesForEditingContext:(id)_ec {
  return NO;
}

#endif

@end /* WODisplayGroup(EOEditorsImpl) */

@implementation WODisplayGroup(EOMessageHandlersImpl)

#if 0

- (void)editingContext:(id)_ec
  presentErrorMessage:(NSString *)_msg
{
}

- (BOOL)editingContext:(id)_ec
  shouldContinueFetchingWithCurrentObjectCount:(unsigned)_oc
  originalLimit:(unsigned)_olimit
  objectStore:(EOObjectStore *)_store
{
  return NO;
}

#endif

@end /* WODisplayGroup(EOMessageHandlersImpl) */
