/*
    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 <math.h>
#include "MG_lib.h"
#include "WRMS_include.h"

WRMS_worm *
WRMS_get_worm_desc (SPRITE * self)
{
  return (WRMS_worm *) MG_get_key (self, "worm_desc");
}

void
WRMS_worm_unfix_position (SPRITE * self)
{
  WRMS_worm *w = WRMS_get_worm_desc (self);
  w->phys_state = IN_AIR;
  MG_set_weight (self, 1.0f);
}

void
WRMS_worm_fix_position (SPRITE * self)
{
  WRMS_worm *w = WRMS_get_worm_desc (self);
  w->phys_state = ON_LANDSCAPE;
  MG_set_weight (self, 0.0f);
}

int
WRMS_worm_fixed_p (SPRITE * self)
{
  WRMS_worm *w = WRMS_get_worm_desc (self);
  return (w->phys_state == IN_AIR ? 0 : 1);
}

static void
WRMS_choose_physics (SPRITE * self, WORM_STATES cs)
{
#define WORM_JUMP_SPEED -0.15f
#define WORM_LEMMING_SPEED 0.06f

  PHYSICS p_worm = self->physics;

  switch (cs.action)
    {
    case MOVING:
      switch (cs.direction)
	{
	case RIGHT:
	  p_worm.v.x = WORM_LEMMING_SPEED;
	  WRMS_worm_unfix_position (self);
	  break;
	case LEFT:
	  p_worm.v.x = -WORM_LEMMING_SPEED;
	  WRMS_worm_unfix_position (self);
	  break;
	default:
	  break;
	}
      break;

    case JUMPING:
      p_worm.v.y = WORM_JUMP_SPEED;
      p_worm.v.x =
	(cs.direction == LEFT ? WORM_JUMP_SPEED / 2 : -(WORM_JUMP_SPEED / 2));
      MG_move_sprite (self, 0, -19);
      WRMS_worm_unfix_position (self);
      break;

    default:
      p_worm.v.x = 0.0f;
      break;
    }

  MG_set_speed (self, &(p_worm.v));
}

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

static void
WRMS_worm_update_drawing (SPRITE * self)
{
  WRMS_worm *w = WRMS_get_worm_desc (self);
  char *str = NULL;

  /* Changing state sequence */
  str =
    WRMS_build_name (w->worm_state.direction, w->worm_state.action, "worm",
		     WRMS_PIXMAP_SUFFIX);

  MG_set_img (self, MG_get_img (str));

  free (str);
  str =
    WRMS_build_name (w->worm_state.direction, w->worm_state.action, "worm",
		     NULL);
  MG_set_animation (self, str);
  free (str);

  WRMS_gun_update_drawing (self);

}

void
WRMS_worm_move (SPRITE * self)
{
  WRMS_worm *w = WRMS_get_worm_desc (self);

  if (w->phys_state != IN_AIR)
    {
      WORM_STATES control_s = WRMS_worm_move_controll (self);

      if ((control_s.direction != w->worm_state.direction) ||
	  (control_s.action != w->worm_state.action))
	{
	  if (MG_get_key (self, "top_col"))
	    {
	      if (control_s.action == JUMPING)
		return;
	    }

	  WRMS_arrow_unset ();

	  w->worm_state = control_s;
	  WRMS_worm_update_drawing (self);
	  WRMS_choose_physics (self, control_s);
	  w->phys_state = IN_AIR;
	}
    }
}

static void
WRMS_worm_sink (SPRITE * worm)
{
  if (WRMS_worm_team (worm))	/* is still living */
    {
      if (WRMS_rules_can_play_p (worm))
	WRMS_rules_swap_players ();

      MG_send_msg (worm, "on_die");

      WRMS_visualizer_enter ();
      WRMS_visualizer_leave ();

      MG_remove_sprite (worm);
    }
  else
    {
      MG_remove_sprite (worm);
      return;
    }
}

void
WRMS_worm_life (SPRITE * self)
{
  SDL_Rect r = WRMS_WORLD_SCREEN;

  /* test if worm is out of screen, if yes sink em */
  if (MG_null_rect_p (MG_rects_cut (self->dest, r)))
    {
      WRMS_worm_sink (self);
      return;
    }

  if (!WRMS_rules_can_play_p (self))
    {
      WRMS_worm *w = WRMS_get_worm_desc (self);
      if (w->worm_state.action != IDLING)
	{
	  w->worm_state.action = IDLING;
	  WRMS_worm_update_drawing (self);
	  WRMS_choose_physics (self, w->worm_state);
	  w->phys_state = IN_AIR;
	  WRMS_crosshair_unset (WRMS_worm_get_gun (self));
	}
      return;
    }

  /* When we visualize flying bullet and damage efect user can do nothing */
  if (WRMS_visualizer_active_p ())
    return;

  /* decreases remaining worm time */
  WRMS_rules_increase_worm_time (self);

  WRMS_worm *w = WRMS_get_worm_desc (self);

  WRMS_worm_move (self);

  if (w->phys_state != IN_AIR && w->worm_state.action != MOVING)
    {
      WRMS_worm_controll_gun (self);
    }
  else
    {
      WRMS_crosshair_unset (WRMS_worm_get_gun (self));
    }
}

int
WRMS_worm_health (SPRITE * worm)
{
  WRMS_worm_player *p = WRMS_worm_get_player (worm);
  return p->health;
}

void
WRMS_worm_play_done (SPRITE * self)
{
  /* printf play done */
  SPRITE *gun = WRMS_worm_get_gun (self);
  WRMS_worm *w = WRMS_get_worm_desc (self);
  WRMS_crosshair_unset (gun);
  w->space_toggle = 0;
  WRMS_gun_set_power (WRMS_worm_get_gun (self), 0);
}

/*
  collision detection implementation
*/
static void
collision_check (SPRITE * worm, SPRITE * landscape)
{
  POINT pp_col;
  POINT wrm_mid;
  POINT move_offset;
  VECTOR v, speed;
  double k;
  float r;
  float len, dl;
  WRMS_worm *w;

  if ((worm->type == WORM && landscape->type == WATER))
    {
      if (worm->dest.y > landscape->dest.y)
	{
	  WRMS_worm_sink (worm);
	}

      return;
    }

  if (worm->type == WORM && landscape->type == LANDSCAPE)
    {
      w = WRMS_get_worm_desc (worm);

      pp_col = pixel_perfect_collision_p (worm, landscape);

      if (pp_col.x != -1 && pp_col.y != -1)
	{
	  speed.y = 0;
	  speed.x = worm->physics.v.x;

	  r = MAX (worm->dest.w / 2, worm->dest.h / 2) + 1;

	  wrm_mid.x = (worm->dest.w) / 2;
	  wrm_mid.y = (worm->dest.h) / 2;

	  move_offset.x = (wrm_mid.x - pp_col.x);
	  move_offset.y = (wrm_mid.y - pp_col.y);

	  v.x = (float) move_offset.x;
	  v.y = (float) move_offset.y;

	  len = vector_length (v);
	  dl = r - len;

	  int check = (v.x * v.x) + (v.y * v.y);
	  if (check)
	    {
	      k = sqrt ((dl * dl) / check);
	    }
	  else
	    {
	      k = -1.0f;
	    }

	  MG_move_sprite (worm, (int) (k * v.x), (int) (k * v.y));

	  if (move_offset.y > 0)
	    {
	      if (worm->physics.v.y < 0.0f)
		{
		  speed.x = 0;
		}
	      MG_set_key (worm, "top_col", (void *) 1);
	    }
	  else
	    MG_set_key (worm, "top_col", NULL);

	  MG_set_speed (worm, &speed);

	  WRMS_worm_fix_position (worm);
	}
      else
	MG_set_key (worm, "top_col", NULL);
    }
}

static void
on_die (SPRITE * worm)
{
  MG_set_img (worm, MG_get_img ("grave.bmp"));
  MG_set_rect (worm, rect (worm->dest.x, worm->dest.y, 24, 24));
  MG_install_callback (worm, "on_iter", NULL);

  SPRITE *title = MG_get_key (worm, "title_sprite");
  MG_detach_sprite (worm, title);
  MG_remove_sprite (title);

  SPRITE *health = MG_get_key (worm, "worm_health");
  MG_detach_sprite (worm, health);
  MG_remove_sprite (health);

  WRMS_worm_unset_gun (worm);
  WRMS_team_die_player (WRMS_worm_team (worm), worm);
}

static void
destroy_worm (SPRITE * worm)
{
  WRMS_worm *w = WRMS_get_worm_desc (worm);
  if (w->player_spec.name)
    free (w->player_spec.name);
  free (w);
}

WRMS_team *
WRMS_worm_team (SPRITE * worm)
{
  return (WRMS_team *) MG_get_key (worm, "team");
}

SPRITE *
WRMS_worm_make (int x, int y)
{
  WRMS_worm *out = (WRMS_worm *) malloc (sizeof (WRMS_worm));
  memset (out, 0, sizeof (WRMS_worm));
  out->worm_state.action = IDLING;
  out->worm_state.direction = RIGHT;

  SPRITE *wrm = MG_make_sprite (MG_get_img ("worm_idling_right.bmp"), NULL);
  if (!wrm)
    {
      free (out);
      return NULL;
    }

  MG_move_sprite (wrm, x, y);
  MG_set_weight (wrm, 10.0f);

  MG_set_key (wrm, "worm_desc", out);

  MG_install_callback (wrm, "on_iter", WRMS_worm_life);
  MG_install_callback (wrm, "on_collide", collision_check);
  MG_install_callback (wrm, "on_die", on_die);
  MG_install_callback (wrm, "on_destroy", destroy_worm);

  wrm->type = WORM;
  WRMS_worm_unfix_position (wrm);

  return wrm;
}
