/*
    Bazoo_worms the game
    Copyright (C) 2011  Pavel Prochazka

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

#include "WRMS_include.h"
#include <limits.h>

void
WRMS_gun_choose_pixmap (SPRITE * gun)
{
  WRMS_gun_desc *g = WRMS_get_gun_desc (gun);
  char *pixmap =
    WRMS_build_name (g->gun_state.direction, g->gun_state.action,
		     g->gun_vtbl.gun_prefix, WRMS_PIXMAP_SUFFIX);
  MG_set_img (gun, MG_get_img (pixmap));
  free (pixmap);
}

/*
  BAZOOKA VIRTUAL FUNCTIONS IMPLEMENTATION
*/
void
bazooka_draw_impl (SPRITE * self)
{
  WRMS_gun_desc *g_desc = WRMS_gun_get_desc (self);
  SPRITE *worm = g_desc->owner;
  WRMS_worm *w_desc = WRMS_get_worm_desc (worm);
  DIRECTION direction = g_desc->gun_state.direction;

  if (direction == w_desc->worm_state.direction)
    return;

  g_desc->gun_state.direction = w_desc->worm_state.direction;
  direction = g_desc->gun_state.direction;

  VECTOR inv_angle = WRMS_gun_get_angle (self);
  inv_angle.x = -inv_angle.x;

  WRMS_gun_set_angle (self, inv_angle, direction);

  WRMS_gun_choose_pixmap (self);
  WRMS_crosshair_update (self);
}

void
bazooka_sighting_impl (SPRITE * self)
{
  /* here can go setting different angle of the gun self */
  WRMS_crosshair_update (self);
}

void
bazooka_fire_impl (SPRITE * self)
{
  WRMS_bullet_bazoo_impl (self);
}

POINT
bazooka_simulate_fire_impl (SPRITE * self, VECTOR v, float power)
{
  POINT mid_bullet = sprite_mid (self);
  SPRITE *landscape = WRMS_landscape_get ();
  PHYSICS p = WRMS_firepower (1.0f, v, power);

  SDL_Rect r_screen = WRMS_WORLD_SCREEN;
  SDL_Rect r = { mid_bullet.x, mid_bullet.y, 1, 1 };

  while (1)
    {
      if (pixel_collide_sprite_p (landscape, mid_bullet))
	return mid_bullet;
      else if (MG_null_rect_p (MG_rects_cut (r_screen, r)))
	return mid_bullet;

      r = MG_apply_physics (&p, r, WRMS_TIMING);
      mid_bullet.x = r.x;
      mid_bullet.y = r.y;
    }

  return mid_bullet;
}

float
bazooka_compute_damage_impl (SPRITE * self, SPRITE * victim, POINT impact)
{
#define BAZOO_ACTIVE_DISTANCE 80.0f
#define MAX_DAMAGE 100.0f
  float distance = (float) points_distance (sprite_mid (victim), impact);
  float perc = distance / BAZOO_ACTIVE_DISTANCE;

  float damage = MAX_DAMAGE - (perc * MAX_DAMAGE);
  return (damage <= 0.0f ? 0.0f : damage);
}

static const WRMS_gun gun_vtbl[] = {
  {
   "bazooka",
   8,
   bazooka_draw_impl,
   bazooka_sighting_impl,
   bazooka_fire_impl,
   bazooka_simulate_fire_impl,
   bazooka_compute_damage_impl,
   }
};

/*
  GENERAL FUNCTIONS FOR GUNS
*/
WRMS_gun_desc *
WRMS_get_gun_desc (SPRITE * gun)
{
  return (WRMS_gun_desc *) MG_get_key (gun, "gun_desc");
}

static void
gun_destroy (SPRITE * gun)
{
  free (MG_get_key (gun, "gun_desc"));
}

SPRITE *
WRMS_gun_make (WRMS_gun_type gun_definition)
{
  SPRITE *out = MG_make_sprite (NULL, NULL);
  WRMS_gun g = gun_vtbl[gun_definition];
  WRMS_gun_desc *gun_desc = (WRMS_gun_desc *) malloc (sizeof (WRMS_gun_desc));
  memset (gun_desc, 0, sizeof (WRMS_gun_desc));

  gun_desc->gun_state.direction = -1;
  gun_desc->gun_state.action = -1;
  gun_desc->gun_vtbl = g;

  gun_desc->ammo_left = g.ammo;
  gun_desc->vtbl_idx = gun_definition;
  out->type = GUN;

  MG_set_key (out, "gun_desc", gun_desc);
  MG_install_callback (out, "on_destroy", gun_destroy);
  return out;
}

WRMS_gun_desc *
WRMS_gun_get_desc (SPRITE * gun)
{
  return (WRMS_gun_desc *) MG_get_key (gun, "gun_desc");
}

SPRITE *
WRMS_worm_get_gun (SPRITE * worm)
{
  WRMS_worm *w = WRMS_get_worm_desc (worm);
  return w->player_spec.active_gun;
}

const char *
WRMS_gun_string (WRMS_gun_type g_type)
{
  return gun_vtbl[g_type].gun_prefix;
}

int
WRMS_worm_set_gun (SPRITE * worm, WRMS_gun_type g_type)
{
  SPRITE *gun = (SPRITE *) MG_get_key (worm, WRMS_gun_string (g_type));
  if (!gun)
    return -1;

  WRMS_worm *w_desc = WRMS_get_worm_desc (worm);
  WRMS_gun_desc *g_desc = WRMS_gun_get_desc (gun);
  char *str = NULL;
  SDL_Surface *gun_surf;

  g_desc->gun_state.direction = w_desc->worm_state.direction;
  g_desc->owner = worm;
  g_desc->angle.x = (g_desc->gun_state.direction == LEFT ? -1.0f : 1.0f);
  g_desc->angle.y = 0;
  g_desc->power = 0.0f;

  w_desc->player_spec.active_gun = gun;

  gun->dest.x = 0;
  gun->dest.y = 0;

  str = WRMS_build_name (g_desc->gun_state.direction, -1,
			 WRMS_gun_string (g_type), WRMS_PIXMAP_SUFFIX);
  gun_surf = MG_get_img (str);

  free (str);

  if (gun_surf)
    {
      gun->src.w = gun_surf->w;
      gun->src.h = gun_surf->h;
      gun->dest.w = gun_surf->w;
      gun->dest.h = gun_surf->h;
    }

  gun->dest.x = worm->dest.x;
  gun->dest.y = worm->dest.y;

  MG_attach_sprite (worm, gun, 0, 0);
  MG_set_img (gun, gun_surf);

  return 0;
}

void
WRMS_worm_unset_gun (SPRITE * worm)
{
  WRMS_worm *w_desc = WRMS_get_worm_desc (worm);
  SPRITE *gun = w_desc->player_spec.active_gun;

  if (!gun)
    {
      printf ("weapon: no gun\n");
      return;
    }

  MG_detach_sprite (worm, gun);
  MG_remove_sprite (gun);

  gun->dest.x = 0;
  gun->dest.y = 0;

  WRMS_crosshair_unset ();
}

WRMS_gun_desc *
WRMS_worm_get_gun_desc (SPRITE * worm)
{
  SPRITE *gun = WRMS_worm_get_gun (worm);
  return WRMS_get_gun_desc (gun);
}

DIRECTION
WRMS_gun_get_direction (SPRITE * gun)
{
  WRMS_gun_desc *g_desc = WRMS_gun_get_desc (gun);
  return g_desc->gun_state.direction;
}

PHYSICS
WRMS_firepower (float weight, VECTOR v, float power)
{
  VECTOR out_speed = vector_normalize (v, 1.0f);
  out_speed.x *= power;
  out_speed.y *= power;
  VECTOR out_power = WRMS_wind_get ();
  PHYSICS out = { weight, out_power, out_speed };

  return out;
}

/*
  FUNCS TO CALL VIRTUAL FUNCTIONS FROM VTBLS
*/
void
WRMS_gun_update_drawing (SPRITE * worm)
{
  SPRITE *gun = WRMS_worm_get_gun (worm);
  WRMS_gun_desc *g_desc = WRMS_gun_get_desc (gun);

  void (*on_draw) (SPRITE *) = g_desc->gun_vtbl.on_draw;

  if (on_draw)
    on_draw (gun);
}

void
WRMS_worm_controll_gun_impl (SPRITE * worm)
{
  SPRITE *gun = WRMS_worm_get_gun (worm);
  WRMS_gun_desc *g_desc = WRMS_gun_get_desc (gun);


  void (*on_sighting) (SPRITE *) = g_desc->gun_vtbl.sighting;

  if (on_sighting)
    on_sighting (gun);
}

void
WRMS_gun_fire (SPRITE * worm)
{
  if (!WRMS_rules_can_fire_p (worm))
    return;

  if (WRMS_visualizer_active_p ())
    {
      printf ("visualizer active...can not fire\n");
      return;
    }

  WRMS_arrow_unset ();

  SPRITE *gun = WRMS_worm_get_gun (worm);
  WRMS_gun_desc *g_desc = WRMS_gun_get_desc (gun);

  void (*on_fire) (SPRITE *) = g_desc->gun_vtbl.fire;
  if (on_fire)
    {
      /* enter bullet visualizer */
      WRMS_visualizer_enter ();
      on_fire (gun);
    }

  WRMS_gun_reset_power (gun);
}

POINT
WRMS_gun_simulate_fire (SPRITE * self, VECTOR v, float power)
{
  WRMS_gun_desc *g_desc = WRMS_gun_get_desc (self);
  POINT (*sim_fire) (SPRITE *, VECTOR, float) =
    gun_vtbl[g_desc->vtbl_idx].simulate_fire;

  if (sim_fire)
    {
      return sim_fire (self, v, power);
    }

  POINT out = { -INT_MAX, -INT_MAX };	/* out of coords */
  return out;
}

float
WRMS_gun_compute_damage (SPRITE * gun, SPRITE * victim, POINT impact)
{
  WRMS_gun_desc *g_desc = WRMS_gun_get_desc (gun);

  if (g_desc->gun_vtbl.compute_damage)
    return g_desc->gun_vtbl.compute_damage (gun, victim, impact);

  printf ("warn: compute_damage virtual function not set!\n");
  return 0.0f;
}

/*
  ANGLE CONTROLLING FUNCS
*/
VECTOR
WRMS_gun_get_angle (SPRITE * gun)
{
  WRMS_gun_desc *g_desc = WRMS_gun_get_desc (gun);
  return g_desc->angle;
}

VECTOR
WRMS_gun_increase_angle (SPRITE * gun)
{
  VECTOR angle = WRMS_gun_get_angle (gun);
  int g_dir = (WRMS_gun_get_direction (gun) == LEFT ? 1 : -1);
  return rotate_vector (angle, g_dir * ANGLE_STEP);
}

VECTOR
WRMS_gun_decrease_angle (SPRITE * gun)
{
  VECTOR angle = WRMS_gun_get_angle (gun);
  int g_dir = (WRMS_gun_get_direction (gun) == LEFT ? -1 : 1);
  return rotate_vector (angle, g_dir * ANGLE_STEP);
}

void
WRMS_gun_set_angle (SPRITE * gun, VECTOR v, DIRECTION d)
{
  if (d == RIGHT)
    {
      if (v.x < 0)
	return;
    }
  else if (d == LEFT)
    {
      if (v.x > 0)
	return;
    }

  WRMS_gun_desc *g_desc = WRMS_gun_get_desc (gun);
  g_desc->angle = v;
}

/*
  GUN POWER CONTROLLING
*/
static Uint32 _time_dt = 0;
#define POWER_TICK 100

float
WRMS_gun_set_power (SPRITE * gun, float power)
{
  WRMS_gun_desc *w = WRMS_gun_get_desc (gun);
  return (w->power = (power <= 1.0f ? power : 1.0f));
}

float
WRMS_gun_increase_power (SPRITE * gun)
{
  WRMS_gun_desc *w = WRMS_gun_get_desc (gun);

  _time_dt += MG_get_system_dt ();

  if (_time_dt >= POWER_TICK)
    {
      w->power += POWER_STEP;
      _time_dt -= POWER_TICK;
    }

  return w->power;
}

void
WRMS_gun_reset_power (SPRITE * gun)
{
  WRMS_gun_desc *w = WRMS_gun_get_desc (gun);
  w->power = 0.0f;
  _time_dt = 0;
}

float
WRMS_gun_get_power (SPRITE * gun)
{
  WRMS_gun_desc *w = WRMS_gun_get_desc (gun);
  return w->power;
}
