#include <stdlib.h>
#include "main.h"
#include "tilemap.h"
#include "sprite.h"
#include "s_bomb.h"
#include "s_player.h"
#include "s_animal.h"
#include "s_blood.h"
#include "s_anim_s.h"
#include "s_pwrup.h"
#include "s_exps.h"
#include "s_expp.h"
#include "s_camera.h"
#include "interact.h"
#include "log.h"

/*
#define SPRITE_TYPE_PLAYER 0
#define SPRITE_TYPE_ANIMAL 1
#define SPRITE_TYPE_BOMB   2
#define SPRITE_TYPE_PWRUP  3
#define SPRITE_TYPE_EXPS   4
#define SPRITE_TYPE_PORTAL 5
*/


SPRITE_DRAW_PROC sprite_draw_proc[N_SPRITE_TYPES] = {
 &draw_player_sprite,
 &draw_animal,
 &draw_bomb,
 &draw_powerup,
 0,
 &draw_portal,
 &draw_blood,
 0,
 &draw_expp
};


SPRITE_UPDATE_PROC sprite_update_proc[N_SPRITE_TYPES] = {
 &update_player_sprite,
 &update_animal,
 &update_bomb,
 &update_powerup,
 &update_exps,
 &update_portal,
 &update_blood,
 &update_camera,
 &update_expp
};


#ifdef FORTIFY
static void freeforall(void *data) {
 free(data);
}
#else
#define freeforall free
#endif
SPRITE_FREEDATA_PROC sprite_freedata_proc[N_SPRITE_TYPES] = {
 0,
 &free_animal_data,
 &freeforall,
 &free_powerup_data,
 &freeforall,
 &freeforall,
 0,
 &freeforall,
 &freeforall
};


/*
#define SPRITE_TYPE_PLAYER 0
#define SPRITE_TYPE_ANIMAL 1
#define SPRITE_TYPE_BOMB   2
#define SPRITE_TYPE_PWRUP  3
#define SPRITE_TYPE_EXPS   4
#define SPRITE_TYPE_PORTAL 5
*/


SPRITE_INTERACT_PROC sprite_interact_proc[N_INTERACTING_SPRITE_TYPES]
                                         [N_INTERACTING_SPRITE_TYPES] = {
{&si_collision,      &si_animal,     &si_bomb_player,    &si_player_powerup, &si_exps,        &si_portal},
{&si_animal,         &si_collision,  &si_animal,         &si_collision,      &si_exps,        0},
{&si_bomb_player,    &si_animal,     &si_collision,      0,                  &si_exps,        &si_portal},
{&si_player_powerup, &si_collision,  0,                  0,                  &si_exps,        0},
{&si_exps,           &si_exps,       &si_exps,           &si_exps,           0,               0},
{&si_portal,         0,              &si_portal,         0,                  0,               0}
};

/*
static int num_sprites = 0;
static int max_sprites = 0;
*/
SPRITE *add_sprite(SPRITE *sprite) {

	SPRITE *newsprite;

	newsprite = malloc(sizeof(SPRITE));
    if (!newsprite) exit(37);
	newsprite->next = sprite;
/*
	num_sprites++;
	if (num_sprites > max_sprites) {
		max_sprites = num_sprites;
		LOG("Max sprites: %i\n", max_sprites);
	}
*/
	return newsprite;
}


SPRITE *spawn_sprite(SPRITE *sprite) {

 SPRITE *newsprite = add_sprite(NULL);
 newsprite->next = sprite->next;
 sprite->next = newsprite;
/*
	num_sprites++;
	if (num_sprites > max_sprites) {
		max_sprites = num_sprites;
		LOG("Max sprites: %i\n", max_sprites);
	}
*/
 return newsprite;
}


SPRITE *destroy_sprite(SPRITE *sprite) {

	SPRITE *next = sprite->next;

	SPRITE_FREEDATA_PROC freedata = sprite_freedata_proc[sprite->type];
	if (freedata) (*freedata)(sprite->data);

	free(sprite);

	//num_sprites--;

	return next;
}


typedef struct SPRITE_NODE {
	SPRITE *spr;
	struct SPRITE_NODE *next;
} SPRITE_NODE;

SPRITE_NODE **sprite_map = NULL;
int sprite_map_w = 0;
int sprite_map_h = 0;
SPRITE_NODE *sprite_node_pool = NULL;
int sprite_node_pool_size = 0;
int sprite_node_pool_capacity = 0;


#if 0
/* Bob's uber-fast dynamic memory-leaking sprite collision system */
void interact_sprites(TILEMAP *map, SPRITE *sprite) {

	int i;

	if (sprite_map == NULL || map->w != sprite_map_w || map->h != sprite_map_h) {
		if (sprite_map) {
			free(sprite_map);
		}
		sprite_map = malloc(sizeof(SPRITE_NODE*) * map->w * map->h);
        if (!sprite_map) exit(37);
		sprite_map_w = map->w;
		sprite_map_h = map->h;
	}
	memset(sprite_map, 0, sizeof(SPRITE_NODE*) * map->w * map->h);
	sprite_node_pool_size = 0;


	/* Scan sprites and insert them in the sprite map as needed */
	while (sprite) {
		if (sprite->type < N_INTERACTING_SPRITE_TYPES) {

			int x, y, x1, y1, x2, y2;
			SPRITE_NODE *node;
			
			/* Grow node pool as needed */
			if (sprite_node_pool_capacity - sprite_node_pool_size < 10) {
				SPRITE_NODE *t = (SPRITE_NODE*)realloc(sprite_node_pool,
					sizeof(SPRITE_NODE) * (sprite_node_pool_capacity + 256));

				if (!t)
					break;
				sprite_node_pool = t;
				sprite_node_pool_capacity += 256;
			}

#define insert_sprite_at(sprite, x, y) { \
	node = &sprite_node_pool[sprite_node_pool_size++]; \
	node->spr = sprite;                 \
	node->next = sprite_map[x + y * sprite_map_w]; \
	sprite_map[x + y * sprite_map_w] = node; \
}


			/* Calculate slots */
			x = (int)sprite->x;
			y = (int)sprite->y;
			x1 = (int)(sprite->x - sprite->r);
			y1 = (int)(sprite->y - sprite->r);
			x2 = (int)(sprite->x + sprite->r);
			y2 = (int)(sprite->y + sprite->r);

			/* Center of sprite */
			insert_sprite_at(sprite, x, y);

			/* Top */
			if (y1 != y && y > 0)
				insert_sprite_at(sprite, x, y - 1);

			/* Top-Left */
			if (y1 != y && x1 != x && x > 0 && y > 0)
				insert_sprite_at(sprite, x - 1, y - 1);

			/* Left */
			if (x1 != x && x > 0)
				insert_sprite_at(sprite, x - 1, y);

			/* Bottom-Left */
			if (y2 != y && x1 != x && x > 0 && y < sprite_map_h - 1)
				insert_sprite_at(sprite, x - 1, y + 1);

			/* Bottom */
			if (y2 != y && y < sprite_map_h - 1)
				insert_sprite_at(sprite, x, y + 1);

			/* Bottom-Right */
			if (y2 != y && x2 != x && x < sprite_map_w - 1 && y < sprite_map_h - 1)
				insert_sprite_at(sprite, x + 1, y + 1);

			/* Right */
			if (x2 != x && x < sprite_map_w - 1)
				insert_sprite_at(sprite, x + 1, y);

			/* Top-Right */
			if (y1 != y && x2 != x && y > 0 && x < sprite_map_w - 1)
				insert_sprite_at(sprite, x + 1, y - 1);

#undef insert_sprite_at
		}

		sprite = sprite->next;
	}


	/* Scan all lists in map to interact */
	for (i = 0; i < map->w * map->h; i++) {
		SPRITE_NODE *node1, *node2;
		node1 = sprite_map[i];
		while(node1) {
			SPRITE_INTERACT_PROC *interact_range = sprite_interact_proc[node1->spr->type];

			node2 = sprite_map[i];

			while (node2) {
				SPRITE_INTERACT_PROC interact = interact_range[node2->spr->type];

				if (node1 != node2 && interact) {
					(*interact)(map, node1->spr, node2->spr);
				}

				node2 = node2->next;
			}

			node1 = node1->next;
		}
	}
}
#else
/* The old, tested sprite interaction code. Bleh. */
void interact_sprites(TILEMAP *map, SPRITE *sprite) {

 SPRITE *spr2 = 0;

 /* Scan sprites, and create a linked list of interacting ones using 'tnext'.
 */
 while (sprite) {

  if (sprite->type < N_INTERACTING_SPRITE_TYPES) {
   sprite->tnext = spr2;
   spr2 = sprite;
  }

  sprite = sprite->next;
 }

 /* Scan list, causing all pairs to interact. */
 while (spr2) {

  SPRITE_INTERACT_PROC *interact_range = sprite_interact_proc[spr2->type];

  sprite = spr2->tnext;
  while (sprite) {
   SPRITE_INTERACT_PROC interact = interact_range[sprite->type];
   if (interact) (*interact)(map, spr2, sprite);
   sprite = sprite->tnext;
  }

  spr2 = spr2->tnext;
 }
}
#endif



void update_sprites(TILEMAP *map, SPRITE **sprite) {

	SPRITE *spr = *sprite;

	while (spr) {
		SPRITE *next = spr->next;
        (*sprite_update_proc[spr->type])(map, spr);
		spr = next;
	}

	while (*sprite) {
		SPRITE *spr = *sprite;

		if (spr->life == 0)
			*sprite = destroy_sprite(spr);
		else
			sprite = &spr->next;
	}
}



void destroy_all_sprites(SPRITE *sprite) {
	while (sprite) sprite = destroy_sprite(sprite);
}
