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

typedef struct WRMS_game
{
  LIST *initial_worms;
  LIST *worms;
  LIST *teams;
  Uint32 time_used;
  int waiting_for_detonation_p;
  int teams_count;
} WRMS_game;

static WRMS_game game_rules;

WRMS_team *
WRMS_team_make (const char *team_name, WRMS_player_types type, SDL_Color c)
{
  WRMS_team *out = (WRMS_team *) malloc (sizeof (WRMS_team));
  memset (out, 0, sizeof (WRMS_team));
  out->team_name = strdup (team_name);
  out->c = c;
  out->type = type;
  return out;
}

void
WRMS_team_destroy (WRMS_team * t)
{
  free (t->team_name);
  free (t);
}

SPRITE *
WRMS_team_add_player (WRMS_team * t, SPRITE * worm)
{
  MG_add (&(t->teammates), worm);
  MG_set_key (worm, "team", t);
  t->initial_teammates_count++;
  return worm;
}

static void
clear_empty_teams ()
{
  LIST *teams = game_rules.teams;
  LIST *teams_to_remove = NULL;;

  while (teams)
    {
      WRMS_team *team = teams->value;
      if (!WRMS_team_players_count (team))
	MG_add (&teams_to_remove, team);

      teams = teams->node;
    }

  LIST *it = teams_to_remove;

  while (it)
    {
      MG_remove_node (&game_rules.teams, (WRMS_team *) it->value);
      WRMS_team_destroy ((WRMS_team *) it->value);
      it = it->node;
    }

  MG_remove_list (teams_to_remove, NULL);
}

SPRITE *
WRMS_team_die_player (WRMS_team * t, SPRITE * worm)
{
  MG_set_key (worm, "team", NULL);
  MG_remove_node (&t->teammates, worm);
  MG_remove_node (&game_rules.worms, worm);

  clear_empty_teams ();

  return worm;
}

int
WRMS_team_players_count (WRMS_team * t)
{
  return MG_list_length (t->teammates);
}

LIST *
WRMS_team_players (WRMS_team * t)
{
  return t->teammates;
}

int
WRMS_team_health (WRMS_team * t)
{
  int out = 0;
  LIST *tmp = t->teammates;

  while (tmp)
    {
      SPRITE *worm = (SPRITE *) tmp->value;
      out += WRMS_worm_health (worm);
      tmp = tmp->node;
    }

  return out;
}

void
WRMS_init_rules ()
{
  memset (&game_rules, 0, sizeof (WRMS_game));
  WRMS_rules_clear ();
}

LIST *
WRMS_rules_teams ()
{
  return game_rules.teams;
}

Uint32
WRMS_rules_remaining ()
{
  return TIME_LIMIT - game_rules.time_used;
}

void
WRMS_rules_add_team (WRMS_team * t)
{
  MG_add (&(game_rules.teams), t);
}

void
WRMS_rules_update_worms_health ()
{
  LIST *worms = WRMS_rules_worms ();
  LIST *worms_to_die = NULL;

  while (worms)
    {
      SPRITE *w = (SPRITE *) worms->value;
      WRMS_rules_compute_health (w);

      if (WRMS_worm_health (w) <= 0)
	{
	  MG_add (&worms_to_die, w);
	}
      else
	{
	  SPRITE *health = MG_get_key (w, "worm_health");
	  if (health)
	    {
	      MG_detach_sprite (w, health);
	      MG_remove_sprite (health);
	    }
	  int ihealth = WRMS_worm_health (w);
	  char h[25];
	  WRMS_team *t = WRMS_worm_team (w);
	  sprintf (h, "%d", ihealth);
	  health = WRMS_text (h, t->c, 10, WRMS_FONT);

	  MG_attach_sprite (w, health, 0, 0);
	  sprite_align_mid (health, w);
	  MG_move_sprite (health, 0, -36);

	  MG_set_key (w, "worm_health", health);
	}

      worms = worms->node;
    }

  LIST *it = worms_to_die;
  while (it)
    {
      MG_send_msg ((SPRITE *) it->value, "on_die");
      it = it->node;
    }

  if (worms_to_die)
    MG_remove_list (worms_to_die, NULL);
}

int
WRMS_game_begin (LIST * teams, int beginning_team)
{
  printf ("RULES: begining game\n");

  WRMS_init_rules ();
  WRMS_crosshair_clear ();
  WRMS_visualizer_init ();

  srand (clock ());

  LIST *it = teams;
  SDL_Color start_color[] =
    { color (255, 128, 0), color (0, 128, 255), color (128, 0, 255),
color (128, 255, 0) };
  int i = 0;

  while (it)
    {
      HASH *thash = (HASH *) it->value;
      char *team_name = (char *) MG_hash_get_key (thash, ":name")->values;
      LIST *players = (LIST *) MG_hash_get_key (thash, ":teammates")->values;

      char *type = (char *) MG_hash_get_key (thash, ":type")->values;
      int itype = WRMS_COMPUTER;

      if (!strcmp (type, "HUMAN"))
	itype = WRMS_HUMAN;

      WRMS_team *t = WRMS_team_make (team_name, itype, start_color[i++]);
      int i = 0;

      while (players && (i < TEAMMATES_MAX_COUNT))
	{
	  HASH *player = players->value;
	  if (!player)
	    break;

	  WRMS_player_make ((char *) player->values, t, 0);
	  players = players->node;
	  i++;
	}

      WRMS_rules_add_team (t);
      /* TODO: clear hashes and arg list ! */

      it = it->node;
    }

  WRMS_build_sea ("sea.png");
  WRMS_wind_rand ();
  WRMS_hud_init ();

  WRMS_arrow_reset ();

  WRMS_visualizer_leave ();

  return 0;
}

void
WRMS_rules_clear ()
{
  MG_remove_list ((game_rules.initial_worms), NULL);
  MG_remove_list ((game_rules.teams), (void (*)(void *)) WRMS_team_destroy);
  MG_remove_list ((game_rules.worms), NULL);
  memset (&game_rules, 0, sizeof (WRMS_game));
}

WRMS_team *
WRMS_get_win_team ()
{
  if (!MG_list_length (game_rules.teams))
    {
      return NULL;
    }

  return (WRMS_team *) game_rules.teams->value;
}

SPRITE *
WRMS_rules_swap_players ()
{
  SPRITE *active_player;
  WRMS_team *active_team;

  clear_empty_teams ();

  game_rules.time_used = 0;
  game_rules.waiting_for_detonation_p = 0;

  active_team =
    (WRMS_team *) MG_remove_node (&game_rules.teams, game_rules.teams->value);

  printf ("RULES: old team %s\n", active_team->team_name);

  if (WRMS_team_players_count (active_team) > 0)
    {
      MG_add (&game_rules.teams, (void *) active_team);

      /* remove first item in team */
      active_player = (SPRITE *) MG_remove_node (&(active_team->teammates),
						 active_team->teammates->
						 value);

      /* add the first item to end */
      MG_add (&(active_team->teammates), active_player);
      WRMS_worm_play_done (active_player);
    }
  else
    {
      WRMS_team_destroy (active_team);
    }

  WRMS_ai_reset ();
  WRMS_wind_rand ();

  return NULL;
}

int
WRMS_rules_game_done_p ()
{
  int not_zero_teams_count = 0;
  LIST *teams = game_rules.teams;
  WRMS_team *team;

  while (teams)
    {
      team = (WRMS_team *) teams->value;
      not_zero_teams_count += (WRMS_team_players_count (team) > 0 ? 1 : 0);
      teams = teams->node;
    }

  return (not_zero_teams_count <= 1 ? 1 : 0);
}

WRMS_team *
WRMS_rules_active_team ()
{
  return (WRMS_team *) game_rules.teams->value;
}

SPRITE *
WRMS_rules_active_worm ()
{
  if (!game_rules.teams)
    return NULL;

  WRMS_team *t = (WRMS_team *) game_rules.teams->value;

  if (!t || !t->teammates)
    return NULL;

  return (SPRITE *) t->teammates->value;
}

void
WRMS_rules_register_player (SPRITE * worm)
{
  MG_add (&(game_rules.worms), (void *) worm);
}

void
WRMS_rules_unregister_player (SPRITE * player)
{
  MG_remove_node (&(game_rules.worms), (void *) player);
}

LIST *
WRMS_rules_worms ()
{
  return game_rules.worms;
}

void
WRMS_rules_refresh ()
{
  if (game_rules.time_used >= TIME_LIMIT)
    {
      printf ("RULES: time limit reached\n");
      WRMS_rules_swap_players ();
      WRMS_visualizer_enter ();
      WRMS_visualizer_leave ();
      game_rules.time_used = 0;
    }
}

int
WRMS_rules_can_play_p (SPRITE * player)
{
  if (WRMS_rules_game_done_p ())
    {
      return 0;
    }

  if (WRMS_rules_active_worm () == player)
    {
      return 1;
    }
  return 0;
}

void
WRMS_rules_increase_worm_time (SPRITE * worm)
{
  if (WRMS_rules_active_worm () == worm)
    {
      game_rules.time_used += MG_get_system_dt ();
    }
}

int
WRMS_rules_decrease_ammo_count (SPRITE * player)
{
  SPRITE *gun = WRMS_worm_get_current_gun (player);
  WRMS_gun_desc *wd = WRMS_get_gun_desc (gun);

  return (wd->ammo_left ? --wd->ammo_left : 0);
}

int
WRMS_rules_can_fire_p (SPRITE * player)
{
  if (game_rules.waiting_for_detonation_p)
    return 0;

  SPRITE *gun = WRMS_worm_get_current_gun (player);
  WRMS_gun_desc *wd = WRMS_get_gun_desc (gun);

  return wd->ammo_left;
}

int
WRMS_rules_get_damage (SPRITE * self)
{
  WRMS_worm_player *p = WRMS_worm_get_player (self);
  return (p->got_damage);
}

int
WRMS_rules_give_damage (SPRITE * attacker, SPRITE * victim, int hp)
{
  WRMS_worm_player *p = WRMS_worm_get_player (victim);
  return (p->got_damage = hp);
}

int
WRMS_rules_compute_health (SPRITE * victim)
{
  WRMS_worm_player *p = WRMS_worm_get_player (victim);
  p->health -= p->got_damage;

  if (p->health < 0)
    {
      p->health = 0;
    }

  p->got_damage = 0;

  return p->health;
}
