/* Test basic Objective-C foreach syntax.  This tests the mutation.
*/
/* { dg-do compile } */

/* FIXME: This test should be run, and it succeeds if the program
   aborts at the right time (when the mutation happens).  It currently
   works, but how do we tell the testsuite to test for it ?
*/

#include "../objc-obj-c++-shared/TestsuiteObject.m"
#ifndef __NEXT_RUNTIME__
#include <objc/NXConstStr.h>
#endif

extern int printf (const char *, ...);
#include <stdlib.h>

/*
struct __objcFastEnumerationState
{
  unsigned long state;
  id            *itemsPtr;
  unsigned long *mutationsPtr;
  unsigned long extra[5];
};
*/

 /* A mini-array implementation that can be used to test fast
    enumeration.  You create the array with some objects; you can
    mutate the array, and you can fast-enumerate it.
 */
@interface MyArray : TestsuiteObject
{
  unsigned int length;
  id *objects;
  unsigned long mutated;
}
- (id) initWithLength: (unsigned int)l  objects: (id *)o;
- (void) mutate;
- (unsigned long)countByEnumeratingWithState: (struct __objcFastEnumerationState *)state
                                     objects:(id *)stackbuf 
                                       count:(unsigned long)len;
@end

@implementation MyArray : TestsuiteObject
- (id) initWithLength: (unsigned int)l
	      objects: (id *)o
{
  length = l;
  objects = o;
  mutated = 0;
  return self;
}
- (void) mutate
{
  mutated = 1;
}
- (unsigned long)countByEnumeratingWithState: (struct __objcFastEnumerationState*)state 
		  		     objects: (id*)stackbuf
			 	       count: (unsigned long)len
{
  unsigned long i, batch_size;

  /* Change the mutationsPtr if 'mutate' is called.  */
  state->mutationsPtr = &mutated;

  /* We keep how many objects we served in the state->state counter.  So the next batch
     will contain up to length - state->state objects.  */
  batch_size = length - state->state;

  /* Make obvious adjustments.  */
  if (batch_size < 0)
    batch_size = 0;

  if (batch_size > len)
    batch_size = len;

  /* Copy the objects.  */
  for (i = 0; i < batch_size; i++)
    stackbuf[i] = objects[i];

  state->state += batch_size;
  state->itemsPtr = stackbuf;

  return batch_size;
}
@end

int main (void)
{
  MyArray *array;
  TestsuiteObject *object;
  int counter, i;
  id *objects;

  /* Test iterating over 20 objects, mutating after 15.  */
  objects = malloc (sizeof (id) * 20);
  for (i = 0; i < 20; i++)
    objects[i] = @"object";
  
  array = [[MyArray alloc] initWithLength: 20
			   objects: objects];
  
  counter = 0;
  for (object in array)
    {
      counter++;
      printf ("%d\n", counter);
      if (counter == 14)
	{
	  printf ("Mutating (should abort at next iteration)\n");
	  [array mutate];
	}
    }

  return 0;
}
