/*  Core Wars.
 *  Copyright (C) 1999, 2000  Walter Hofmann
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License version 2 as 
 *  published by the Free Software Foundation.
 *
 *  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, write to the Free Software
 *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 */

#include <stdlib.h>
#include <gtk/gtk.h>
#include <string.h>
#include <dirent.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <math.h>

#include "config.h"
#include "main.h"
#include "main-gui.h"
#include "parser-cw.h"
#include "program.h"
#include "select.h"
#include "execute.h"
#include "execute-cw.h"
#include "execute-rc.h"
#include "statistic.h"
#include "pstatistic.h"
#include "options.h"
#include "tournament.h"
#include "dirbrowser.h"
#include "preferences.h"

GtkWidget *menu_file_start;
GtkWidget *menu_file_pause;
GtkWidget *menu_file_continue;
GtkWidget *menu_file_step;
GtkWidget *menu_file_stop;
GtkWidget *menu_view_gstatistic;
GtkWidget *menu_view_pstatistic;
GtkWidget *menu_tournament_start;
GtkWidget *menu_tournament_stop;

GtkWidget *main_window;
GtkWidget *main_vbox;
GtkWidget *main_drawing_area;
GdkDrawable *main_map;

GtkWidget *about_window = NULL;

char *itos (int i)
{
  static char s[32];

  sprintf (s, "%d", i);
  return s;
}

void menu_start_update ()
{
  struct program *p;
  int proceed = FALSE;

  p = program_list_root;
  while (p)
    {
      if (!p->error && p->load_count && p->language==LANGUAGE)
	proceed = TRUE;
      p = p->next;
    }
  gtk_widget_set (GTK_WIDGET (menu_file_start), "sensitive", proceed, NULL);
  gtk_widget_set (GTK_WIDGET (menu_tournament_start), "sensitive", proceed, NULL);
}

static gint main_configure (GtkWidget *widget, GdkEventConfigure *event)
{
  main_map = widget->window;
  execute_init ();
  return FALSE;
}

static gint main_expose (GtkWidget *widget, GdkEventExpose *event)
{
  int x1,y1,x2,y2;
  int i;

  x1 = (event->area.x-1) / 6;
  y1 = (event->area.y-1) / 6;
  x2 = (event->area.x+event->area.width-1) / 6;
  y2 = (event->area.y+event->area.height-1) / 6;
  for (i=0; i<SIZE; i++)
    if (x1<=i%DIMENSIONX && i%DIMENSIONX<=x2 &&
	y1<=i/DIMENSIONX && i/DIMENSIONY<=y2)
      mark (i, TRUE);
  
  x1 = 6*DIMENSIONX-6*((DIMENSIONX*DIMENSIONY)-SIZE)+1;
  x2 = 6*DIMENSIONX+1;
  y1 = 6*(DIMENSIONY-1)+1;
  y2 = 6*DIMENSIONY+1;
  gdk_gc_set_foreground (execute_gc, &black);
  gdk_draw_rectangle (main_map, execute_gc, TRUE, x1, y1, x2-x1, y2-y1); 

  return TRUE; /* Why do we need this??? */
}

void add_dir_handler (gchar *dir)
{
  add_program_directory (dir);
}

/* Load Directory */ 
static void file_loaddirectory (GtkWidget *w, gpointer data)
{
  char *dir;
  static GtkWidget *dir_browser = NULL;
  
  if (!dir_browser)
    {
#if defined(HAVE_GET_CURRENT_DIR_NAME)
      dir = get_current_dir_name();
#elif defined(PATH_MAX)
      dir = (char*)malloc(PATH_MAX);   /* FIXME: We leak memory. */
      if ( !dir ) fprintf(stderr,"Cannot allocate memory");
      getcwd (dir, PATH_MAX);
#else
# error: cannot find a way to get cwd
#endif

      dir_browser = create_dir_browser ("Load Directory", dir, GTK_SELECTION_SINGLE, add_dir_handler);
      gtk_signal_connect (GTK_OBJECT(dir_browser), "destroy", 
			  GTK_SIGNAL_FUNC(gtk_widget_destroyed), &dir_browser);
      gtk_widget_show(dir_browser);
    }
  else
    gdk_window_raise (dir_browser->window);
}

/* Start Programs */ 
static void file_start (GtkWidget *w, gpointer data)
{
  execute_start ();
}

/* Pause Programs */ 
static void file_pause (GtkWidget *w, gpointer data)
{
  execute_pause ();
}

/* Continue Programs */ 
static void file_continue (GtkWidget *w, gpointer data)
{
  execute_continue ();
}

/* Single Step Programs */ 
static void file_step (GtkWidget *w, gpointer data)
{
  if (LANGUAGE==LANGUAGE_CW)
    cw_execute_step ();
  else
    rc_execute_step ();
  statistic_update (FALSE);
}

/* Stop Programs */ 
static void file_stop (GtkWidget *w, gpointer data)
{
  execute_stop ();
}

/* Game statistic */ 
static void game_statistic (GtkWidget *w, gpointer data)
{
  statistic_view ();
}

/* Program statistic */ 
static void program_statistic (GtkWidget *w, gpointer data)
{
  pstatistic_view ();
}

/* About */ 
static void help_about (GtkWidget *w, gpointer data)
{
  GtkWidget *table;
  GtkWidget *label;
  GtkWidget *okbutton;

  if (!about_window)
    {
      about_window = gtk_dialog_new ();
      gtk_signal_connect (GTK_OBJECT (about_window), "destroy", 
			  GTK_SIGNAL_FUNC (gtk_widget_destroyed), &about_window);
      gtk_window_set_title (GTK_WINDOW (about_window), "About Core Wars");
      table = gtk_table_new (5, 3, FALSE);
      gtk_table_set_row_spacings (GTK_TABLE (table), 10);
      gtk_table_set_col_spacings (GTK_TABLE (table), 10);
      label = gtk_label_new ("Core Wars");
      gtk_table_attach_defaults (GTK_TABLE (table), label, 1, 2, 1, 2);
      gtk_widget_show (label);
      label = gtk_label_new ("Version " VERSION);
      gtk_table_attach_defaults (GTK_TABLE (table), label, 1, 2, 2, 3);
      gtk_widget_show (label);
      label = gtk_label_new ("Copyright 1999, 2000  Walter Hofmann <walterh@gmx.net>");
      gtk_table_attach_defaults (GTK_TABLE (table), label, 1, 2, 3, 4);
      gtk_widget_show (label);
      gtk_box_pack_start (GTK_BOX (GTK_DIALOG (about_window)->vbox),
			  table, TRUE, TRUE, 0);
      gtk_widget_show (table);
      okbutton = gtk_button_new_with_label( "Ok" );
      gtk_signal_connect_object (GTK_OBJECT (okbutton), "clicked", 
				 GTK_SIGNAL_FUNC (gtk_widget_destroy),
				 GTK_OBJECT (about_window)); 
      gtk_box_pack_start (GTK_BOX (GTK_DIALOG (about_window)->action_area),
			  okbutton, TRUE, TRUE, 0);
      gtk_widget_show (okbutton);
      gtk_window_set_position (GTK_WINDOW (about_window), GTK_WIN_POS_CENTER);
      gtk_widget_show (about_window);
    }
  else
    gdk_window_raise (about_window->window);
}

static gint handle_button_press (GtkWidget *widget, GdkEventButton *event)
{
  if (event->button == 1)
    statistic_select (event->x, event->y);
  return TRUE;
}

static gint handle_motion_notify (GtkWidget *widget, GdkEventMotion *event)
{
  int x, y;
  GdkModifierType state;
  
  if (event->is_hint)
    gdk_window_get_pointer (event->window, &x, &y, &state);
  else
    {
      x = event->x;
      y = event->y;
      state = event->state;
    }
  if (state & GDK_BUTTON1_MASK)
    statistic_select (x, y);
  return TRUE;
}

void create_drawing_area ()
{
  main_drawing_area = gtk_drawing_area_new ();
  gtk_signal_connect (GTK_OBJECT (main_drawing_area), "expose_event",
		      (GtkSignalFunc) main_expose, NULL);
  gtk_signal_connect (GTK_OBJECT(main_drawing_area),"configure_event",
		      (GtkSignalFunc) main_configure, NULL);
  gtk_drawing_area_size (GTK_DRAWING_AREA (main_drawing_area), 
			 6*DIMENSIONX+1, 6*DIMENSIONY+1);
  gtk_box_pack_start (GTK_BOX (main_vbox), main_drawing_area, FALSE, FALSE, 0);
  gtk_signal_connect (GTK_OBJECT (main_drawing_area), "motion_notify_event",
		      (GtkSignalFunc) handle_motion_notify, NULL);
  gtk_signal_connect (GTK_OBJECT (main_drawing_area), "button_press_event",
		      (GtkSignalFunc) handle_button_press, NULL);
  gtk_widget_set_events (main_drawing_area, GDK_BUTTON_PRESS_MASK | GDK_POINTER_MOTION_MASK);
  gtk_widget_show (main_drawing_area);
}

/* This is the GtkItemFactoryEntry structure used to generate new menus.
   Item 1: The menu path. The letter after the underscore indicates an
           accelerator key once the menu is open.
   Item 2: The accelerator key for the entry
   Item 3: The callback function.
   Item 4: The callback action.  This changes the parameters with
           which the function is called.  The default is 0.
   Item 5: The item type, used to define what kind of an item it is.
           Here are the possible values:

           NULL               -> "<Item>"
           ""                 -> "<Item>"
           "<Title>"          -> create a title item
           "<Item>"           -> create a simple item
           "<CheckItem>"      -> create a check item
           "<ToggleItem>"     -> create a toggle item
           "<RadioItem>"      -> create a radio item
           <path>             -> path of a radio item to link against
           "<Separator>"      -> create a separator
           "<Branch>"         -> create an item to hold sub items (optional)
           "<LastBranch>"     -> create a right justified branch 
*/

static GtkItemFactoryEntry menu_items[] = {
  { "/_File",                          NULL,         NULL, 0, "<Branch>" },
  { "/File/Load _Directory...",        "<control>D", file_loaddirectory, 0, NULL },
  { "/File/Select Progra_ms...",       "<control>M", file_selectprograms, 0, NULL },
  { "/File/_Start",                    "<control>S", file_start, 0, NULL },
  { "/File/_Pause",                    "<control>A", file_pause, 0, NULL },
  { "/File/_Continue",                 "<control>C", file_continue, 0, NULL },
  { "/File/Si_ngle Step",              "<control>N", file_step, 0, NULL },
  { "/File/Sto_p",                     "<control>P", file_stop, 0, NULL },
  { "/File/sep2",                      NULL,         NULL, 0, "<Separator>" },
  { "/File/_Quit",                     "<control>Q", gtk_main_quit, 0, NULL },
  { "/_View",                          NULL,         NULL, 0, "<Branch>" },
  { "/View/_Game Statistic...",        "<control>G", game_statistic, 0, NULL },
  { "/View/_Program Statistic...",     "<control>R", program_statistic, 0, NULL },
  { "/View/_Options...",               "<control>O", view_options, 0, NULL },
  { "/_Tournament",                    NULL,         NULL, 0, "<Branch>" },
  { "/Tournament/_Start...",           "<control>T", tournament_start, 0, NULL },
  { "/Tournament/Sto_p",               "<control>H", tournament_stop, 0, NULL },
  { "/_Help",                          NULL,         NULL, 0, "<LastBranch>" },
  { "/Help/_About...",                 "<control>B", help_about, 0, NULL },
};


void get_main_menu( GtkWidget  *window,
                    GtkWidget **menubar )
{
  GtkItemFactory *item_factory;
  GtkAccelGroup *accel_group;
  gint nmenu_items = sizeof (menu_items) / sizeof (menu_items[0]);

  accel_group = gtk_accel_group_new ();

  /* This function initializes the item factory.
     Param 1: The type of menu - can be GTK_TYPE_MENU_BAR, GTK_TYPE_MENU,
              or GTK_TYPE_OPTION_MENU.
     Param 2: The path of the menu.
     Param 3: A pointer to a gtk_accel_group.  The item factory sets up
              the accelerator table while generating menus.
  */

  item_factory = gtk_item_factory_new (GTK_TYPE_MENU_BAR, "<main>", 
                                       accel_group);

  /* This function generates the menu items. Pass the item factory,

     the number of items in the array, the array itself, and any
     callback data for the the menu items. */
  gtk_item_factory_create_items (item_factory, nmenu_items, menu_items, NULL);

  /* Attach the new accelerator group to the window. */
  gtk_accel_group_attach (accel_group, GTK_OBJECT (window));

  menu_file_start = gtk_item_factory_get_widget (item_factory, "/File/Start");
  menu_file_pause = gtk_item_factory_get_widget (item_factory, "/File/Pause");
  menu_file_continue = gtk_item_factory_get_widget (item_factory, "/File/Continue");
  menu_file_step = gtk_item_factory_get_widget (item_factory, "/File/Single Step");
  menu_file_stop = gtk_item_factory_get_widget (item_factory, "/File/Stop");
  menu_view_gstatistic = gtk_item_factory_get_widget (item_factory, "/View/Game Statistic");
  menu_view_pstatistic = gtk_item_factory_get_widget (item_factory, "/View/Program Statistic");
  menu_tournament_start = gtk_item_factory_get_widget (item_factory, "/Tournament/Start...");
  menu_tournament_stop = gtk_item_factory_get_widget (item_factory, "/Tournament/Stop");

  if (menubar)
    /* Finally, return the actual menu bar created by the item factory. */ 
    *menubar = gtk_item_factory_get_widget (item_factory, "<main>");
}

int main(int argc,char *argv[])
{
  GtkWidget *menubar;

  preferences_load ();
  DIMENSIONX = (int) sqrt (SIZE);
  DIMENSIONY = (SIZE+DIMENSIONX-1)/DIMENSIONX;

  add_program_directory (DATADIR);
  add_program_directory (".");

  gtk_init (&argc, &argv);
  
  main_window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
  gtk_signal_connect (GTK_OBJECT (main_window), "destroy", 
                      GTK_SIGNAL_FUNC (gtk_main_quit), 
                      "WM destroy");
  gtk_window_set_title (GTK_WINDOW (main_window), "Core Wars");
  gtk_window_set_policy (GTK_WINDOW (main_window), FALSE, FALSE, FALSE);
  
  main_vbox = gtk_vbox_new (FALSE, 1);
  gtk_container_border_width (GTK_CONTAINER (main_vbox), 1);
  gtk_container_add (GTK_CONTAINER (main_window), main_vbox);
  gtk_widget_show (main_vbox);
  
  get_main_menu (main_window, &menubar);
  gtk_box_pack_start (GTK_BOX (main_vbox), menubar, FALSE, TRUE, 0);
  gtk_widget_set (GTK_WIDGET (menu_file_start), "sensitive", FALSE, NULL);
  gtk_widget_set (GTK_WIDGET (menu_file_pause), "sensitive", FALSE, NULL);
  gtk_widget_set (GTK_WIDGET (menu_file_continue), "sensitive", FALSE, NULL);
  gtk_widget_set (GTK_WIDGET (menu_file_step), "sensitive", FALSE, NULL);
  gtk_widget_set (GTK_WIDGET (menu_file_stop), "sensitive", FALSE, NULL);
  gtk_widget_set (GTK_WIDGET (menu_tournament_start), "sensitive", FALSE, NULL);
  gtk_widget_set (GTK_WIDGET (menu_tournament_stop), "sensitive", FALSE, NULL);
  gtk_widget_show (menubar);

  create_drawing_area ();

  gtk_widget_show (main_window);
  
  gtk_main ();

  preferences_save ();
  
  return 0;
}

