/*
  XBubble - xbubble.c

  Copyright (C) 2002  Ivan Djelic <ivan@savannah.gnu.org>
  
  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 2 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, write to the Free Software
  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
*/

#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <unistd.h>
#include <sys/time.h>
#include <X11/Xlib.h>
#include <X11/Xutil.h>

#include "setting.h"
#include "gettext.h"

#include "cell.h"
#include "game.h"
#include "timer.h"
#include "screens.h"
#include "init.h"

Display *display;
Visual *visual;
Window root;
Window win;
int screen;
int depth;
Colormap colormap;

int scale;
int win_width;
int win_height;
int fps;
unsigned long frame_duration;
const char *data_dir;
const char *theme;
int colors[NB_CELLS];
int nb_levels;
int levels[MAX_NB_LEVELS][NB_CELLS];

#define level_handicap(X) ( DEFAULT_HANDICAP + 3000*( NORMAL - (X)))

static void usage() {
  printf(_("Usage: xbubble [-d display] [-s scale] [-f framerate] [-r resourcepath] [-t theme]\n\n"));
  exit(1);
}

static void create_randome_level( int nb_rows ) {
  int i;
  /* preload bubbles */
  for ( i = 0; i < nb_rows*COLS; i++ )
    colors[i] = rnd( NB_COLORS ) + 1;
  for ( i = nb_rows*COLS; i < NB_CELLS; i++ )
    colors[i] = 0;
}

static void play_single_player_game( int *handicap ) {
  int i;
  int won = 1;
  int again = 1;
  int score[2]  = { 0, 0 };
  RuleSet_t ruleset=DEFAULT_RULE_SET;
  enum GameResult result;
  Game game;

  ruleset.max_fire_delay = handicap[0];
  i=0;
  while (again && i < nb_levels) {
    game = new_game( SINGLE_PLAYER, &ruleset, levels[i], i+1, 
		     score, NORMAL );
    
    result = play_game(game);
    won = ( result == PLAYER1_WON );
    again = ( result != ABORTED );
    delete_game( game, again && ( !won || ( i+1 < nb_levels )));
    if (won) i++;
  }
  if ( won )
    printf(_("CONGRATULATIONS !!!\n"));
}

static void play_challenge_game( int *handicap ) {
  enum GameResult result;
  int level=1;
  int score[2] = { 0, 0 };
  RuleSet_t ruleset=DEFAULT_RULE_SET;
  Game game;

  ruleset.max_fire_delay = handicap[0];
  /* preload bubbles */
  do {
     create_randome_level(5);
     game = new_game( SINGLE_PLAYER, &ruleset, colors, level, score,
		      NORMAL );
     result=play_game(game);
     if (result == PLAYER1_WON)
       level++;
     delete_game( game, False );
  } while (result != ABORTED);
}

static void play_match( enum GameMode mode, int *handicap, enum Level level ) {
  int round;
  int done;
  int score[2];
  int games[2]  = { 0, 0 };
  int rounds[2] = { 0, 0 };
  RuleSet_t ruleset[2]={ DEFAULT_RULE_SET, DEFAULT_RULE_SET };
  enum GameResult result;
  Game game;

  ruleset[0].max_fire_delay = handicap[0];
  ruleset[1].max_fire_delay = handicap[1];

  do {
    score[0] = 0;
    score[1] = 0;
    done = 0;
    for ( round = 1; !done; round++ ) {
      /* preload bubbles */
      create_randome_level(5);
      game = new_game( mode, ruleset, colors, round, score, level);
      result = play_game(game);
      done = score[1]==2 || score[0]==2 || result == ABORTED;
       //not_done= (!(((score[1]>=5)&&(score[1]-score[0]>1)) || 
       // ((score[0]>=5)&&(score[0]-score[1]>1))) &&
       // ( result != ABORTED ));
      delete_game( game, !done );
    }
    /* update stats */
    rounds[0] += score[0];
    rounds[1] += score[1];
    if ( result != ABORTED ) {
      games[0] += ( score[0] > score[1] )? 1 : 0 ;
      games[1] += ( score[1] > score[0] )? 1 : 0 ;
    }
  }
  while ( screen_2p( mode, rounds, games ) == S3_CONTINUE );
}

static void play_demo( enum Level level ) {
  int not_done;
  RuleSet_t ruleset[2]={ DEFAULT_RULE_SET, DEFAULT_RULE_SET };
  int score[2] = { 0, 0 };
  Game game;
  
  ruleset[0].max_fire_delay = level_handicap(NORMAL);
  ruleset[1].max_fire_delay = level_handicap(NORMAL);
  
  do {
    create_randome_level(5);
    game = new_game( DEMO, ruleset,  colors, 1, score, level );
    not_done = ( play_game(game) != ABORTED );
    delete_game( game, not_done );
  }
  while ( not_done );
}

static void play_xbubble() {
  int quit, level;
  int choice[2];
  int handicap[2];
  quit = 0;
  int preset=S1_SINGLE_PLAYER;

  while ( ! quit ) {
    handicap[0] = level_handicap(NORMAL);
    handicap[1] = level_handicap(NORMAL);

    switch (preset=screen_main(preset) ) {

    case S1_SINGLE_PLAYER:
      /* select handicap */
      choice[0] = screen_1p_handicap();
      if ( choice[0] != S5_ESCAPE ) {
	handicap[0] = level_handicap( choice[0] );
	play_single_player_game( handicap );
      }
      break;

    case S1_TWO_PLAYERS:
      /* select players handicaps */
      if ( screen_2p_handicap(choice) == S4_OK ) {
	/* player 1 is on the left */
	handicap[0] = level_handicap( choice[1] );
	handicap[1] = level_handicap( choice[0] );
	play_match( TWO_PLAYERS, handicap, NORMAL );
      }
      break;

    case S1_PLAYER_VS_COMPUTER:
      /* select computer skill */
      level = screen_computer_skill();
      if ( level != S2_ESCAPE ) {
	level += VERY_EASY - S2_VERY_EASY;
	play_match( PLAYER_VS_COMPUTER, handicap, level );
      }
      break;

    case S1_CHALLENGE:
      /* select handicap */
      choice[0] = screen_1p_handicap();
      if ( choice[0] != S5_ESCAPE ) {
	handicap[0] = level_handicap( choice[0] );
	play_challenge_game( handicap );
      }
      break;
       
    case S1_DEMO:
      play_demo( VERY_HARD );
      break;

    case S1_SETTINGS:
      choice[0] = screen_settings(S6_KEY);
      while ( choice[0] != S6_BACK && choice[0] != S6_ESCAPE) {
	 screen_unimplemented();
	 choice[0] = screen_settings(choice[0]);
      }       
      break;
       
    case S1_ESCAPE:
    case S1_QUIT:
      quit = 1;
      break;
    default:
      break;
    }
  }
}

int main( int argc, char ** argv ) {
  int c;
  double zoom;
  char *display_name;
  char app_name[256];
  int max_win_width;
  int pscale;
  struct timeval tv;
  XSizeHints xsh;
  XWMHints wm_hint;
  XClassHint class_hint;

  /* NLS nuisances */
#if ENABLE_NLS
  setlocale(LC_ALL, "");
  bindtextdomain(PACKAGE, LOCALEDIR);
  textdomain(PACKAGE); 
#endif
   
  /* default values */
  fps = DEFAULT_FPS;
  data_dir = DATADIR;
  theme = "fancy";
  pscale = 0;
  display_name = NULL;
  /* mtrace(); */

  /* parse options */
  while (( c = getopt( argc, argv, "s:f:r:t:d:" )) != -1 ) {
    switch (c) {
    case 's': /* scale */
      if ( sscanf( optarg, "%d", &pscale ) != 1 )  {
	fprintf( stderr, _("Invalid scale: %s.\n"), optarg);
	usage();
      }
      break;
    case 'f': /* frame rate */
      if ( sscanf( optarg, "%d", &fps ) != 1 )  {
	fprintf( stderr, _("Invalid frame rate: %s.\n"), optarg);
	usage();
      }
      break;
    case 'r': /* resources */
      data_dir = optarg;
      break;
    case 't': /* themes */
      theme = optarg;
      break;
    case 'd': /* display */
      display_name = optarg;
      break;
    default:
      usage();
    }
  }
  init_display( display_name );

  /* adjust frame rate */
  fps = clip( fps, 10, 100 );
  frame_duration = get_closest_itimer_interval(1000000/fps);
  fps = 1000000 / frame_duration;

  /* adjust scale */
  max_win_width = game_win_width(MAX_SCALE);
  if ( ! pscale ) /* automagically adjust window size to 100% of screen width */
    pscale = DEFAULT_SCALE*WidthOfScreen(DefaultScreenOfDisplay(display))/max_win_width;
  pscale = clip( pscale, 5, 100 );
  scale = pscale*MAX_SCALE/100;
  zoom = 1.0*scale/MAX_SCALE;

  win_width = game_win_width(scale);
  win_height = game_win_height(scale);

  /* create main window */
  win = XCreateSimpleWindow( display, root, 0, 0, win_width, win_height,0,0,0);
  XSelectInput( display,
		win,
		ExposureMask |
		StructureNotifyMask | 
		KeyPressMask |
		KeyReleaseMask );

  if ( depth == 8 )
    XSetWindowColormap( display, win, colormap );
  /* we don't want window resizing */
  xsh.flags = PBaseSize | PMinSize | PMaxSize;
  xsh.min_width = xsh.max_width = xsh.base_width = win_width;
  xsh.min_height = xsh.max_height = xsh.base_height = win_height;
  XSetWMNormalHints( display, win, &xsh );

  /* set window title name, name, class, etc. */
  sprintf( app_name, _("XBubble @ %d fps  v%s"), fps, VERSION );
  XStoreName( display, win, app_name );
  class_hint.res_name = (char *)"xbubble";
  class_hint.res_class = (char *)"XBubble";
  wm_hint.flags = InputHint | StateHint;
  wm_hint.input = True;
  wm_hint.initial_state = NormalState;
  XSetClassHint( display, win, &class_hint );
  XSetWMHints( display, win, &wm_hint );

  /* new randomize seed */
  gettimeofday( &tv, NULL );
  srand( tv.tv_usec );

  splash_screen(zoom);
  play_xbubble();

  cleanup_graphics();
  XDestroyWindow( display, win );
  if ( depth == 8 )
    XFreeColormap( display, colormap );
  XCloseDisplay( display );
  return 0;
}
