/*
    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 "stdio.h"
#include "string.h"
#include "MG_lib.h"
#include "WRMS_include.h"
#include "WRMS_utils.h"
#include "gui.h"
#include "stages.h"

/* menu impl */
SPRITE *bckg = NULL;
SPRITE *_active_dialog = NULL;

#define ALIGN_LEFT 300
#define ALIGN_TOP 20

/* when game ends: */
static void
menu_set_current_dialog (SPRITE * dialog)
{
  if (_active_dialog)
    MG_remove_sprite (_active_dialog);

  _active_dialog = dialog;
}

static void
on_end_done (SPRITE * self)
{
  _active_dialog = NULL;
}

static SPRITE *
WRMS_end_game_dialog (char *game_message)
{
  SPRITE *dialog = WRMS_dialog_error (".: END GAME :.", game_message);
  _active_dialog = NULL;	/* must been destroyed yet */
  menu_set_current_dialog (dialog);

  free (game_message);

  MG_install_callback (dialog, "on_error_dialog_accept", on_end_done);

  return dialog;
}

static char *
env_path (const char *env, const char *path)
{
  char *got_env = strdup (getenv (env));

  if (!got_env)			/* windows problem */
    {
      got_env = strdup (".");
    }

  char *full_path =
    (char *) malloc ((strlen (got_env) + strlen (path) + 2) * sizeof (char));

  strcpy (full_path, got_env);
  strcat (full_path, path);

  free (got_env);

  return full_path;
}

static int
hash_semantic_check (HASH * h)
{
  HASH *teamsh = MG_hash_get_key (h, ":teams");
  if (!teamsh)
    return 0;

  LIST *teams = teamsh->values;

  if (!teams)
    return 0;

  while (teams)
    {
      HASH *team = (HASH *) teams->value;
      if (!team)
	return 0;

      HASH *name = MG_hash_get_key (team, ":name");
      if (!name || !name->values)
	return 0;

      if (strlen ((char *) name->values) > WRMS_TEAM_MAX_CHARS)
	return 0;

      HASH *type = MG_hash_get_key (team, ":type");
      if (!type || !type->values)
	return 0;
      if (strcmp ((char *) type->values, "HUMAN")
	  && strcmp ((char *) type->values, "AI"))
	return 0;

      HASH *players = MG_hash_get_key (team, ":teammates");
      if (!players || !players->values)
	return 0;

      int len = MG_list_length (players->values);
      if (len != TEAMMATES_MAX_COUNT)
	return 0;

      LIST *players_it = players->values;
      while (players_it)
	{
	  HASH *plrh = (HASH *) players_it->value;
	  if (!plrh || !plrh->values)
	    return 0;

	  if (strlen ((char *) plrh->values) > WRMS_TEAM_MAX_CHARS)
	    return 0;

	  players_it = players_it->node;
	}

      teams = teams->node;
    }

  return 1;			/* semantic check passed */
}

int
WRMS_conf_write (HASH * h, const char *path)
{
  if (!h || !hash_semantic_check (h))
    {
      printf ("semantic check not passed\n");
      return 0;
    }

  char *p = (path ? strdup (path) : NULL);

  if (!p)
    p = env_path ("HOME", CONF_PATH);

  char *dump = MG_hash_dump (h);

  FILE *out = fopen (p, "w+");
  free (p);

  if (!out)
    return 0;

  if (!fwrite (dump, sizeof (char), strlen (dump), out))
    {
      fclose (out);
      return 0;
    }

  fclose (out);
  return 1;
}

int
WRMS_conf_force_implicit (const char *path)
{
  const char *text = IMPLICIT_CONF;

  FILE *conf_file = fopen (path, "w+");

  if (!conf_file)
    {
      printf ("warn: file could not been written!\n");
      return 0;			/* damn */
    }

  int res = fwrite (text, sizeof (char), strlen (text), conf_file);
  if (!res)
    {
      printf ("warn: file writing failed - disk error?\n");
      fclose (conf_file);
      return 0;
    }

  fclose (conf_file);
  return 1;
}

HASH *
WRMS_get_conf (const char *path)
{
  char *p = (path ? strdup (path) : NULL);
  if (!path)
    {
      p = env_path ("HOME", CONF_PATH);
    }

  char *txt = file_read (p);
  HASH *out = NULL;

  if (!txt)
    {
      WRMS_conf_force_implicit (p);
      free (p);
      /* This is consistent every time */
      return MG_text_list (IMPLICIT_CONF);
    }
  else
    {
      out = MG_text_list (txt);
      free (txt);

      if (!out || !hash_semantic_check (out))
	{
	  if (out)
	    {
	      MG_hash_free (out);
	      out = NULL;
	    }

	  if (!WRMS_conf_force_implicit (p))	/* forcing went wrong */
	    {
	      /* TODO: inform user about disk is not responsible */
	      free (p);
	      return MG_text_list (IMPLICIT_CONF);
	    }
	  free (p);
	  /* forcing went ok so load it again */
	  return WRMS_get_conf (path);
	}
      free (p);
    }

  return out;
}

void on_team_select (SPRITE * self);

static void
on_new_game_destroy (SPRITE * self)
{
  HASH *config = MG_get_key (self, "config");
  if (config)
    MG_hash_free (config);

  LIST *teams = MG_get_key (self, "selected_worms");
  MG_remove_list (teams, NULL);
}

static void
on_team_uncheck (SPRITE * self)
{
  SPRITE *dialog_body = MG_get_key (self, "parent");
  SPRITE *dialog = MG_get_key (dialog_body, "parent");
  LIST *teams = MG_get_key (dialog, "selected_worms");

  MG_remove_node (&teams, MG_get_key (self, "team_conf"));
  MG_set_key (dialog, "selected_worms", teams);
  SPRITE *chk = MG_get_key (self, "check_item");

  WRMS_widget_unbind (self, chk);
  MG_remove_sprite (chk);
  MG_set_key (self, "check_item", NULL);

  MG_install_callback (self, "on_click", on_team_select);
}

void
on_team_select (SPRITE * self)
{
  SPRITE *dialog_body = MG_get_key (self, "parent");
  SPRITE *dialog = MG_get_key (dialog_body, "parent");
  LIST *teams = MG_get_key (dialog, "selected_worms");

  int len = MG_list_length (teams);

  if (len >= TEAM_MAX_COUNT)	/* no more team can be selected */
    return;

  MG_add (&teams, MG_get_key (self, "team_conf"));
  MG_set_key (dialog, "selected_worms", teams);

  SPRITE *check = WRMS_widget_make (MG_get_img ("check.bmp"), NULL, NULL);
  MG_set_key (self, "check_item", check);
  WRMS_widget_bind (self, check);

  MG_move_sprite (check, self->dest.x + self->dest.w, 0);
  MG_install_callback (self, "on_click", on_team_uncheck);
}

static void
on_game_enter (SPRITE * self)
{
  SPRITE *dialog_buttons = MG_get_key (self, "parent");
  SPRITE *dialog = MG_get_key (dialog_buttons, "parent");
  LIST *teams = MG_get_key (dialog, "selected_worms");

  if (MG_list_length (teams) < 2)
    {
      //SPRITE * nfo = WRMS_dialog_error( "INFO:" , "Not enough players selected!\n" );
      //MG_move_sprite( nfo , ALIGN_LEFT , ALIGN_TOP ); 
      return;			/* not enough teams selected! */
    }

  /* make a copy */
  NODE *arg = NULL;

  while (teams)
    {
      MG_add (&arg, MG_hash_copy (teams->value));
      teams = teams->node;
    }

  WRMS_stage_set_args (arg);
  WRMS_enter_stage (STAGE_INGAME);
}

static void
on_new_game (SPRITE * self)
{
  SPRITE *dialog = WRMS_dialog_make (".: SELECT TEAMS :.", DIALOG_WIDTH, 600);
  menu_set_current_dialog (dialog);

  HASH *conf = WRMS_get_conf (NULL);
  printf ("new game created\n");
  HASH *teams_hash = MG_hash_get_key (conf, ":teams");

  MG_set_key (dialog, "selected_worms", NULL);
  MG_set_key (dialog, "config", conf);

  if (teams_hash->type != THASH)
    {
      printf ("warn: bad conf file\n");
      return;
    }

  LIST *teams = teams_hash->values;
  int teams_id = 0;

  if (MG_list_length (teams) > MENU_MAX_TEAMS)
    {
      printf ("warn: too much teams - cutting!!\n");
    }

  while (teams && (teams_id < MENU_MAX_TEAMS))
    {
      HASH *t = (HASH *) teams->value;

      char *team_name = (char *) MG_hash_get_key (t, ":name")->values;
      if (!team_name)
	team_name = (char *) "untitled_team";

      SPRITE *button =
	WRMS_button_make (team_name, NULL, WRMS_FONT_MENU_COLOR);
      MG_set_key (button, "team_conf", t);
      MG_install_callback (button, "on_click", on_team_select);

      WRMS_widget_bind (WRMS_dialog_body (dialog), button);

      teams_id++;
      teams = teams->node;
    }

  MG_move_sprite (dialog, DIALOG_ALIGN_LEFT (dialog),
		  DIALOG_ALIGN_TOP (dialog));

  SPRITE *proceed_button =
    WRMS_button_make ("Start game >>", MG_get_img ("proceed.bmp"),
		      WRMS_FONT_MENU_COLOR);
  MG_install_callback (proceed_button, "on_click", on_game_enter);
  SPRITE *button_bar = WRMS_dialog_buttons (dialog);

  WRMS_widget_bind (button_bar, proceed_button);
  WRMS_widget_center (button_bar, proceed_button);

  MG_install_callback (dialog, "on_dialog_destroy", on_new_game_destroy);
}

static void
quit_game (SPRITE * self)
{
  WRMS_enter_stage (STAGE_CLOSING);
}

static void
clear_quit (SPRITE * yesno)
{
  menu_set_current_dialog (NULL);
}

static void
on_quit_game (SPRITE * self)
{
  SPRITE *dialog_yesno =
    WRMS_dialog_yesno (".: END GAME :.", "Are you sure?");

  MG_move_sprite (dialog_yesno, DIALOG_ALIGN_LEFT (dialog_yesno),
		  DIALOG_ALIGN_TOP (dialog_yesno));

  menu_set_current_dialog (dialog_yesno);

  MG_install_callback (dialog_yesno, "on_yes", quit_game);
  MG_install_callback (dialog_yesno, "on_no", clear_quit);
}

static void
on_credits_show (SPRITE * self)
{
  SPRITE *dialog = WRMS_dialog_make (".: CREDITS :.", DIALOG_WIDTH, 600);
  menu_set_current_dialog (dialog);
  MG_move_sprite (dialog, DIALOG_ALIGN_LEFT (dialog),
		  DIALOG_ALIGN_TOP (dialog));

  SPRITE *me = WRMS_widget_make (MG_get_img ("myface.png"), NULL, NULL);

  SPRITE *credits_text =
    WRMS_textarea_make
    ("Created by Pavel Procházka under GNU/GPLv3\nas bachelor thesis 2010-2011\nBASIC CONTROLLS: arrows right, left (move),\n arrows up, down (targetting), space (fire),\n enter (jump), esc (exit)");

  WRMS_widget_bind (WRMS_dialog_body (dialog), me);
  WRMS_widget_center (WRMS_dialog_body (dialog), me);
  MG_move_sprite (me, 0, -52);

  WRMS_widget_bind (WRMS_dialog_body (dialog), credits_text);
  MG_move_sprite (credits_text,
		  WRMS_dialog_body (dialog)->dest.w / 2 -
		  credits_text->dest.w / 2, 0);
}

/* teams editing */
static void
update_conf_hash (SPRITE * body_content, HASH * conf_part)
{
  LIST *attachments = body_content->attached_sprites;

  SPRITE *team_name_wdgt = MG_list_nth (attachments, 1);
  SPRITE *team_type = MG_list_nth (attachments, 3);
  SPRITE *worms[TEAMMATES_MAX_COUNT];

  int start_worm_idx = 5;
  for (; start_worm_idx < 5 + TEAMMATES_MAX_COUNT; start_worm_idx++)
    {
      worms[start_worm_idx - 5] = MG_list_nth (attachments, start_worm_idx);
    }

  HASH *team_name = MG_hash_get_key (conf_part, ":name");
  free (team_name->values);
  team_name->values =
    (LIST *) strdup (WRMS_edit_box_get_text (team_name_wdgt));

  HASH *team_type_hsh = MG_hash_get_key (conf_part, ":type");
  free (team_type_hsh->values);
  team_type_hsh->values =
    (LIST *) strdup (MG_get_key (team_type, "team_type_state"));

  HASH *worms_list = MG_hash_get_key (conf_part, ":teammates");
  LIST *l = worms_list->values;

  int i = 0;
  for (; (i < TEAMMATES_MAX_COUNT) && l; i++)
    {
      HASH *worm_hsh = l->value;
      char *str = WRMS_edit_box_get_text (worms[i]);
      char *old_str = (char *) worm_hsh->values;

      if (strcmp (str, old_str))
	{
	  free (old_str);
	  worm_hsh->values = (LIST *) strdup (str);
	}
      l = l->node;
    }
}

static void
on_type_swap (SPRITE * button)
{
  char *intelligence_type = MG_get_key (button, "team_type_state");

  if (!intelligence_type)
    return;

  char *str_ai;

  if (!strcmp (intelligence_type, "AI"))
    str_ai = strdup ("HUMAN");
  else
    str_ai = strdup ("AI");

  MG_set_key (button, "team_type_state", str_ai);
  free (intelligence_type);

  SPRITE *text = MG_list_nth (button->attached_sprites, 0);
  WRMS_update_text (text, EDITABLE_TEXT_INACTIVE, 20, str_ai);
}

static void
fill_team_body (SPRITE * dialog)
{
  LIST *team_it = (LIST *) MG_get_key (dialog, "teams_iterator");
  HASH *team = team_it->value;
  char *str_name = (char *) (MG_hash_get_key (team, ":name")->values);
  SPRITE *body = WRMS_dialog_body (dialog);

  SPRITE *team_name =
    WRMS_text ("TEAM NAME: ", WRMS_FONT_MENU_COLOR, 16, WRMS_FONT);
  SPRITE *name_input = WRMS_edit_box_make (168, 20, WRMS_TEAM_MAX_CHARS);
  WRMS_edit_box_set_text (name_input, str_name);

  WRMS_widget_bind (body, team_name);

  WRMS_widget_bind (body, name_input);

  char *str_type = (char *) MG_hash_get_key (team, ":type")->values;
  SPRITE *team_type =
    WRMS_text ("TEAM TYPE: ", WRMS_FONT_MENU_COLOR, 16, WRMS_FONT);
  SPRITE *button_type =
    WRMS_button_make (str_type, NULL, EDITABLE_TEXT_INACTIVE);

  MG_install_callback (button_type, "on_click", on_type_swap);

  MG_set_key (button_type, "team_type_state", strdup (str_type));

  WRMS_widget_bind (body, team_type);
  WRMS_widget_bind (body, button_type);

  SPRITE *teammates_gtxt =
    WRMS_text ("teammates:", WRMS_FONT_MENU_COLOR, 16, WRMS_FONT);
  WRMS_widget_bind (body, teammates_gtxt);

  int i = 0;
  LIST *worms_list = MG_hash_get_key (team, ":teammates")->values;

  for (i = 0; (i < TEAMMATES_MAX_COUNT) && worms_list; i++)
    {
      HASH *worm_hash = worms_list->value;
      char *str_worm = (char *) worm_hash->values;

      SPRITE *input = WRMS_edit_box_make (168, 20, WRMS_TEAM_MAX_CHARS);
      WRMS_edit_box_set_text (input, str_worm);
      WRMS_widget_bind (body, input);
      MG_move_sprite (input, 128, 0);

      worms_list = worms_list->node;
    }
}

/* edit controllings */
static void
on_next_conf (SPRITE * self)
{
  SPRITE *container = WRMS_widget_parent (self);
  SPRITE *dialog = WRMS_widget_parent (container);
  SPRITE *body = WRMS_dialog_body (dialog);

  NODE *it = MG_get_key (dialog, "teams_iterator");

  update_conf_hash (body, (HASH *) it->value);

  if (it->node)
    {
      MG_set_key (dialog, "teams_iterator", it->node);
    }
  else
    {
      NODE *queue = MG_get_key (dialog, "teams_queue");
      MG_set_key (dialog, "teams_iterator", queue);
    }

  WRMS_container_clear (WRMS_dialog_body (dialog));
  fill_team_body (dialog);
}

static void
on_prev_conf (SPRITE * self)
{
  SPRITE *container = WRMS_widget_parent (self);
  SPRITE *dialog = WRMS_widget_parent (container);
  SPRITE *body = WRMS_dialog_body (dialog);

  NODE *it = MG_get_key (dialog, "teams_iterator");

  update_conf_hash (body, (HASH *) it->value);

  NODE *queue_it = MG_get_key (dialog, "teams_queue");
  LIST *queue = queue_it;
  NODE *prev = NULL;

  while (queue_it != it)
    {
      prev = queue_it;
      queue_it = queue_it->node;
    }

  if (!prev)
    {
      prev = queue;
      while (prev->node)
	{
	  prev = prev->node;
	}
    }

  MG_set_key (dialog, "teams_iterator", prev);

  WRMS_container_clear (WRMS_dialog_body (dialog));
  fill_team_body (dialog);
}

static void
on_config_destroy (SPRITE * self)
{
  HASH *conf = MG_get_key (self, "team_conf");
  MG_hash_free (conf);
}

static void
on_config_apply (SPRITE * self)
{
  SPRITE *dialog = self;
  SPRITE *body = WRMS_dialog_body (dialog);

  NODE *it = MG_get_key (dialog, "teams_iterator");
  update_conf_hash (body, (HASH *) it->value);

  HASH *conf = MG_get_key (dialog, "team_conf");
  LIST *teams = MG_get_key (dialog, "teams_queue");
  printf ("teams count: %d\n", MG_list_length (teams));
  int max_teams_p = (int) MG_get_key (dialog, "max_teams_p");

  if (!max_teams_p)
    {
      HASH *last = MG_list_nth (teams, MG_list_length (teams) - 1);

      HASH *untitled_t = MG_text_list (UNTITLED_TEAM);
      char *untitled_c = MG_hash_dump (untitled_t);
      MG_hash_free (untitled_t);

      char *last_c = MG_hash_dump (last);
      printf ("lastc : %s\n", last_c);

      if (!strcmp (last_c, untitled_c))
	{
	  /* user did not changed the untitled team */
	  MG_remove_node (&teams, last);
	  printf ("info: last not changed\n");
	}

      free (last_c);
      free (untitled_c);
    }

  if (!WRMS_conf_write (conf, NULL))
    printf ("err: ?!\n");
}

static void
on_config_accept (SPRITE * self)
{
  SPRITE *menu_bar = WRMS_widget_parent (self);
  SPRITE *dialog = WRMS_widget_parent (menu_bar);
  on_config_apply (dialog);
  menu_set_current_dialog (NULL);
}

static void
on_teams_edit (SPRITE * button)
{
  HASH *conf = WRMS_get_conf (NULL);
  int max_teams_reached_p = 0;

  if (!conf)
    {
      printf ("err: conf not available - probably memmory error!\n");
      return;
    }

  HASH *teams = MG_hash_get_key (conf, ":teams");
  if (MG_list_length (teams->values) < MENU_MAX_TEAMS)
    {
      HASH *untitled = MG_text_list (UNTITLED_TEAM);
      MG_add (&teams->values, untitled);
    }
  else
    {
      max_teams_reached_p = 1;
    }

  SPRITE *dialog =
    WRMS_dialog_make (".: TEAMS :.", DIALOG_WIDTH, DIALOG_HEIGHT);
  MG_set_key (dialog, "max_teams_p", (void *) max_teams_reached_p);
  menu_set_current_dialog (dialog);

  /* LOOK:
     ------------- title -------------
     .................................
     Team name: blah
     Team type: AI|HUMAN

     Worm1: wrm1
     Worm2: wrm2
     Worm3: wrm3
     Worm4: wrm4
     .................................
     <<             OK              >>
     ---------------------------------
   */

  MG_set_key (dialog, "team_conf", conf);

  HASH *teams_hash = MG_hash_get_key (conf, ":teams");
  LIST *it = teams_hash->values;
  NODE *teams_queue = it;

  MG_set_key (dialog, "teams_queue", teams_queue);
  MG_set_key (dialog, "teams_iterator", teams_queue);

  fill_team_body (dialog);

  SPRITE *prev_button =
    WRMS_button_make ("<< Previous", NULL, WRMS_FONT_MENU_COLOR);
  MG_install_callback (prev_button, "on_click", on_prev_conf);
  SPRITE *accept_button = WRMS_button_make ("OK", NULL, WRMS_FONT_MENU_COLOR);
  MG_install_callback (accept_button, "on_click", on_config_accept);
  SPRITE *next_button =
    WRMS_button_make ("Next >>", NULL, WRMS_FONT_MENU_COLOR);
  MG_install_callback (next_button, "on_click", on_next_conf);
  SPRITE *button_bar = WRMS_dialog_buttons (dialog);

  WRMS_widget_bind (button_bar, prev_button);
  WRMS_widget_bind (button_bar, accept_button);
  WRMS_widget_bind (button_bar, next_button);

  WRMS_widget_center (button_bar, accept_button);
  MG_sprite_set_position (next_button,
			  button_bar->dest.w - next_button->dest.w - 10,
			  next_button->dest.y);

  MG_move_sprite (dialog, DIALOG_ALIGN_LEFT (dialog),
		  DIALOG_ALIGN_TOP (dialog));

  MG_install_callback (dialog, "on_dialog_destroy", on_config_destroy);
}

SPRITE *
WRMS_menu_main_buttons ()
{
  SDL_Surface *bckg_img = MG_get_img ("button.png");

  SPRITE *container = WRMS_container_make (NULL, WRMS_CONTAINER_VERTICAL);
  SPRITE *to_play_button =
    WRMS_button_make ("PLAY", bckg_img, WRMS_FONT_MENU_COLOR);
  SPRITE *teams_selection =
    WRMS_button_make ("TEAMS", bckg_img, WRMS_FONT_MENU_COLOR);
  SPRITE *credits =
    WRMS_button_make ("CREDITS", bckg_img, WRMS_FONT_MENU_COLOR);
  SPRITE *quit = WRMS_button_make ("QUIT", bckg_img, WRMS_FONT_MENU_COLOR);

  WRMS_widget_bind (container, to_play_button);
  WRMS_widget_bind (container, teams_selection);
  WRMS_widget_bind (container, credits);
  WRMS_widget_bind (container, quit);

  MG_install_callback (to_play_button, "on_click", on_new_game);
  MG_install_callback (quit, "on_click", on_quit_game);
  MG_install_callback (credits, "on_click", on_credits_show);
  MG_install_callback (teams_selection, "on_click", on_teams_edit);

  MG_install_callback (quit, "on_quit", on_quit_game);

  return container;
}

int
WRMS_menu_enter (void *arg_stage)
{
  printf ("MENU: entering menu\n");
  _active_dialog = NULL;
  SDL_Rect r = screen_rect ();
  MACHINE *m = MG_get_machine ();
  m->view_rect.x = 0;
  m->view_rect.y = 0;

  bckg = WRMS_widget_make (NULL, NULL, "menu_bckg");
  MG_set_rect (bckg, r);

  SDL_Surface *sbackg =
    surface_scale (MG_get_img ("menu_background.png"), r.w, r.h);

  SPRITE *bckg_img = WRMS_widget_make (sbackg, NULL, "bckg_img");
  MG_install_callback (bckg_img, "on_widget_destroy", sprite_clear_surface);
  WRMS_widget_bind (bckg, bckg_img);

  SPRITE *buttons_container = WRMS_menu_main_buttons ();

  WRMS_widget_bind (bckg_img, buttons_container);
  MG_move_sprite (buttons_container, 4,
		  bckg_img->dest.h / 2 - buttons_container->dest.h / 2);

  if (arg_stage)
    {
      SPRITE *dialog = WRMS_end_game_dialog (arg_stage);
      MG_move_sprite (dialog, DIALOG_ALIGN_LEFT (dialog),
		      DIALOG_ALIGN_TOP (dialog));
    }

  return 0;
}
