/*!
  @header FTOrderedReferenceSetImpl
  @abstract Module of FT
 
  @availability OS X, GNUstep
  @copyright 2004, 2005, 2006 Free Software Foundation, Inc.
  
  This library 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.1 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
  Lesser General Public License for more details.

  You should have received a copy of the GNU Lesser General Public
  License along with this library; if not, write to the Free Software
  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA

  <pre>
  -------------------------------------------------------------------------
  Modification history

  11.09.05 ola     initial version
  23.08.06 ola     license changed
  -------------------------------------------------------------------------
  </pre>
*/

#include <FT/FTOrderedReferenceSetImpl.h>
#include <FT/FTReference.h>
#include <FT/FTReferenceImpl.h>

@implementation FTOrderedReferenceSetImpl


- initForBaseNode: (id <FTNode>) aNode {
  self = [super init];

  self->baseNodeId = [[aNode nodeId] retain];

  self->references = [[NSMutableArray alloc] init];
  self->nodeIdToIndexArray = [[NSMutableDictionary alloc] init];
  self->edgeIdToIndex = [[NSMutableDictionary alloc] init];

  return self;
}


- (id) initWithCoder:(NSCoder *) decoder {
  self->baseNodeId = [[decoder decodeObject] retain];
  self->references = [[decoder decodeObject] retain];
  
  self->nodeIdToIndexArray = nil;
  self->edgeIdToIndex = nil;
  
  [self createIndexes];
  
  return self;
}


- (void) dealloc {
  [self->baseNodeId release];
  [self->references release];
  [self->nodeIdToIndexArray release];
  [self->edgeIdToIndex release];
  
  [super dealloc];
}


- (id <ECIterator>) allReferences {
  return [[ECArrayIterator alloc] initWithArray: self->references];
}


- (unsigned) countReferences {
  return [self->references count];
}


- createAndAppendReferenceToNode: (id <FTId>) aNodeId
  withEdgeId: (id <FTId>) anEdgeId {
  NSNumber *indexOfNewEntry = 
    [[NSNumber alloc] initWithUnsignedInt: [self->references count]];
  FTReferenceImpl *reference = 
    [[FTReferenceImpl alloc] initWithNodeId: aNodeId edgeId: anEdgeId];
    
  [self->references addObject: reference ];
  
  [self updateIndexes: indexOfNewEntry forReference: reference];
  
  [reference release];
  [indexOfNewEntry release];
  
  return self;
}


- createIndexes {
  unsigned i;
  
  if( nil != self->nodeIdToIndexArray ) {
    [self->nodeIdToIndexArray release];
  }
  
  self->nodeIdToIndexArray = [[NSMutableDictionary alloc] init];
  
  if( nil != self->edgeIdToIndex ) {
    [self->edgeIdToIndex release];
  }
  
  self->edgeIdToIndex = [[NSMutableDictionary alloc] init];
  
  for(i = 0; i < [self->references count]; i++) {
    FTReferenceImpl *current 
      = (FTReferenceImpl *) [self->references objectAtIndex: i];
    NSNumber *index = [[NSNumber alloc] initWithUnsignedInt: i];
    
    [self updateIndexes: index forReference: current];
    
    [index release];
  }

  return self;
}


- (void) encodeWithCoder:(NSCoder *) encoder {
  [encoder encodeObject: self->baseNodeId];
  [encoder encodeObject: self->references];
}


- (unsigned) hash {
  return [self->baseNodeId hash] + 17;
}


- (BOOL) isEqual: (id) anObject {
  if( nil != anObject ) {
    if( [anObject isKindOfClass: [self class]] ) {
      FTOrderedReferenceSetImpl *set = (FTOrderedReferenceSetImpl *) anObject;
      if( [self->baseNodeId isEqual: set->baseNodeId] ) {
        return [self->references isEqual: set->references];
      } else {
        return NO;
      }
    } else {
      return NO;
    }
  } else {
    return NO;
  }
}


- (id <ECIterator>) referencesByNodeId: (id <FTId>) aNodeId {
  NSArray *indexArray = [self->nodeIdToIndexArray objectForKey: aNodeId];
  NSMutableArray *foundReferences = [[NSMutableArray alloc] init];
  ECArrayIterator *result;
  
  if( nil != indexArray ) {
    int i;
    
    for(i = 0; i < [indexArray count]; i++) {
      NSNumber *currentIndex = [indexArray objectAtIndex: i];
      [foundReferences addObject: 
        [self->references objectAtIndex: [currentIndex unsignedIntValue]]];
    }
  }
  
  result = [[ECArrayIterator alloc] initWithArray: foundReferences];
  
  [foundReferences release];
  
  return result;
}


- (id <FTReference>) referenceByEdgeId: (id <FTId>) anEdgeId {
  NSNumber *index = (NSNumber *) [self->edgeIdToIndex objectForKey: anEdgeId];
  FTReferenceImpl *toReturn = nil;
  
  if( nil != index ) {
    toReturn = (FTReferenceImpl *) 
      [self->references objectAtIndex: [index unsignedIntValue]];
  }
  
  return toReturn;
}


- removeReference: (id <FTReference>) toRemove {
  NSNumber *index = (NSNumber *) 
    [self->edgeIdToIndex objectForKey: [toRemove edgeId]];
  
  if( nil != index ) {
    [self->references removeObjectAtIndex: [index unsignedIntValue]];
    
    [self createIndexes];
  }
  
  return self;
}


- updateIndexes: (NSNumber *) indexOfNewEntry 
  forReference: (FTReferenceImpl *) aReference {
  NSMutableArray *indexArray 
    = [self->nodeIdToIndexArray objectForKey: [aReference nodeId]];
    
  if( nil == indexArray ) {
    indexArray = [[NSMutableArray alloc] init];
    [self->nodeIdToIndexArray setObject: indexArray
      forKey: [aReference nodeId]];
  }
  
  if( [self->edgeIdToIndex objectForKey: [aReference edgeId]] ) {
    // an entry already exists for the given reference!
    [indexArray release];
    [[[ECIllegalArgumentException alloc]
      initWithArgumentInfo: @"FTOrderedReferenceSetImpl::updateIndexes: "\
        "Edge id already exists in set!" ] raise];
  }
  
  [self->edgeIdToIndex setObject: indexOfNewEntry forKey: [aReference edgeId]];
  [indexArray addObject: indexOfNewEntry];
  
  [indexArray release];
  return self;
}

@end
