/* 
   EOArrayProxy.m

   Copyright (C) 1999 MDlink online service center GmbH, Helge Hess

   Author: Helge Hess (hh@mdlink.de)
   Date:   1999

   This file is part of the GNUstep Database Library.

   This library is free software; you can redistribute it and/or
   modify it under the terms of the GNU Library General Public
   License as published by the Free Software Foundation; either
   version 2 of the License, or (at your option) any later version.

   This library 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
   Library General Public License for more details.

   You should have received a copy of the GNU Library General Public
   License along with this library; see the file COPYING.LIB.
   If not, write to the Free Software Foundation,
   59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/

#import "common.h"
#import "EOArrayProxy.h"
#import "EODatabaseChannel.h"
#import "EODatabaseContext.h"
#import "EOEntity.h"
#import "EOSQLQualifier.h"
#import "EOGenericRecord.h"

@implementation EOArrayProxy

- (id)initWithQualifier:(EOSQLQualifier *)_qualifier
  fetchOrder:(NSArray *)_fetchOrder
  channel:(EODatabaseChannel *)_channel
{
  self->qualifier  = RETAIN(_qualifier);
  self->fetchOrder = RETAIN(_fetchOrder);
  self->channel    = RETAIN(_channel);
  return self;
}

+ (id)arrayProxyWithQualifier:(EOSQLQualifier *)_qualifier
  fetchOrder:(NSArray *)_fetchOrder
  channel:(EODatabaseChannel *)_channel
{
  return AUTORELEASE([[self alloc] initWithQualifier:_qualifier
                                   fetchOrder:_fetchOrder
                                   channel:_channel]);
}

- (void)dealloc {
  AUTORELEASE(self->content);
  RELEASE(self->qualifier);
  RELEASE(self->fetchOrder);
  RELEASE(self->channel);
  [super dealloc];
}

// accessors

- (BOOL)isFetched {
  return self->content ? YES : NO;
}

- (EODatabaseChannel *)databaseChannel {
  return self->channel;
}
- (EOEntity *)entity {
  return [self->qualifier entity];
}
- (NSArray *)fetchOrder {
  return self->fetchOrder;
}
- (EOSQLQualifier *)qualifier {
  return self->qualifier;
}

// operations

- (void)clear {
  RELEASE(self->content);
  self->content = nil;
}

- (BOOL)fetch {
  BOOL           inTransaction;
  BOOL           didOpenChannel;
  NSMutableArray *result;
  
  [self clear];
  result = [NSMutableArray arrayWithCapacity:16];

  didOpenChannel = (![self->channel isOpen])
    ? [self->channel openChannel]
    : NO;
    
  if ([self->channel isFetchInProgress]) {
    [NSException raise:NSInvalidArgumentException
		 format:@"attempt to fetch with busy channel: %@", self];
  }

  inTransaction = 
    [[self->channel databaseContext] transactionNestingLevel] > 0;
  if (!inTransaction) {
    if (![[self->channel databaseContext] beginTransaction]) {
      NSLog(@"WARNING: could not begin transaction to fetch array proxy !");

      if (didOpenChannel)
        [self->channel closeChannel];
      return NO;
    }
  }
    
  if (![self->channel selectObjectsDescribedByQualifier:self->qualifier
                      fetchOrder:self->fetchOrder]) {
    if (!inTransaction)
      [[self->channel databaseContext] rollbackTransaction];
    if (didOpenChannel)
      [self->channel closeChannel];
    
    NSLog(@"ERROR: select on array proxy failed ..");
    return NO;
  }

  { // fetch objects
    NSZone *z = [self zone];
    id object;
    
    while ((object = [self->channel fetchWithZone:z]))
      [result addObject:object];
    object = nil;
  }
  [self->channel cancelFetch];

  if (!inTransaction) {
    if (![[self->channel databaseContext] commitTransaction]) {
      NSLog(@"WARNING: could not commit array proxy's transaction !");
      
      if (didOpenChannel)
        [self->channel closeChannel];
      return NO;
    }
  }

  if (didOpenChannel)
    [self->channel closeChannel];
  
  self->content = [result copyWithZone:[self zone]];
  return YES;
}

// turn fault to real array ..

void _checkFetch(EOArrayProxy *self) {
  if (self->content) return;
  [self fetch];
}

// NSArray operations

- (id)objectAtIndex:(unsigned int)_index {
  _checkFetch(self);
  return [self->content objectAtIndex:_index];
}
- (unsigned int)count {
  _checkFetch(self);
  return [self->content count];
}
- (unsigned int)indexOfObjectIdenticalTo:(id)_object {
  _checkFetch(self);
  return [self->content indexOfObjectIdenticalTo:_object];
}

// NSCopying

- (id)copyWithZone:(NSZone*)zone {
  if (NSShouldRetainWithZone(self, zone))
    return RETAIN(self);
  else {
    _checkFetch(self);
    return [[NSArray allocWithZone:zone] initWithArray:self->content copyItems:NO];
  }
}

- (id)mutableCopyWithZone:(NSZone*)zone {
  _checkFetch(self);
  return [[NSMutableArray alloc] initWithArray:self->content];
}


#if 0

// forwarding

- (void)forwardInvocation:(NSInvocation *)_invocation {
  _checkFetch(self);
  [_invocation invokeWithTarget:self->content];
}

- (retval_t)forward:(SEL)_selector:(arglist_t)_frame {
  _checkFetch(self);
  return objc_msg_sendv(self->content, _selector, _frame);
}

#endif

@end
