/*!
  @header FTOrderedEdgeSet
  @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

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

#include <Encore/Encore.h>
#include <FT/FTOrderedEdgeSetImpl.h>
#include <Encore/Encore.h>
#include <FT/FTNodeImpl.h>


@implementation FTOrderedEdgeSetImpl
- init {
  self = [super init];
  
  self->edges = [[NSMutableArray alloc] init];
  self->sourceNodeIdToArrayOfEdgeIndexes = [[NSMutableDictionary alloc] init];
  self->targetNodeIdToArrayOfEdgeIndexes = [[NSMutableDictionary alloc] init];
  self->edgeIdToEdgeIndex = [[NSMutableDictionary alloc] init];

  self = [self initTransientFields];
  
  return self;
}


- initTransientFields {
  self->lock = [[NSLock alloc] init];
  
  return self;
}


- (id) initWithCoder:(NSCoder *) decoder {
  self = [self initTransientFields];
  
  self->edges = [[decoder decodeObject] retain];
  self->sourceNodeIdToArrayOfEdgeIndexes = [[decoder decodeObject] retain];
  self->targetNodeIdToArrayOfEdgeIndexes = [[decoder decodeObject] retain];
  self->edgeIdToEdgeIndex = [[decoder decodeObject] retain];
  
  return self;
}


- (void) dealloc {
  [self->edges release];
  [self->sourceNodeIdToArrayOfEdgeIndexes release];
  [self->targetNodeIdToArrayOfEdgeIndexes release];
  [self->edgeIdToEdgeIndex release];
  [self->lock release];
  
  [super dealloc];
}


- addIdOfNode: (id <FTNode>) aNode 
  ofEdgeArrayIndex: (NSNumber *) anEdgeArrayIndex
  toNodeIdToArrayMap: (NSMutableDictionary *) dict {
  NSMutableArray *edgeArray;
  
  /**
   * fetch array of edges for given node id. If not existent then create one:
   */
  edgeArray = (NSMutableArray *) [dict objectForKey: [aNode nodeId]];
  if( nil == edgeArray ) {
    edgeArray = [[NSMutableArray alloc] init];
    [dict setObject: edgeArray forKey: [aNode nodeId]];
  } else {
    [edgeArray retain];
  }
  
  [edgeArray addObject: anEdgeArrayIndex ];

  return self;
}


- appendEdge: (FTEdgeImpl *) toAdd {
  NSNumber *index = nil;
  
  if( nil == toAdd ) {
    [[[ECIllegalArgumentException alloc] 
      initWithArgumentInfo:  @"FTOrderedEdgeSet::appendEdge: "\
        "nil for edge specified"] raise];
  }
  
  if( nil == [toAdd edgeId] ) {
    [[[ECIllegalArgumentException alloc] 
      initWithArgumentInfo:  @"FTOrderedEdgeSet::appendEdge: "\
        "nil for edge specified"] raise];
  }
  
  if( nil != [self->edgeIdToEdgeIndex objectForKey: [toAdd edgeId]] ) {
    /* TODO: This test has to be done at a central instance in order to check that
     * edge identifiers are globally unique! 
     */
    [self->lock unlock];
    [[[ECIllegalArgumentException alloc] 
      initWithArgumentInfo: @"FTOrderedEdgeSet::appendEdge: Edge with the "\
        "specified id already exists!"] raise];
  }
  
  index = [[NSNumber alloc] initWithUnsignedInt: [self->edges count]];
  
  [self->edges addObject: toAdd];
  [self->edgeIdToEdgeIndex setObject: index forKey: [toAdd edgeId]];
 
  [self addIdOfNode: [toAdd sourceNode]
    ofEdgeArrayIndex: index
    toNodeIdToArrayMap: self->sourceNodeIdToArrayOfEdgeIndexes
  ];
  
  [self addIdOfNode: [toAdd targetNode]
    ofEdgeArrayIndex: index
    toNodeIdToArrayMap: self->targetNodeIdToArrayOfEdgeIndexes
  ];
  
  return self;
}


- (ft_edge_type_t) edgeType: (id <FTEdge>) toLookFor {
  ft_edge_type_t to_return = FT_EDGE_NOT_IN_SET;
  
  if( nil != [self->edgeIdToEdgeIndex objectForKey: [toLookFor edgeId]] ) {
    if( nil != [self->sourceNodeIdToArrayOfEdgeIndexes 
      objectForKey: [[toLookFor sourceNode] nodeId]] ) {
      to_return = FT_EDGE_IS_INCOMING;
    } else {
      if( nil != [self->targetNodeIdToArrayOfEdgeIndexes 
        objectForKey: [[toLookFor targetNode] nodeId]] ) {
        to_return = FT_EDGE_IS_OUTGOING;
      } else {
        [[[ECIllegalStateException alloc] 
          initWithIllegalStateInfo: @"FTOrderedEdgeSetImpl::edgeType: got an "\
            "edge whose nodes are neither in source nor in target node id set!"]
          raise];
      }
    }
  }
  
  return to_return;
}


- (id <ECIterator>) edgesWithNodeId: (id <FTId>) aNodeId 
  inNodeIdToArrayOfEdges: (NSDictionary *) map {
  NSArray *edgeIndexArray;
  NSMutableArray *edgeArray;
  
  [self->lock lock];
  
  edgeIndexArray = [map objectForKey: aNodeId];
  edgeArray = [[NSMutableArray alloc] init];
  
  if( nil != edgeArray ) {
    /**
     * Got an array of indexes within edge array. Now fetch the corresponding
     * edges
     */
    int i;
    
    for( i = 0; i < [edgeIndexArray count]; i++ ) {
      NSNumber *index = [edgeIndexArray objectAtIndex: i];
      
      [edgeArray addObject: [self->edges objectAtIndex: [index intValue]]];
    }
  }
  
  [self->lock unlock];
  
  return [[ECArrayIterator alloc] initWithArray: edgeArray];
}


- (id <ECIterator>) edgesWithSourceNodeId: (id <FTId>) aNodeId {
  return [self edgesWithNodeId: aNodeId 
    inNodeIdToArrayOfEdges: self->sourceNodeIdToArrayOfEdgeIndexes];
}


- (id <ECIterator>) edgesWithTargetNodeId: (id <FTId>) aNodeId {
  return [self edgesWithNodeId: aNodeId 
    inNodeIdToArrayOfEdges: self->targetNodeIdToArrayOfEdgeIndexes];
}


- (void) encodeWithCoder: (NSCoder *) encoder {
  [encoder encodeObject: self->edges];
  [encoder encodeObject: self->sourceNodeIdToArrayOfEdgeIndexes];
  [encoder encodeObject: self->targetNodeIdToArrayOfEdgeIndexes];
  [encoder encodeObject: self->edgeIdToEdgeIndex];
}


- removeEdge: (id <FTEdge>) toRemove {
  //__FTNodeIdToEdgeArrayIndex *edgeInfo;
  
  [self->lock lock];
  
  if( nil == [self->edgeIdToEdgeIndex objectForKey: [toRemove edgeId]] ) {
    [self->lock unlock];
    return self;
  }
#if 0  
  /**
   * search incoming links:
   */
  edgeInfo = [[__FTNodeIdToEdgeArrayIndex alloc]
    initWithNodeId: [[toRemove sourceNode] nodeId]
    withNodeIdToArrayEdgesDictionary: self->sourceNodeIdToArrayOfEdgeIndexes
    withEdge: toRemove
    withEdgeArray: self->edges];
    
  if( [edgeInfo withinAnEdge] ) {
    NSArray *indexArray = [edgeInfo indexArray];
    int i;
    
    for( i = 0; i < [indexArray count]; TODO --> muss mutable sein!!
    [[edgeInfo array] removeObjectAtIndex: [edgeInfo arrayIndex]];
  }

  [edgeInfo release];  
  
  
  /**
   * search outgoing links:
   */
  edgeInfo = [[__FTNodeIdToEdgeArrayIndex alloc]
    initWithNodeId: [[toRemove targetNode] nodeId]
    withNodeIdToArrayEdgesDictionary: self->targetNodeIdToArrayOfEdgeIndexes
    withEdge: toRemove
    withEdgeArray: self->edges];
    
  if( [edgeInfo withinAnEdge] ) {
    [[edgeInfo array] removeObjectAtIndex: [edgeInfo arrayIndex]];
  }

  [edgeInfo release];  

  [self->edgeIdToEdgeIndex removeObjectForKey: [toRemove edgeId]];
#endif 
  
  [self->lock unlock];
  return self;
}


- removeEdges: (id <ECIterator>) toRemove {  
  id <FTEdge> edge;
  
  while( [toRemove hasNext] ) {
    edge = [toRemove next];
    
    [self removeEdge: edge];
  }
  
  return self;
}
@end


@implementation __FTOrderedEdgeSetFilter 

- initWithsourceNodeIdToArrayOfEdgeIndexes: 
    (NSMutableDictionary *) asourceNodeIdToArrayOfEdgeIndexes
    targetNodeIdToArrayOfEdgeIndexes: 
      (NSMutableDictionary *) atargetNodeIdToArrayOfEdgeIndexes
    edgeIdToEdgeIndex: (NSMutableDictionary *) anedgeIdToEdgeIndex {
  self = [super init];
  
  self->sourceNodeIdToArrayOfEdgeIndexes = [asourceNodeIdToArrayOfEdgeIndexes retain];
  self->targetNodeIdToArrayOfEdgeIndexes = [atargetNodeIdToArrayOfEdgeIndexes retain];
  self->edgeIdToEdgeIndex = [anedgeIdToEdgeIndex retain];
  
  self->filterSourceNodeId = nil;
  self->filterTargetNodeId = nil;
  self->filterEdge = nil;
  
  self->filteredEdgeIndexes = nil;
  
  self->filtered = NO;
  
  return self;
}


- (void) dealloc {
  [self->sourceNodeIdToArrayOfEdgeIndexes release];
  [self->targetNodeIdToArrayOfEdgeIndexes release];
  [self->edgeIdToEdgeIndex release];
  
  if( nil != self->filteredEdgeIndexes ) {
    [self->filteredEdgeIndexes release];
  }
  
  if( nil != self->filterSourceNodeId ) {
    [self->filterSourceNodeId release];
  }
  
  if( nil != self->filterTargetNodeId ) {
    [self->filterTargetNodeId release];
  }
  
  if( nil != self->filterEdge ) {
    [self->filterEdge release];
  }
  
  [super dealloc];
}


- setFilterSourceNodeId: (id <FTId>) aNodeId {
  self->filterSourceNodeId = [aNodeId retain];
  return self;
}


- setFilterTargetNodeId: (id <FTId>) aNodeId {
  self->filterTargetNodeId = [aNodeId retain];
  return self;
}


- setFilterEdge: (id <FTId>) anEdge {
  [self->filterEdge retain];
  return self;
}


- filterNodeId: (id <FTId>) aNodeId 
  nodeIdToArrayOfEdgeIndexes: (NSDictionary *) nodeIdToArrayOfEdgeIndexes
  filteredEdgeIndexes: (NSMutableArray *) filteredEdges {
  NSArray *edgeIndexes = [nodeIdToArrayOfEdgeIndexes objectForKey: aNodeId];
  
  if( nil != edgeIndexes ) {
    [filteredEdges addObjectsFromArray: edgeIndexes];
  }
  
  return self;
}


- filter {
  NSNumber *edgeIndex;

  if( nil != self->filteredEdgeIndexes ) {
    return self;
  } 
  
  self->filteredEdgeIndexes = [[NSMutableArray alloc] init];
  
  /**
   * determine corresponding arrays of sourceNodeIdToArrayOfEdgeIndexes and
   * sourceNodeIdToArrayOfEdgeIndexes and filter out all entries which do not
   * point to an edge which is equal to the specified filterEdge. Do not filter
   * out any entry if no filterEdge is being given. 
   */
  if( nil != self->sourceNodeIdToArrayOfEdgeIndexes ) {
    [self filterNodeId: self->filterSourceNodeId 
      nodeIdToArrayOfEdgeIndexes: self->sourceNodeIdToArrayOfEdgeIndexes
      filteredEdgeIndexes: self->filteredEdgeIndexes];
  }
  
  if( nil != self->targetNodeIdToArrayOfEdgeIndexes ) {
    [self filterNodeId: self->filterTargetNodeId 
      nodeIdToArrayOfEdgeIndexes: self->targetNodeIdToArrayOfEdgeIndexes
      filteredEdgeIndexes: self->filteredEdgeIndexes];
  }
  
  edgeIndex = [self->edgeIdToEdgeIndex objectForKey: [filterEdge edgeId]];

  if( nil != edgeIndex ) {
    [self->filteredEdgeIndexes addObject: edgeIndex];
  }
 
  return self;
}
@end

