// Built from the Swarm Template application. Copyright © 1996-2000 Swarm Development Group.
// Paul Johnson, June 2002

#import "ModelSwarm.h"
#import <collections.h>
#import "Person.h"
#import <simtools.h>  //for Nselect
#import <analysis.h>  //for Averager
#import <objectbase.h> //for setArg


#define numOpinions 10
#define numPeople 10000

@implementation ModelSwarm


+createBegin: (id) aZone {
  ModelSwarm * obj;
  id <ProbeMap> probeMap;

  obj = [super createBegin: aZone];

  // MODIFY: add in defaults for your simulation parameters.
  obj->increment = 1;

  probeMap = [EmptyProbeMap createBegin: aZone];
  [probeMap setProbedClass: [self class]];
  probeMap = [probeMap createEnd];

  // MODIFY: add your own probes for your parameters.
  [probeMap addProbe: [probeLibrary getProbeForVariable: "increment"
				    inClass: [self class]]];
  [probeLibrary setProbeMap: probeMap For: [self class]];
  
  return obj;
}

// MODIFY: possible creation objects here.
- createEnd 
{
  return [super createEnd];
}

- buildObjects 
{
  int i;


  [super buildObjects];

  // MODIFY: create your simulation objects and set them up from the
  // model parameters.
  
  womenList = [List create: self];
  menList = [List create: self];
  singleWomen = [List create: self];
  singleMen = [List create: self];
  marriedWomen = [List create: self];


  for (i=0; i < numPeople; i++)
    {
      id aPerson = [[[[Person createBegin: self] createEnd] setSex: female] setID: i];
      [womenList addLast: aPerson];
      [singleWomen addLast: aPerson];
    }

  for (i=0; i < numPeople; i++)
    {
      id aPerson = [[[[Person createBegin: self] createEnd] setSex: male] setID: i ];
      [menList addLast: aPerson];
      [singleMen addLast: aPerson];
    }


  {
    id aWoman;
    //printf("singleWomen has %d members\n",[singleWomen getCount]);
    id index = [singleWomen begin: self]; 
    // for ( aWoman = [index next]; [index getLoc] == Member; aWoman = [index next])

    while ((aWoman = [index next]))
    {
      id aMan;
      int dist = 99;
      int checks = 0;
      id index2 = [singleMen begin: self];
      // printf("singleWomen has %d members\n",[singleWomen getCount]);
      if (aWoman == nil) printf("Caution, nil Woman \n");
  
      do
	{
	  aMan = [index2 next];
	  if (aMan)
	    {
	      dist =  [aWoman considerMarrying: aMan];
	      checks++;
	    }
	} while (aMan && (dist >= 5) && (checks < 16));
      if (aWoman && aMan && dist < 4)
	{
	  [index  remove];
	  [marriedWomen addLast: aWoman];
	  [index2 remove];
	  [aWoman setSpouse: aMan];
	  [aMan setSpouse: aWoman];
	  [index2 drop];
	}
    }
    [index drop];
  }


  originalNMarried=[marriedWomen getCount];

 
  //  [self singleWomenListDiagnostic];
  //  [self marriedWomenListDiagnostic];



  return self;
}



- singleWomenListDiagnostic
{
  id aWoman;
  id index;
  index = [singleWomen begin: self];
  while ((aWoman=[index next]))
    {
      printf ("Single Woman number: %d", [aWoman getID]);
      if ([aWoman getSpouse])
	printf("error. This single woman says she has a spouse");
    }
  [index drop];
  return self;
}


- marriedWomenListDiagnostic
{
  id aWoman;
  id index;
  index = [marriedWomen begin: self];
  while ((aWoman=[index next]))
    {
      printf ("Married Woman number: %d Spouse ID: %d \n", [aWoman getID],[[aWoman getSpouse] getID]);
      
    }
  [index drop];
  return self;
}

- getMarriedList
{
  return marriedWomen;
}

- (double)getSmallerDistMarried
{

  id aWoman;
  id index;
  int cumul = 0;
  index = [marriedWomen begin: self];
  while ((aWoman=[index next]))
    {
      if ([aWoman getSmallerDist]) cumul++;
    }
  [index drop];
  return (double)cumul/[marriedWomen getCount];
}


- (double)getNMarried
{
  //return (double)[marriedWomen getCount]/originalNMarried;
  return (double)[marriedWomen getCount];
}

- compute: xPtr: yPtr
{
  double x = *(double *)xPtr;
  double y = [self gatherRecollects: x ];

  *(double *)yPtr = y;
return (id)YES;

  
}



- (double)gatherRecollects: (double) x
{
  int n = 0;
  double cumul = 0;
//   id <Averager> averager = [Averager createBegin: self];
//   [averager setCollection: marriedWomen];
//   [averager setProbedSelector: M(getDistAt:)];
//   averager = [averager createEnd];
//   [averager setArg: 0 ToUnsigned: (unsigned) x];
//   theValue = [averager getAverage];
//   fprintf(stderr,"The value averager is %f \n",theValue);

//  [averager drop];
  id aWoman;
  id index = [marriedWomen  begin: self];
  while ((aWoman=[index next]))
    {
      cumul += [aWoman getDistAt: (unsigned)x];
      n++;
    }

  fprintf(stderr,"The value averager is %f \n",cumul/(double)n);
  return cumul /(double) n;
}


// - calcualateRecollections
// {
//   for (i = 0; i < 20; i++)
//     {
      
//     }


// }



- randomlyChangeOpinions
{
  id candidateList = [List create: self];
  id index;
  id aCandidate;

  [NSelect select: 0.05*numPeople from: menList into: candidateList];
  index = [candidateList begin: self];
  for (aCandidate=[index next]; [index getLoc]==Member; aCandidate=[index next])
    {
      int opin = [uniformIntRand getIntegerWithMin:0 withMax: numOpinions-1];
      [aCandidate changeOpinion: opin];
    }
  [index drop];


  [candidateList removeAll];

  [NSelect select: 0.05*numPeople from: womenList into: candidateList];
  index = [candidateList begin: self];
  for (aCandidate=[index next]; [index getLoc]==Member; aCandidate=[index next])
    {
      int opin = [uniformIntRand getIntegerWithMin:0 withMax: numOpinions-1];
      [aCandidate changeOpinion: opin];
    }
  [index drop];

  return self;
}

- recheckMarriages
{
  id aWoman, aMan;
  int i =0;
  id index;
  id divorcedWomenList= [List create:self];//women
  id divorcedMenList = [List create:self]; //men
  for ( i = 0; i< [marriedWomen getCount]; i++)
  {
    aWoman = [marriedWomen atOffset:i];
    id divorcedHusband = [aWoman considerStayingMarried];
    if (divorcedHusband)
	{
	  [divorcedWomenList addLast: aWoman];
	}

    if (divorcedHusband)
      {
	[divorcedMenList addLast: divorcedHusband];
      }

     //  if (divorcedHusband != nil)
// 	{
// 	  id theWoman = [index remove];
// 	  [divorcedHusband setSpouse: nil];
// 	  [singleMen addLast: divorcedHusband];
// 	  [singleWomen addLast: theWoman];
// 	}
  }
  
  
  index = [divorcedWomenList begin: self];
  while ((aWoman=[index next]))
    {
      [marriedWomen remove: aWoman];
      [singleWomen addLast: aWoman];
    }

  index = [divorcedMenList begin: self];
  while ((aMan=[index next]))
    {
      [singleMen addLast: aWoman];
    }

  [index drop];
  [divorcedWomenList drop];
  [divorcedMenList drop];
  printf("Random change, N of married is %d \n", [marriedWomen getCount]);
  return self;
}

- buildActions 
{
  [super buildActions];

  // MODIFY: schedule your own agents as appropriate.
  modelActions = [ActionGroup create: self];
 
  [modelActions createActionTo: self message: M(randomlyChangeOpinions)];
  [modelActions createActionTo: self message: M (recheckMarriages)];
  modelSchedule = [Schedule createBegin: self];
  [modelSchedule setRepeatInterval: 1];
  modelSchedule = [modelSchedule createEnd];
  [modelSchedule at: 0 createAction: modelActions];

  return self;
}

- activateIn:  swarmContext 
{
  [super activateIn: swarmContext];

  [modelSchedule activateIn: self];

  return [self getSwarmActivity];
}

@end
