#include <math.h>
#include "main.h"
#include "sprite.h"
#include "s_bomb.h"
#include "s_animal.h"
#include "s_exps.h"
#include "player.h"
#include "interact.h"
#include "collide.h"

/* Generic collision detection between two sprites. Any player sprites
   involved in the collision will lose health depending on the impact speed.

   In addition to collision detection, magnetic bombs attract each other, and
   they attract players.
*/
void si_collision(TILEMAP *map, SPRITE *spr1, SPRITE *spr2) {

 float xd = spr2->x - spr1->x;
 float yd = spr2->y - spr1->y;
 float zd = spr2->z - spr1->z;

 float d = xd*xd + yd*yd + zd*zd;
 float r = 0.0;

 if (spr1->type == SPRITE_TYPE_BOMB && ((BOMB *)spr1->data)->type == 1 &&
     ! (spr2->type == SPRITE_TYPE_PLAYER &&
        ((BOMB *)spr1->data)->player == spr2->data))
  r += 0.002;

 if (spr2->type == SPRITE_TYPE_BOMB && ((BOMB *)spr2->data)->type == 1 &&
     ! (spr1->type == SPRITE_TYPE_PLAYER &&
        ((BOMB *)spr2->data)->player == spr1->data))
  r += 0.002;

 if (r > 0.0) {

  r /= MID(0.001, d, 1.0);

  /* Inverse, not inverse square. Not physically accurate, but nicer and much
     faster than inverse square law.
  */

  spr1->xv += xd*r;
  spr1->yv += yd*r;
  spr1->zv += zd*r;

  spr2->xv -= xd*r;
  spr2->yv -= yd*r;
  spr2->zv -= zd*r;
 }

 r = spr1->r + spr2->r;

 if (d < r*r) {

  d = sqrt(d);

  if (d < 0.001) {
   xd = 0;
   yd = 0;
   zd = 1;
  } else {
   xd /= d;
   yd /= d;
   zd /= d;
  }

  d = 1.0 - d/r;

  r = d * spr1->r;
  spr1->x -= xd*r;
  spr1->y -= yd*r;
  spr1->z -= zd*r;
  sprite_tilemap_collision(map, spr1, NULL);

  r = d * spr2->r;
  spr2->x += xd*r;
  spr2->y += yd*r;
  spr2->z += zd*r;
  sprite_tilemap_collision(map, spr2, NULL);

  d = (spr1->xv - spr2->xv) * xd +
      (spr1->yv - spr2->yv) * yd +
      (spr1->zv - spr2->zv) * zd;

  if (d >= 0) {
   spr1->xv -= 0.9*xd*d;
   spr1->yv -= 0.9*yd*d;
   spr1->zv -= 0.9*zd*d;

   spr2->xv += 0.9*xd*d;
   spr2->yv += 0.9*yd*d;
   spr2->zv += 0.9*zd*d;

   if (d >= 0.1) {
    int hp = (int)(d * 65536) - 6553 + 1;
    if (spr1->type == SPRITE_TYPE_PLAYER) {
		if (spr2->type == SPRITE_TYPE_BOMB && spr2->data 
			&& ((BOMB*)spr2->data)->type != 1)
			player_deduct_hp(spr1->data, hp);
	}
    if (spr2->type == SPRITE_TYPE_PLAYER)
		if (spr1->type == SPRITE_TYPE_BOMB && spr1->data 
			&& ((BOMB*)spr1->data)->type != 1)
			player_deduct_hp(spr2->data, hp);
   }
  }

  if (spr1->type == SPRITE_TYPE_PLAYER && spr2->type == SPRITE_TYPE_PLAYER && spr1->data && spr2->data) {
	  PLAYER *pl1 = spr1->data;
	  PLAYER *pl2 = spr2->data;

	  if (pl1->stat & PLAYER_STAT_INVULNERABLE && pl1->stat_timer[PLAYER_STAT_INVULNERABLE_N] > 0) {
		  spr2->xv = spr2->xv * 3;
		  spr2->yv = spr2->yv * 3;
		  spr2->zv = spr2->zv * 3;

		  player_deduct_hp(pl2, 12000);
		  create_explosion(spr2, 30.0);

	  }
	  if (pl2->stat & PLAYER_STAT_INVULNERABLE && pl2->stat_timer[PLAYER_STAT_INVULNERABLE_N] > 0) {
		  spr1->xv = spr1->xv * 3;
		  spr1->yv = spr1->yv * 3;
		  spr1->zv = spr1->zv * 3;

		  player_deduct_hp(pl1, 12000);
		  create_explosion(spr1, 30.0);
	  }
  }
 }
}



/* Explosion source interaction */
void si_exps(TILEMAP *map, SPRITE *spr1, SPRITE *spr2) {

	float xd = spr2->x - spr1->x;
	float yd = spr2->y - spr1->y;
	float zd = spr2->z - spr1->z;

	float d = SQDIST3(xd, yd, zd);
	float fact = MAX(1, d);
	float r = 0.01;

	SPRITE *source, *other;

	(void)map;

	if (spr1->type == SPRITE_TYPE_EXPS) {
		source = spr1;
		other = spr2;
	}
	else {
		source = spr2;
		other = spr1;
	}	

	r /= fact * fact;

	other->xv += xd*r;
	other->yv += yd*r;
	other->zv += zd*r;

	/* Distance check - collide with explosion? */
	r = 0.6 * pow(((EXPSOURCE*)source->data)->damage_size, 1.0/3.0) + spr1->r + spr2->r;
	if (d < r) {
		if (other->type == SPRITE_TYPE_BOMB) {
			/* Explode bomb! */
			BOMB *bomb = other->data;
			if (other->life > 10 || other->life == -1)
				other->life = 10;
			bomb->mini_exp = TRUE;
		}
		else if (other->type == SPRITE_TYPE_ANIMAL) {
			/* Animal dies */
			other->life = 0;

			/* Store collision speed */
			((ANIMAL*)other->data)->col_speed = SQDIST3(spr1->xv - spr2->xv, spr1->yv - spr2->yv, spr1->zv - spr2->zv);
		}
		else if (other->type == SPRITE_TYPE_PLAYER) {
			PLAYER *pl = other->data;

			/* Hurt player */
			player_deduct_hp(pl, source->life * 2);

			/* Repel player */
			d = MAX(0.7, d) * 40;
			other->xv -= (source->x - other->x) / d / 20;
			other->yv -= (source->y - other->y) / d / 20;
			other->zv -= (source->z - other->z) / d / 20;
			//if (ABS(other->thv) < 0.002)
				other->thv += RND(0.005) - 0.0025 * d / 40;
			//else
				//other->thv *= 1.05;
		}
	}
	return;
}


/* Bomb-player interaction - explode on non-owner */
void si_bomb_player(TILEMAP *map, SPRITE *spr1, SPRITE *spr2) {

	SPRITE *pl_spr, *bomb_spr;
	BOMB *bomb;
	PLAYER *pl;
	float d;

	/* Find out who's who */
	if (spr1->type == SPRITE_TYPE_BOMB) {
		bomb_spr = spr1;
		pl_spr = spr2;
	}
	else {
		bomb_spr = spr2;
		pl_spr = spr1;
	}

	bomb = bomb_spr->data;
	pl = pl_spr->data;

	/* Does the bomb belong to the player? */
	if (bomb->player == pl) {
		/* Do regular collision */
		si_collision(map, spr1, spr2);
		return;
	}

	/* Distance check */
	d = SQDIST3(spr2->x - spr1->x, spr2->y - spr1->y, spr2->z - spr1->z);

	if (d < SQ(spr1->r + spr2->r)) {
		/* Otherwise, explode! */
		bomb_spr->life = 0;
		create_explosion(bomb_spr, 50.0);
		player_deduct_hp(pl, 6000);
	}
	else {
		si_collision(map, spr1, spr2);
	}

	return;
}



/* Generic repulsion between two sprites. 
 *  Uses same algorithm as the colliding one.
 */
void si_repel(TILEMAP *map, SPRITE *spr1, SPRITE *spr2) {

 float xd = spr2->x - spr1->x;
 float yd = spr2->y - spr1->y;
 float zd = spr2->z - spr1->z;

 float d = SQDIST3(xd, yd, zd);
 float r = 0.001;

 (void)map;

 r /= MAX(0.01, d);

  /* Inverse, not inverse square. Not physically accurate, but nicer and much
     faster than inverse square law.
  */

  spr1->xv -= xd*r;
  spr1->yv -= yd*r;
  spr1->zv -= zd*r;

  spr2->xv += xd*r;
  spr2->yv += yd*r;
  spr2->zv += zd*r;

}

