//Adapted from "PermutedIndex" by Marcus Daniels, swarm-support, June 21, 2000
//avltr is "right threaded" avl tree. Better if you need "next" support.

#import <simtools.h> // initSwarm
#import <collections.h> // Array
#import <defobj/Create.h> // CreateDrop
#import <objectbase/Swarm.h> // Swarm
#import <defobj/defalloc.h> // getZone
#import <objectbase/SwarmObject.h>  //superclass of AVLtree

//#include <misc/avl.h> //for the AVLtree

#include <misc/avl.h>


#define COUNT 10


	
@interface AVLSet: SwarmObject
{
  avl_tree * myTree;
}

+ createBegin: aZone;

+ createBegin:  (int (*)(const void*,const void*,void*)) compareFunc  Param: (void *) param Zone: aZone;

- createEnd;

//Set Protocol
-(BOOL) add: anObject;

- (id *) replace: anObject;

//End Set Protocol



//New thing I made up because I didn't like the Set default behavior
- forcefulReplace: anObject;

-(void)  walk: (void (*)(void*,void*)) aFunction Param: (void *) PARAM;

- traverse: (avl_traverser *) travPtr;
//KeyedCollection Protocol
- at: aKey;

- removeKey: aKey;

- (BOOL) containsKey: aKey;

- createIndex: (id <Zone>)aZone fromMember: anObject ;

//EndKeyedCollection Protocol


@end



@implementation AVLSet

//compare functions for AVLtrees must take 3 args, 2 objects and 1 optional parameter.
static int
compare_agent_ids (const void *A, const void *B, void *PARAM)
{
  int a = (unsigned) A;
  int b = (unsigned) B;

  if (a  < b )
    return -1;

  return a > b;
}

+createBegin: aZone  //???don't understand how to use Zone
{
  AVLSet * obj; 
  obj = [super createBegin: aZone];
  obj->myTree = avl_create ( compare_agent_ids, NULL);
  return obj;
}

-createEnd
{
  return [super createEnd];
}

+createBegin: (int (*)(const void*,const void*,void*)) comparFunc  Param: (void *) param Zone: aZone
{
  AVLSet * obj; 
  obj = [super createBegin: aZone];
  obj->myTree = avl_create (comparFunc, param);
  return obj;
}

//add an object if it is not currently in the set
//if anObject is in the set, then return that value.
//does nothing and returns NO otherwise
-(BOOL) add: anObject
  {
    id value = avl_insert (myTree, anObject );//uses integer keys;
    if (value == NULL)
      return YES;
    else return NO;
  }

// if anObject matching key exists, replace it, return old one.
// otherwise do nothing and return nil
- (id *) replace: anObject
{
  void * value;
  if (( value = avl_find(myTree, anObject) ))
    {
      avl_replace (myTree, anObject);
    }
  return value; //have to worry about NULL being different from nil?
}


// Super power insert. Inserts if object does not exist, replaces if object does.
// returns object being replaced if there is one.
- forcefulReplace: anObject
  {
    id value = avl_replace (myTree, anObject );//uses integer keys;
    if (value != NULL) 
      return value;
    else return nil;
  }


-(void)  walk: (void (*)(void*,void*)) aFunction Param: (void *) PARAM
{
  avl_walk(myTree , aFunction, NULL);
}


- traverse: (avl_traverser *) travPtr
{
  id anObject;

  anObject = avl_traverse (myTree, travPtr) ;
  
 return anObject;
}

//to implement the KeyedCollection Protocol:

- at: aKey
{
  void * value=avl_find(myTree, aKey);
   return value;
}


- removeKey: aKey
{
  return avl_delete(myTree, aKey);
}


- (BOOL) containsKey: aKey
{
  if (avl_find(myTree, aKey))
    return YES;
  else
    return NO;
}

- createIndex: (id <Zone>)aZone fromMember: anObject 
{
  raiseEvent(WarningMessage,"Not Implemented");
  return self;
}
@end



@interface Agent: CreateDrop
{
  id <Array> population;
  unsigned personalNumber;
}
+ create: (id <Zone>)aZone setPopulation: (id <Array>)thePopulation setPIN: (unsigned)pin;

- print;

- (int) getPIN;

@end

@implementation Agent
+ create: (id <Zone>)aZone setPopulation: (id <Array>)thePopulation setPIN: (unsigned)pin
{
  Agent *obj = [self createBegin: aZone];
  obj->population = thePopulation;
  obj->personalNumber = pin;
  return [obj createEnd];
}

- print
{
  printf("Hello, from Agent %d \n",personalNumber);
  return self;
}


- (int) getPIN
{
  return personalNumber;
}

@end

@interface ModelSwarm: Swarm
{
  id <Array> population;
   id myTree1;
  id myTree2;

}
- buildObjects;
@end

@implementation ModelSwarm

//here is a user-specified comparison function. Note 3 args!
static int
compare_agent_position (const void *A, const void *B, void *PARAM)
{
  int a =  [(const id) A getPIN];
  int b =  [(const id) B getPIN];
  
  if (a  < b )
    return -1;

  return a> b;
}

static void
print_node ( void *A, void *PARAM)
{
  id a = (id) A;
  [a print];
}


- buildObjects
{
  unsigned i;
  id anAgent;

  [super buildObjects];
  population = [Array create: globalZone setCount: COUNT];

  myTree1 = [[AVLSet createBegin: self ] createEnd]; 

  myTree2 = [[AVLSet createBegin:  compare_agent_position Param: NULL Zone: self ] createEnd] ; //[AVLtree create

  for (i = 0; i < COUNT; i++)
    {
      anAgent= [Agent create: self setPopulation: population setPIN: i];
      [population atOffset: i put: anAgent];

      [myTree1 add: anAgent];//uses integer keys;

      [myTree2 add: anAgent];//retrieves node or inserts if key not in tree already
    }	                         //this amounts to a "set"
   return self;
}

- (void)test
{
  int i;
  //  int j;
  id anAgent=nil;
  avl_traverser trav = {0};
  

  printf("Now, use the avl_walk to march through the tree1\n");
  printf("This is supposed to start with the smallest key and move on up.\n");

  [myTree1 walk: print_node Param: NULL];

  anAgent = nil;
  printf("Now, go forwards through the tree1 with a for loop that uses   \n");
  printf("avl_find with the value of the agent itself from [population atOffset:i] as the key  \n");

  for (i = 0; i< COUNT; i++)
    {
      id outputAgent=nil;
      anAgent =  [population atOffset: i];
      outputAgent= [myTree1 at: anAgent];
      [outputAgent print];
    }


  printf("Now, go backwards through the tree2 with a for loop that uses  \n");
  printf("avl_find with the value of the agent itself as the key  \n");

  for (i = 0; i< COUNT; i++)
    {
      id outputAgent = nil;
      anAgent = [population atOffset: COUNT-i-1];
      outputAgent = [myTree2 at: anAgent];
      [outputAgent print];
    }

  anAgent = nil;
  printf("Now, grab the agent 5th from the tree2 by asking for it by name \n");
  anAgent = [myTree2 at: [population atOffset: 4]];
  [anAgent print];


  printf ("Now, grab the agent 5th (index value=4) from the tree1 by asking for it by name \n");
  anAgent=nil;
  
  anAgent = [myTree1 at: [population atOffset: 4]];
  [anAgent print];


  printf (" Now delete the 5th agent from the tree1 and walk it\n");
  [myTree1 removeKey: [population atOffset: 4]];
  [myTree1 walk: print_node Param: NULL];

  printf ("Observe that the 5th object still exists \n"); 
  printf ("but it is no longer pointed to by an element in the AVLtree \n");

  printf ("What happens if you drop an agent before removing it from the tree? \n");
  printf ("Go into the source code and uncomment the next 3 lines after this \n");
  printf ("comment if you want to see a segmentation violation \n");
  //printf ("lets drop the 6th agent, then do the walk\n");
  //[[population atOffset: 5] drop];
  //[myTree1 walk: print_node Param: NULL];

  anAgent = nil;
  printf("traverse \n");
  while ( (anAgent=[myTree1 traverse: &trav]) != NULL)
    [anAgent print];
}
@end


int
main (int argc, const char **argv)
{
  initSwarmBatch (argc, argv);
  
  {
    id modelSwarm = [ModelSwarm create: globalZone];

    [modelSwarm buildObjects];
    [modelSwarm test];
  }
  return 0;
}

/*
Local Variables:
compile-command: "$SWARMHOME/bin/libtool-swarm --mode=link gcc -D_GNU_SOURCE \
  -DAPPNAME=avlusage2 -o avlusage2 -Wall -Werror -g -Wno-import -I$SWARMHOME/include \
  -I$SWARMHOME/include/swarm -L$SWARMHOME/lib/swarm avlusage2.m  -lswarm -lobjc" 
End:
*/




