//18 July 2001 - COTD submitted by PREDATOR [predator.lair@gmx.co.uk]

//
// global stuff
//
#define MAX_FRAGM	256	// maximum number of objects fragments
#define SECTOR_H	128	// sector width (crap name, I know :)
#define SECTOR_V	96	// sector height


// a very basic object class,
// the real thing would have lots of additional data
//
typedef struct obj_s
{
  float   x, y;
  int	  width, height;
  void	  (*think)(obj_s* self);
  void	  (*collide)(obj_s* self, obj_s* other);
} obj_t;

//
// the key : the object fragment...
//
typedef struct objfrag_s
{
  obj_t*      o;
  objfrag_s*  next;
  objfrag_s*  prev;
} objfrag_t;


//
// global data
//
objfrag_t **	 sectorlist;
objfrag_t	 fraglist[MAX_FRAGM];
int		 numobjs, numfrags;    // current number of objects & fragments
int		 sectors_x, sectors_y; // number of sectors per width / hight
int		 world_h, world_v;     // some sort of world dimensions
// all of the objects are stored in this imaginary linklist type :)
linklist_t *	 obj_list;

//
// setting up the system... ( all of this can be, of course, put in a class )
//
void	word_init()
{
  int count;

  numobjs	= 0;
  numfrags	= 0;
  sectors_x	= world_h / SECTOR_H;
  sectors_y	= world_v / SECTOR_V;
  count 	= sizeof(*sectorlist) * sectors_x * sectors_y;
  sectorlist	= (objfrag_t**)malloc(count);
}

//
// refresh the things a bit, called each frame...
//
void	world_purge()
{
  memset(sectorlist, 0, count);

  // isn't really necesary, the new fragments will be owerwritten...
  memset(fraglist, 0, sizeof(fraglist));
  numfrags = 0;
}


// called each time an object needs to be updated,
// it gets inserted in the collision grid at (sx, sy)
//
// you can spilt up an object between sectors like this:
//     for ( sy = (int)o->y / SECTOR_V;
//	     sy <= ((int)o->y + o->height) / SECTOR_V;
//	     sy++ )
//	   for ( sx = (int)o->x / SECTOR_H;
//		 sx <= ((int)o->x + o->width) / SECTOR_H;
//		 sx++ )
//		 world_sectorize(o, sx, sy);
void	world_sectorize(obj_t* o, int sx, int sy)
{
  objfrag_t** link;
  objfrag_t*   f;

  // grab a root fragment in this sector
  link	= &sectorlist[sectors_x * sy + sx];
  // the new fragment to be added
  f	= &fraglist[numfrags];

  numfrags++;
  if (numfrags > MAX_FRAGM ) return;

  // the tricky part: linking the fragment in
  f->o	  = o;
  f->prev = NULL;
  f->next = *link;
  if ( *link ) (*link)->prev = f;
  *link = f;
}

//
// the really usefull stuff: checking for collisions per sector
//
void	world_check_collisions()
{
  obj_t* o;
  obj_t* oo;
  objfrag_t* f;
  objfrag_t* ff;
  int i;

  for ( i = 0; i < sectors_x * sectors_y; i++ ) {
      f = sectorlist[i];  // prepare an object

      while ( f ) {
	 o = f->o;  // grab the actual object

	 ff = sectorlist[i];
	 while ( ff ) { // check for the others in the same sector
	    oo = ff->o;

	    if ( o != oo ) // can't collide with itself !

	      // user defined collision detection function, usually
	      // a bounding box check, then a more pixel accurate one
	      if ( SOME_SORT_OF_COLISION_DETECTION_MACRO() ) {
		 // call the objects collide funcs. ( or do anything else...)
		 // to be sure that the functions are call only once per object
		 // cut out the second statement or keep a collision counter
		 if ( o->collide )  o->collide(o, oo);
		 if ( oo->collide ) oo->collide(oo, o);
	      }
	      ff = ff->next; // the remaining objects in this sector to be checked
	 }
	 f = f->next; // the next object
      }
  }
}
