/* gcompris - imageid.c
 *
 * Copyright (C) 2001, 2008 Pascal Georges
 *
 *   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 .
 */
#include 
#include "gcompris/gcompris.h"
/* libxml includes */
#include 
#include 
#define SOUNDLISTFILE PACKAGE
static GcomprisBoard *gcomprisBoard = NULL;
static gboolean board_paused = TRUE;
static void		 start_board (GcomprisBoard *agcomprisBoard);
static void		 pause_board (gboolean pause);
static void		 end_board (void);
static gboolean		 is_our_board (GcomprisBoard *gcomprisBoard);
static void		 set_level (guint level);
static int gamewon;
static void		 process_ok(void);
static void		 highlight_selected(GooCanvasItem *);
static void		 game_won(void);
static void		 config_start(GcomprisBoard *agcomprisBoard,
				      GcomprisProfile *aProfile);
static void		 config_stop(void);
#define MAX_PROPOSAL 6
typedef struct {
  gchar  *pixmapfile;
  gchar  *text[MAX_PROPOSAL + 1];
  guint solution; /* Index to solution in text[] */
} Board;
/* XML */
static void		 init_xml(guint level);
static void		 add_xml_data(xmlDocPtr doc,xmlNodePtr xmlnode, GNode * child);
static void		 parse_doc(xmlDocPtr doc);
static gboolean		 read_xml_file(char *fname);
static void		 destroy_board_list(void);
static void		 destroy_board(Board * board);
/* This is the list of boards */
static GList *board_list = NULL;
#define HORIZONTAL_SEPARATION 30
// the values are taken from the backgound image imageid-bg.jpg
#define IMAGE_AREA_X1 280
#define IMAGE_AREA_X2 750
#define IMAGE_AREA_Y1 90
#define IMAGE_AREA_Y2 430
#define TEXT_COLOR "white"
/* ================================================================ */
static int board_number; // between 0 and board_list.length-1
static int right_word; // indicates which choice is the right one
static GooCanvasItem *selected_button;
static GooCanvasItem *boardRootItem = NULL;
static void imageid_create_item(GooCanvasItem *parent);
static void imageid_destroy_all_items(void);
static void imageid_next_level(void);
static gboolean item_event (GooCanvasItem  *item,
			    GooCanvasItem  *target,
			    GdkEventButton *event,
			    gpointer data);
/* Description of this plugin */
static BoardPlugin menu_bp =
  {
    NULL,
    NULL,
    N_("Reading"),
    N_("Learn how to read"),
    "Pascal Georges pascal.georges1@free.fr>",
    NULL,
    NULL,
    NULL,
    NULL,
    start_board,
    pause_board,
    end_board,
    is_our_board,
    NULL,
    process_ok,
    set_level,
    NULL,
    NULL,
    config_start,
    config_stop
  };
/*
 * Main entry point mandatory for each Gcompris's game
 * ---------------------------------------------------
 *
 */
GET_BPLUGIN_INFO(imageid)
/*
 * in : boolean TRUE = PAUSE : FALSE = CONTINUE
 *
 */
static void
pause_board (gboolean pause)
{
  if(gcomprisBoard==NULL)
    return;
  if(gamewon == TRUE && pause == FALSE) /* the game is won */
    {
      game_won();
    }
  board_paused = pause;
}
/*
 */
static void
start_board (GcomprisBoard *agcomprisBoard)
{
  gchar *filename = NULL;
  GHashTable *config = gc_db_get_board_conf();
  gc_locale_set(g_hash_table_lookup( config, "locale"));
  g_hash_table_destroy(config);
  if(agcomprisBoard!=NULL)
    {
      gcomprisBoard=agcomprisBoard;
      gc_set_background(goo_canvas_get_root_item(gcomprisBoard->canvas),
			"imageid/imageid-bg.svgz");
      gcomprisBoard->level=1;
      gcomprisBoard->sublevel=1;
      /* Calculate the maxlevel based on the available data file for this board */
      gcomprisBoard->maxlevel=1;
      while( (filename = gc_file_find_absolute("%s/board%d.xml",
					       gcomprisBoard->boarddir,
					       gcomprisBoard->maxlevel++,
					       NULL)) )
	{
	  g_free(filename);
	}
      g_free(filename);
      gcomprisBoard->maxlevel -= 2;
      gc_bar_set(GC_BAR_CONFIG|GC_BAR_LEVEL);
      imageid_next_level();
      gamewon = FALSE;
      pause_board(FALSE);
    }
}
static void
end_board ()
{
  if(gcomprisBoard!=NULL)
    {
      pause_board(TRUE);
      gc_score_end();
      imageid_destroy_all_items();
      destroy_board_list();
    }
  gc_locale_set( NULL );
  gcomprisBoard = NULL;
}
static void
set_level (guint level)
{
  if(gcomprisBoard!=NULL)
    {
      gcomprisBoard->level=level;
      gcomprisBoard->sublevel=1;
      imageid_next_level();
    }
}
static gboolean
is_our_board (GcomprisBoard *gcomprisBoard)
{
  if (gcomprisBoard)
    {
      if(g_ascii_strcasecmp(gcomprisBoard->type, "imageid")==0)
	{
	  /* Set the plugin entry */
	  gcomprisBoard->plugin=&menu_bp;
	  return TRUE;
	}
    }
  return FALSE;
}
/*-------------------------------------------------------------------------------*/
/*-------------------------------------------------------------------------------*/
/* set initial values for the next level */
static void
imageid_next_level()
{
  gc_bar_set_level(gcomprisBoard);
  imageid_destroy_all_items();
  selected_button = NULL;
  gamewon = FALSE;
  destroy_board_list();
  init_xml(gcomprisBoard->level);
  gcomprisBoard->number_of_sublevel = g_list_length(board_list);
  gc_score_end();
  gc_score_start(SCORESTYLE_NOTE,
		 BOARDWIDTH - 195,
		 BOARDHEIGHT - 30,
		 gcomprisBoard->number_of_sublevel);
  gc_score_set(gcomprisBoard->sublevel);
  /* Try the next level */
  imageid_create_item(goo_canvas_get_root_item(gcomprisBoard->canvas));
}
static void
imageid_next_sublevel()
{
  imageid_destroy_all_items();
  selected_button = NULL;
  gamewon = FALSE;
  gc_score_set(gcomprisBoard->sublevel);
  /* Try the next level */
  imageid_create_item(goo_canvas_get_root_item(gcomprisBoard->canvas));
}
/* ==================================== */
/* Destroy all the items */
static void imageid_destroy_all_items()
{
  if(boardRootItem!=NULL)
    goo_canvas_item_remove(boardRootItem);
  boardRootItem = NULL;
}
/* ==================================== */
static void
imageid_create_item(GooCanvasItem *parent)
{
  GooCanvasItem *item;
  GooCanvasItem *group;
  GooCanvasItem *button;
  int x, y, xp, yp;
  guint vertical_separation;
  GdkPixbuf *pixmap = NULL;
  Board *board;
  guint i;
  board_number = gcomprisBoard->sublevel-1;
  g_assert(board_number >= 0  && board_number < g_list_length(board_list));
  boardRootItem = \
    goo_canvas_group_new (goo_canvas_get_root_item(gcomprisBoard->canvas),
			  NULL);
  /* display the image */
  board = g_list_nth_data(board_list, board_number);
  g_assert(board != NULL);
  right_word = board->solution;
  pixmap = gc_pixmap_load(board->pixmapfile);
  x = IMAGE_AREA_X1 + ( IMAGE_AREA_X2 - IMAGE_AREA_X1 - gdk_pixbuf_get_width(pixmap))/2;
  y = IMAGE_AREA_Y1 + ( IMAGE_AREA_Y2 - IMAGE_AREA_Y1 - gdk_pixbuf_get_height(pixmap))/2;
  item = goo_canvas_image_new (boardRootItem,
			       pixmap,
			       x,
			       y,
			       NULL);
#if GDK_PIXBUF_MAJOR <= 2 && GDK_PIXBUF_MINOR <= 24
  gdk_pixbuf_unref(pixmap);
#else
  g_object_unref(pixmap);
#endif
  xp = HORIZONTAL_SEPARATION;
  /* Calc the number of proposals */
  i = 0;
  while(board->text[i++]);
  vertical_separation = 10 + 60 / i;
  guint button_pixmap_height = 48;
  guint button_pixmap_width = 191;
  yp = (BOARDHEIGHT -
	i *  button_pixmap_height
	- 2 * vertical_separation)/2 - 20;
  i = 0;
  while(board->text[i])
    {
      group = goo_canvas_group_new (boardRootItem,
				    NULL);
      button = goo_canvas_svg_new (group,
				   gc_skin_rsvg_get(),
				   "svg-id", "#BUTTON_TEXT",
				   NULL);
      SET_ITEM_LOCATION(button,
			xp,
			yp);
      g_object_set_data(G_OBJECT(group),
		      "button", button);
      g_signal_connect(button, "button_press_event",
		       (GCallback) item_event,
		       GINT_TO_POINTER(i));
      item = goo_canvas_text_new (group,
				  board->text[i],
				  xp +  button_pixmap_width/2 + 1.0,
				  yp +  button_pixmap_height/2 + 1.0,
				  -1,
				  GTK_ANCHOR_CENTER,
				  "font", gc_skin_font_board_big,
				  "fill_color_rgba", gc_skin_color_shadow,
				  NULL);
      g_signal_connect(item, "button_press_event",
		       (GCallback) item_event,
		       GINT_TO_POINTER(i));
      item = goo_canvas_text_new (group,
				  board->text[i],
				  xp +  button_pixmap_width/2,
				  yp +  button_pixmap_height/2,
				  -1,
				  GTK_ANCHOR_CENTER,
				  "font", gc_skin_font_board_big,
				  "fill_color_rgba", gc_skin_color_text_button,
				  NULL);
      g_signal_connect(item, "button_press_event",
		       (GCallback) item_event,
		       GINT_TO_POINTER(i));
      yp +=  button_pixmap_height + vertical_separation;
      i++;
    }
}
/* ==================================== */
static void game_won()
{
  gcomprisBoard->sublevel++;
  if(gcomprisBoard->sublevel > gcomprisBoard->number_of_sublevel)
    {
      /* Try the next level */
      gcomprisBoard->sublevel=1;
      gcomprisBoard->level++;
      if(gcomprisBoard->level>gcomprisBoard->maxlevel)
	gcomprisBoard->level = gcomprisBoard->maxlevel;
      imageid_next_level();
    }
  else
    imageid_next_sublevel();
}
/* ==================================== */
static gboolean
process_ok_timeout()
{
  gc_bonus_display(gamewon, GC_BONUS_SMILEY);
  return FALSE;
}
static void process_ok() {
  // leave time to display the right answer
  g_timeout_add(TIME_CLICK_TO_BONUS, process_ok_timeout, NULL);
}
/* ==================================== */
static gboolean
item_event (GooCanvasItem  *item,
	    GooCanvasItem  *target,
	    GdkEventButton *event,
	    gpointer data)
{
  gint button_id = GPOINTER_TO_INT(data);
  GooCanvasItem *button;
  if(board_paused)
    return FALSE;
  switch (event->type)
    {
    case GDK_BUTTON_PRESS:
      board_paused = TRUE;
      if ( button_id == right_word )
	gamewon = TRUE;
      else
	gamewon = FALSE;
      button = (GooCanvasItem*)g_object_get_data(G_OBJECT(goo_canvas_item_get_parent(item)),
						   "button");
      highlight_selected(button);
      process_ok();
      break;
    default:
      break;
    }
  return FALSE;
}
/* ==================================== */
static void
highlight_selected(GooCanvasItem * button)
{
  if (selected_button != NULL && selected_button != button)
    {
      g_object_set(selected_button,
		   "svg-id", "#BUTTON_TEXT",
		   NULL);
    }
  if (selected_button != button)
    {
      g_object_set(button,
		   "svg-id", "#BUTTON_TEXT_SELECTED",
		   NULL);
      selected_button = button;
    }
}
/* ===================================
 *                XML stuff
 *                Ref : shapegame.c
 * ==================================== */
static void
init_xml(guint level)
{
  gchar *filename;
  filename = gc_file_find_absolute("%s/board%d.xml",
				   gcomprisBoard->boarddir,
				   level);
  g_assert(read_xml_file(filename)== TRUE);
  g_free(filename);
}
/* ==================================== */
static void
add_xml_data(xmlDocPtr doc, xmlNodePtr xmlnode, GNode *child)
{
  Board * board = g_new0(Board, 1);
  guint text_index = 0;
  xmlnode = xmlnode->xmlChildrenNode;
  while( (xmlnode = xmlnode->next) != NULL)
    {
      if (!strcmp((char *)xmlnode->name, "pixmapfile"))
	board->pixmapfile =						\
	  (gchar *)xmlNodeListGetString(doc, xmlnode->xmlChildrenNode, 1);
      if (!strcmp((char *)xmlnode->name, "good"))
	{
	  board->solution = text_index;
	  board->text[text_index++] =					\
	    g_strdup(gettext((gchar *)xmlNodeListGetString(doc, xmlnode->xmlChildrenNode, 1)));
	}
      if (!strcmp((char *)xmlnode->name, "bad"))
	board->text[text_index++] =					\
	  g_strdup(gettext((gchar *)xmlNodeListGetString(doc, xmlnode->xmlChildrenNode, 1)));
      if(text_index > MAX_PROPOSAL)
	{
	  gc_dialog(_("Data file for this level is not properly formatted. Too many choices are proposed."),
		    gc_board_stop);
	  g_free(board);
	  return;
	}
    }
  /* Check there is enough information to play it */
  if ( board->pixmapfile == NULL
       || board->text[0] == NULL
       || board->text[1] == NULL )
    {
      gc_dialog(_("Data file for this level is not properly formatted."),
		gc_board_stop);
      g_free(board);
      return;
    }
  /* Randomize the set */
  {
    guint c = text_index * 2;
    gchar *text;
    while(c--)
      {
	guint i1 = g_random_int_range(0, text_index);
	guint i2 = g_random_int_range(0, text_index);
	/* Swap entries */
	text = board->text[i1];
	board->text[i1] = board->text[i2];
	board->text[i2] = text;
	if(i1 == board->solution)
	  board->solution = i2;
	else if(i2 == board->solution)
	  board->solution = i1;
      }
  }
  /* Insert boards randomly in the list */
  if(g_random_int_range(0, 2))
    board_list = g_list_append (board_list, board);
  else
    board_list = g_list_prepend (board_list, board);
}
/* ==================================== */
static void
parse_doc(xmlDocPtr doc)
{
  xmlNodePtr node;
  for(node = doc->children->children; node != NULL; node = node->next) {
    if ( g_ascii_strcasecmp((gchar *)node->name, "Board") == 0 )
      add_xml_data(doc, node, NULL);
  }
}
/* ==================================== */
/* read an xml file into our memory structures and update our view,
   dump any old data we have in memory if we can load a new set */
static gboolean
read_xml_file(char *fname)
{
  /* pointer to the new doc */
  xmlDocPtr doc;
  g_return_val_if_fail(fname!=NULL, FALSE);
  /* parse the new file and put the result into newdoc */
  doc = xmlParseFile(fname);
  /* in case something went wrong */
  if(!doc)
    return FALSE;
  if(/* if there is no root element */
     !doc->children ||
     /* if it doesn't have a name */
     !doc->children->name ||
     /* if it isn't a ImageId node */
     g_ascii_strcasecmp((char *)doc->children->name,"ImageId")!=0) {
    xmlFreeDoc(doc);
    return FALSE;
  }
  parse_doc(doc);
  xmlFreeDoc(doc);
  return TRUE;
}
/* ======================================= */
static void
destroy_board_list()
{
  Board *board;
  while(g_list_length(board_list)>0)
    {
      board = g_list_nth_data(board_list, 0);
      board_list = g_list_remove (board_list, board);
      destroy_board(board);
    }
}
/* ======================================= */
static void
destroy_board(Board * board)
{
  guint i = 0;
  g_free(board->pixmapfile);
  while(board->text[i])
    g_free(board->text[i++]);
  g_free(board);
}
/* ************************************* */
/* *            Configuration          * */
/* ************************************* */
/* ======================= */
/* = config_start        = */
/* ======================= */
static GcomprisProfile *profile_conf;
static GcomprisBoard   *board_conf;
/* GHFunc */
static void save_table (gpointer key,
			gpointer value,
			gpointer user_data)
{
  gc_db_set_board_conf ( profile_conf,
			    board_conf,
			    (gchar *) key,
			    (gchar *) value);
}
static gboolean conf_ok(GHashTable *table)
{
  if (!table){
    if (gcomprisBoard)
      pause_board(FALSE);
    return TRUE;
  }
  g_hash_table_foreach(table, (GHFunc) save_table, NULL);
  if (gcomprisBoard) {
    GHashTable *config;
    if (profile_conf)
      config = gc_db_get_board_conf();
    else
      config = table;
    gc_locale_set(g_hash_table_lookup( config, "locale"));
    if (profile_conf)
      g_hash_table_destroy(config);
    destroy_board_list();
    init_xml(gcomprisBoard->level);
    imageid_next_level();
    pause_board(FALSE);
  }
  board_conf = NULL;
  profile_conf = NULL;
  return TRUE;
}
static void
config_start(GcomprisBoard *agcomprisBoard,
	     GcomprisProfile *aProfile)
{
  board_conf = agcomprisBoard;
  profile_conf = aProfile;
  if (gcomprisBoard)
    pause_board(TRUE);
  gchar *label = g_strdup_printf(_("%s configuration\n for profile %s"),
				 agcomprisBoard->name,
				 aProfile ? aProfile->name : "");
  GcomprisBoardConf *bconf;
  bconf = gc_board_config_window_display( label,
				 conf_ok);
  g_free(label);
  /* init the combo to previously saved value */
  GHashTable *config = gc_db_get_conf( profile_conf, board_conf);
  gchar *locale = g_hash_table_lookup( config, "locale");
  gc_board_config_combo_locales(bconf, locale);
}
/* ======================= */
/* = config_stop        = */
/* ======================= */
static void
config_stop()
{
}