/*
  XBubble - init.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 <limits.h>
#include <string.h>
#include <ctype.h>
#include <math.h>
#include <X11/Xlib.h>
#include <dirent.h>

#include "setting.h"
#include "cell.h"
#include "rgba.h"
#include "dialog.h"
#include "frame.h"
#include "bubble.h"
#include "board.h"
#include "gettext.h"
#include "init.h"

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

extern Colormap colormap;

extern char * data_dir;
extern char * theme;
extern int scale;
extern int win_width;
extern int win_height;

extern int nb_levels;
extern int levels[MAX_NB_LEVELS][NB_CELLS];

XFontStruct *title_font;
XFontStruct *dialog_font;
XFontStruct *menu_font;
GC dialog_gc;
GC dialog_text_gc;
GC menu_gc;
GC selected_gc;
Pixmap win_bg;
Pixmap board_decoration;
Pixmap board_decoration_mask;
Pixmap cup_on;
Pixmap cup_off;
Pixmap cup_on_mask;
Pixmap cup_off_mask;

Set alert_animation;
Set canon_animation;
Set countdown_animation;
Set bubble_animation[NB_COLORS][NB_BUBBLE_STATES];

const char *game_fonts[] = FONTS;
int titlefont_color[3] = TITLEFONT_COLOR;
int menufont_color[3] = MENUFONT_COLOR;
int menufont_selected_color[3] = MENUFONT_SELECTED_COLOR;

int notitle = 0;

static char * data_file( const char * file, int themed) {
  int path_max;
  int theme_len;
  char * abs_data_dir;
  static int offset = 0;
  static char buffer[1024];
  if ( ! offset ) {
#ifdef PATH_MAX
    path_max = PATH_MAX;
#else
    path_max = pathconf (path, _PC_PATH_MAX);
    if (path_max <= 0)
      path_max = 4096;
#endif
    abs_data_dir = (char *) xmalloc( path_max * sizeof(char));
    realpath( data_dir, abs_data_dir );
    offset = strlen(abs_data_dir);
    if ( offset > 1024-128 )
      offset = 1024-128;
    strncpy( buffer, abs_data_dir, offset );
    buffer[offset++] = '/';
    buffer[1023] = '\0';
    free( abs_data_dir );
  }
  if (themed) {
    theme_len=strlen(theme);
    strncpy( buffer + offset, "themes/", 126);
    strncpy( buffer + offset + 7, theme, 119);
    strncpy( buffer + offset + theme_len + 7, "/", 119 - theme_len);
    strncpy( buffer + offset + theme_len + 8, file, 118 - theme_len);
  } else {
    strncpy( buffer + offset, file, 126);
  }
  return buffer;
}


static void load_levels( const char * file ) {
  FILE *fd;
  char line[128];
  unsigned char r[8];
  int i, cell = 0;
  int line_counter = 0;
  int long_row = 1;
  int color, parse_error = 0;

  nb_levels = 0;
  file = data_file(file, 0);

  fd = fopen( file, "r" );
  if ( fd == NULL ) {
    warn( _("cannot open levels file %s."), file);
    return;
  }
  while (( fgets( line, 128, fd ) != NULL )&&( ! parse_error )) {
    line_counter++;
    if (( isalnum(line[0]) )||( line[0] == '-' )||( line[0] == ' ' )) {
      /* parse row */
      if ( long_row )
	parse_error = ( sscanf( line, "%c   %c   %c   %c   %c   %c   %c   %c",
				r, r+1, r+2, r+3, r+4, r+5, r+6, r+7 ) != 8);
      else
	parse_error = ( sscanf( line, "  %c   %c   %c   %c   %c   %c   %c",
				r+1, r+2, r+3, r+4, r+5, r+6, r+7 ) != 7 );
      if ( ! parse_error ) {
	for ( i = 0; i < 8; i++ ) {
	  switch( r[i] ) {
	  default:
	  /* the shift in values read and stored comes from the compatibility with fb */
	  case '-': color = 0; break;
	  case '0': color = 1; break;
	  case '1': color = 2; break;
	  case '2': color = 3; break;
	  case '3': color = 4; break;
	  case '4': color = 5; break;
	  case '5': color = 6; break;
	  case '6': color = 7; break;
	  case '7': color = 8; break;
	  }
	  levels[nb_levels][cell++] = color;
	}
	long_row = 1 - long_row;
      }
      else
	warn( _("parse error in file %s at line %d."), file, line_counter );
    }
    if ( cell >= (ROWS-1)*8 ) {
      nb_levels++;
      cell = 0;
      long_row = 1;
    }
    if ( nb_levels >= MAX_NB_LEVELS ) {
      warn( _("too many levels in file %s."), file );
      break;
    }
  }
}

void init_display( char * display_name ) {
  int i;
  XColor colors[256];
  display = XOpenDisplay( display_name );
  if ( display == NULL )
    fail( _("cannot open display.") );
  screen = DefaultScreen( display);
  visual = DefaultVisual( display, screen);
  root = RootWindow( display, screen);
  depth = DefaultDepth( display, screen);
  if ( depth < 8 ) {
    XCloseDisplay(display);
    fail( _("sorry, your display color depth is not supported.") );
  }
  if ( depth == 8 ) { /* allocate our own RRRGGGBB palette */
    for ( i = 0; i < 256; i++ ) {
      colors[i].pixel = i;
      colors[i].red =   (( i & 0xe0 ) << 8 );
      colors[i].green = (( i & 0x1c ) << 11 );
      colors[i].blue =  (( i & 0x03 ) << 14 );
      colors[i].flags = DoRed | DoGreen | DoBlue;
    }
    colormap = XCreateColormap( display, root, visual, AllocAll );
    XStoreColors( display, colormap, colors, 256 );
  }
}

static void add_font_path( char * path ) {
  int i;
  int npaths;
  char ** fontpath;
  char ** new_fontpath;

  fontpath = XGetFontPath( display, &npaths );    
  if ( fontpath != NULL ) {
    /* check if path is already present */
    for ( i = 0; i < npaths; i++ )
      if ( strncmp( path, fontpath[i], strlen(path) ) == 0 ) {
	XFreeFontPath(fontpath);
	return;
      }
    new_fontpath = (char **) xmalloc( sizeof(char *)*( npaths + 1 ));
    memcpy( new_fontpath, fontpath, npaths*sizeof(char *));
    new_fontpath[npaths] = path;
    XSetFontPath( display, new_fontpath, npaths+1 );
    XFreeFontPath(fontpath);
    free(new_fontpath);
  }
}

static XFontStruct * load_font( int pixel_size ) {
  XFontStruct * xfont;
  char name[128];
  int i;
  static int added_font_path = 0;

  /* try to load each font in turn */
  for ( i = 0; game_fonts[i] != NULL; i++ ) {
    sprintf( name, game_fonts[i], pixel_size );
    xfont = XLoadQueryFont( display, name );
    if ( xfont != NULL )
      return xfont;
  }
  /* let's see if we can supply a font */
  if ( ! added_font_path ) {
    add_font_path(data_file("",0));
    added_font_path = 1;
  }
  sprintf( name, SUPPLIED_FONT, pixel_size );
  xfont = XLoadQueryFont( display, name );
  if ( xfont != NULL )
    return xfont;
  
  /* at least we should have the "fixed" font ... uurgh */
  xfont = XLoadQueryFont( display, "fixed" );
  if ( xfont == NULL )
    fail( _("couldn't load any font !") );
  return xfont;
}

static GC create_copy_gc() {
  XGCValues gcv;
  unsigned long gcm;
  gcm = GCFunction | GCGraphicsExposures;
  gcv.graphics_exposures = False;
  gcv.function = GXcopy;
  return XCreateGC( display, root, gcm, &gcv );
}

static Frame load_frame( char *file, double zoom ) {
  Pixmap pixmap;
  Pixmap mask;
  Pixmap *pmask;
  RgbaImage ri1;
  RgbaImage ri2;
  Frame frame = (Frame) xmalloc( sizeof( struct _Frame ));

  ri1 = create_rgba_image_from_png_file(data_file(file,1));
/*   ri2 = ( zoom < 1.0 )? scale_rgba_image( ri1, zoom, zoom ) : ri1; */
  ri2 = scale_rgba_image( ri1, zoom, zoom ) ;
  pmask = ( ri2->has_alpha )? &mask : NULL;
  create_pixmaps_from_rgba_image( ri2, &pixmap, pmask );
  
  frame->width = ri2->width;
  frame->height = ri2->height;
  /* center sprite */
  frame->cx = ri2->width/2;
  frame->cy = ri2->height/2;
  /* default frame duration = 40 ms */
  frame->delay = 40;
  frame->has_mask = ri2->has_alpha;
  frame->pixmap = pixmap;
  if ( frame->has_mask ) {
    frame->gc = create_copy_gc();
    /* precompute clipmask */
    XSetClipMask( display, frame->gc, mask );
    XFreePixmap( display, mask );
  }
  if ( ri1 != ri2 )
    delete_rgba_image(ri1);
  delete_rgba_image(ri2);
  return frame;
}

static void delete_frame( Frame frame ) {
  XFreePixmap( display, frame->pixmap);
  if ( frame->has_mask )
    XFreeGC( display, frame->gc );
  free(frame);
}

static Set load_animation( const char *filename, double zoom ) {
  Frame frame;
  Set animation = NULL;
  int cx;
  int cy;
  int delay;
  int max_nb_frames = 0;
  int lc = 0;
  char line[128];
  char framename[128];
  FILE *fd;

  /* open animation description file */
  fd = fopen( data_file(filename,1), "r" );
  if ( fd == NULL )
    fail( _("cannot open animation file %s."), data_file(filename,1) );
  
  /* count frames */
  while ( fgets( line, 128, fd ) != NULL ) {
    if ( isalnum(line[0]))
      max_nb_frames++;
  }
  rewind(fd);
  /* load frames */
  if ( max_nb_frames > 0 ) {
    animation = set_new(max_nb_frames);
    while ( fgets( line, 128, fd ) != NULL ) {
      lc++;
      /* parse line */
      if ( isalnum(line[0])) {
	if ( sscanf( line, "%127s%d%d%d", framename, &cx, &cy, &delay) == 4 ) {
	  frame = load_frame( framename, zoom );
	  set_add( animation, frame );
	  frame->cx = cx*zoom;
	  frame->cy = cy*zoom;
	  frame->delay = delay;
	}
	else
	  warn( _("error parsing line %d of file %s."), lc, filename );
      }
    }
    if ( animation->size == 0 )
      fail( _("animation %s has no valid frames."), filename );
  }
  fclose(fd);
  return animation;
}

static void load_scaled_image( const char *file, Pixmap *pix, Pixmap *mask, double zoom ) {
  RgbaImage ri1;
  RgbaImage ri2;
  ri1 = create_rgba_image_from_png_file(data_file(file,1));
  ri2 = ( zoom < 1.0 )? scale_rgba_image( ri1, zoom, zoom ) : ri1;
  create_pixmaps_from_rgba_image( ri2, pix, mask );
  delete_rgba_image(ri1);
  if ( ri2 != ri1 )
    delete_rgba_image(ri2);
}

static void delete_animation( Set animation ) {
  int i;
  if (animation) {
     for ( i = 0; i < animation->size; i++ )
       delete_frame( animation->element[i] );
     set_free( animation );
  }
}

static void unref_animation( Set animation ) {
  if (animation && !--animation->ref)
     delete_animation( animation );
}

static void make_canon_animation( double zoom ) {
  RgbaImage ri;
  RgbaImage ri2;
  Pixmap mask;
  Frame frame;
  int i;

  ri2 = create_rgba_image_from_png_file(data_file("canon.png",1));  
  ri2->hotx = CANON_CX;
  ri2->hoty = CANON_CY;
  ri = scale_rgba_image( ri2, zoom, zoom );
  delete_rgba_image(ri2);
  
  canon_animation = set_new( NB_ANGLES );

  for ( i = -CANON_ANGLE_MAX; i <= CANON_ANGLE_MAX; i++ ) {
    frame = (Frame) xmalloc( sizeof( struct _Frame ));
    ri2 = rotate_rgba_image( ri, -i*ANGLE_STEP, True );
    create_pixmaps_from_rgba_image( ri2, &frame->pixmap, &mask );
    frame->width = ri2->width;
    frame->height = ri2->height;
    frame->cx = ri2->hotx;
    frame->cy = ri2->hoty;
    frame->delay = 0;
    frame->has_mask = ri2->has_alpha;
    if ( frame->has_mask ) {
      frame->gc = create_copy_gc();
      /* precompute clipmask */
      XSetClipMask( display, frame->gc, mask );
      XFreePixmap( display, mask );
    }
    delete_rgba_image(ri2);
    set_add( canon_animation, frame );
  }
  delete_rgba_image(ri);
}

/* Determine whether it is a valid bubble name or not. 
   The following Naming convention is used : 
   Bubble_<COLOR>_<STATE>_<num>.png 
*/
static int parse_bubble_name(const char *name,int *color_val, int *state_val)
{
  char *copy = NULL, *copy_orig = NULL;
  char *token = NULL;
  int color,state;
  int return_val = 0;
  
  copy = copy_orig = calloc(strlen(name) + 1, sizeof(char));
  strcpy(copy,name);
  token = strtok(copy,"_");

  if(strcmp(token,"Bubble")==0) { /* Let's determine the color */
    token = strtok(NULL,"_");

    for ( color = 0; color < NB_COLORS; color++ ) {
      if(strcmp(name_color_get(color),token)==0) {
	break;
      }
    }
    if(color==NB_COLORS) { 
      warn( _("%s : %s is not a valid color."), name, token ); 
    } else { /* Let's determine the state now */
      *color_val = color;

      token = strtok(NULL,"_");
      for ( state = 0; state < NB_BUBBLE_STATES; state++ ) {
	if(strcmp(name_state_get(state),token)==0) {
	  break;
	}
      }
      if((state == NB_BUBBLE_STATES) && (strcmp(DEFAULT_STATE_NAME,token)!=0)) {
	warn( _("%s : %s is not a valid state."), name, token ); 
      } else {
	return_val = 1;
	*state_val = ((state==NB_BUBBLE_STATES)?DEFAULT_STATE:state);
      }
    }
  } 
  free(copy_orig);
  return return_val;
}

static int diren_select_bubbles (const struct dirent *d) {
  int color;
  int state;

  /* Check whether this is a .png file or not */
  if(strcmp(d->d_name + strlen(d->d_name) - 4,".png")!=0) {
    return 0;
  }
  return(parse_bubble_name(d->d_name,&color,&state));
}

static void load_animations( double zoom ) {
  int color=-1;
  int state=-1;
   
  int bubble_animation_size[NB_COLORS][NB_BUBBLE_STATES + 1];
  Set default_animation[NB_COLORS];
   
  struct dirent **namelist = NULL;
  int n = 0;
  int i;
  char *dir = NULL;

  alert_animation = load_animation( "frame_light.txt", zoom );
  countdown_animation = load_animation( "countdown.txt", zoom );

  for ( color = 0; color < NB_COLORS; color++ )
     default_animation[color] = NULL;
   
  memset(bubble_animation,0, sizeof(bubble_animation));   
  memset(bubble_animation_size,0, sizeof(bubble_animation_size));
   
  color=state=-1;
  
  /* bubble animation */
  dir = data_file("./",1);

  n = scandir(dir, &namelist, diren_select_bubbles , alphasort);
  if (n < 0) fail(_("Error while scanning %s"),dir);
   
  /* Count how much frame in each animation */
  for(i=0; i<n; i++) {
    parse_bubble_name(namelist[i]->d_name,&color,&state);
    if (state == DEFAULT_STATE) {
      bubble_animation_size[color][NB_BUBBLE_STATES]++;
    } else {
      bubble_animation_size[color][state]++;
    }
  }
  
  /* Make room */
  for ( color = 0; color < NB_COLORS; color++ ) {
     
    for ( state = 0; state < NB_BUBBLE_STATES; state++ ) {
      if(bubble_animation_size[color][state]==0) {
	/* create the default animation set if needed and ref it if not */
	if (default_animation[color] == NULL) { 
	  default_animation[color] = set_new(bubble_animation_size[color][NB_BUBBLE_STATES]);
	} else {
	  set_ref(default_animation[color]);
	}
	/* use the default animation */
        bubble_animation[color][state] = default_animation[color]; 
      } else {
	bubble_animation[color][state] = set_new(bubble_animation_size[color][state]);
      }
    }
  }
  
  /* read the files and fill the animations */
  for (i=0; i<n; i++) {
    color = -1;
    state = -1;
    parse_bubble_name(namelist[i]->d_name,&color,&state);
    if (state == DEFAULT_STATE) {
      Frame frame = load_frame(namelist[i]->d_name, zoom);
      set_add( default_animation[color], 
	       load_frame(namelist[i]->d_name, zoom) );
    } else {
      set_add( bubble_animation[color][state], 
	       load_frame(namelist[i]->d_name, zoom));
    }
  }

  for ( color = 0; color < NB_COLORS; color++ ) {
    for ( state = 0; state < NB_BUBBLE_STATES; state++ ) {
      if(bubble_animation[color][state]==NULL) {
	fail(_("Badly formed theme : no image for %s bubbles in state %s"),
	     name_color_get(color),name_state_get(state));
      }
    }
  }
  for(i=0; i<n; i++)
    free(namelist[i]);
  free(namelist);
}

static void put_pixmap( Pixmap dest_pixmap,
			Pixmap dest_mask,
			Pixmap src_pixmap,
			Pixmap src_mask,
			int x, int y, int w, int h, int dx, int dy ) {
  GC gc;
  GC gc_mask;
  XGCValues gcv;
  unsigned long gcm;
  gcm = GCFunction | GCGraphicsExposures;
  gcv.graphics_exposures = False;
  gcv.function = GXcopy;
  gc = XCreateGC( display, root, gcm, &gcv );
  gcv.function = GXor;
  gc_mask = XCreateGC( display, dest_mask, gcm, &gcv );
  XSetClipMask( display, gc, src_mask );
  XSetClipOrigin( display, gc, dx, dy );
  XCopyArea( display, src_pixmap, dest_pixmap, gc, x, y, w, h, dx, dy );
  if ( src_mask != None )
    XCopyArea( display, src_mask, dest_mask, gc_mask, x, y, w, h, dx, dy );
  else {
    XSetFillStyle( display, gc_mask, FillSolid );
    XFillRectangle( display, dest_mask, gc_mask, dx, dy, w, h );
  }
  XFreeGC( display, gc );
  XFreeGC( display, gc_mask );
}

static void load_images( double zoom ) {
  GC gc;
  GC gc_mask;
  int x;
  int y;
  unsigned int width;
  unsigned int height;
  unsigned int d;
  Window w;
  Frame frame;
  Pixmap tile;
  Pixmap tile_mask;
  Pixmap side;
  Pixmap side_mask;
  Pixmap tee;
  Pixmap tee_mask;
  Pixmap bottom;
  Pixmap bottom_mask;
  Pixmap bar;
  XGCValues gcv;
  unsigned long gcm;
  gcm = GCFunction | GCGraphicsExposures;
  gcv.graphics_exposures = False;
  gcv.function = GXcopy;

  load_scaled_image( "blue_bg.png", &tile, &tile_mask, zoom );
  load_scaled_image( "side.png", &side, &side_mask, zoom );
  load_scaled_image( "bar.png", &bar, NULL, zoom );
  load_scaled_image( "tee.png", &tee, &tee_mask, zoom );
  load_scaled_image( "bottom.png", &bottom, &bottom_mask, zoom );
  load_scaled_image( "cup_on.png", &cup_on, &cup_on_mask, zoom);
  load_scaled_image( "cup_off.png", &cup_off, &cup_off_mask, zoom);

  width = board_win_width(scale);
  height = board_win_height(scale);

  /* make board background decoration */
  board_decoration = XCreatePixmap( display, root, width, height, depth );
  board_decoration_mask = XCreatePixmap( display, root, width, height, 1);
  gc = XCreateGC( display, root, gcm, &gcv );
  gc_mask = XCreateGC( display, board_decoration_mask, gcm, &gcv );

  /* draw background tile */
  XSetFillStyle( display, gc, FillTiled );
  XSetTile( display, gc, tile );
  XFillRectangle( display, board_decoration, gc, 0, 0, width, height);
  XSetFillStyle( display, gc_mask, FillTiled );
  XSetTile( display, gc_mask, tile_mask );
  XFillRectangle( display, board_decoration_mask, gc_mask, 0, 0, width,height);

  /* left side */
  put_pixmap( board_decoration, board_decoration_mask, side, side_mask,
	      0, 0, scale_x( 0, scale), height, 0, 0 );
  /* right side */
  put_pixmap( board_decoration, board_decoration_mask, side, side_mask,
	      0, 0, scale_x( 0, scale), height, width - scale_x(0,scale), 0 );
 
  /* bar */
  x = scale_x( 0, scale );
  y = scale_y( (ROWS-1)*ROW_HEIGHT, scale );
  width = scale_x( BOARD_WIDTH, scale ) - x;
  height = 20*scale/MAX_SCALE;
  XSetTile( display, gc, bar );
  XSetForeground( display, gc_mask, WhitePixel(display, root));
  XSetFillStyle( display, gc_mask, FillSolid );
  XSetClipMask( display, gc, None );
  XSetClipOrigin( display, gc, 0, 0 );
  XFillRectangle( display, board_decoration, gc, x, y, width, height );
  XFillRectangle( display, board_decoration_mask, gc_mask, x, y, width,height);

  /* add bottom */ 
  XGetGeometry( display, bottom, &w, &x, &y, &width, &height, &d, &d );
  x = ( board_win_width(scale) - width )/2;
  y = board_win_height(scale) - height;
  put_pixmap( board_decoration, board_decoration_mask, bottom, bottom_mask,
	      0, 0, width, height, x, y );
  /* add tee */ 
  XGetGeometry( display, tee, &w, &x, &y, &width, &height, &d, &d );
  x = scale_x( 1.0, scale ) - width/2;
  y = board_win_height(scale) - height;
  put_pixmap( board_decoration, board_decoration_mask, tee, tee_mask,
	      0, 0, width, height, x, y );
  /* add top */
  frame = alert_animation->element[0];
  XGetGeometry( display, frame->pixmap, &w, &x, &y, &width, &height, &d, &d );
  x = ( board_win_width(scale) - width )/2;
  put_pixmap( board_decoration, board_decoration_mask,
	      frame->pixmap, None,
	      0, 0, frame->width, frame->height, x, 0 ); 
  
  /* cleanup */
  XFreeGC( display, gc );
  XFreeGC( display, gc_mask );
  XFreePixmap( display, bar );
  XFreePixmap( display, tile );
  XFreePixmap( display, tile_mask );
  XFreePixmap( display, side );
  XFreePixmap( display, side_mask );
  XFreePixmap( display, bottom );
  XFreePixmap( display, bottom_mask );
  XFreePixmap( display, tee );
  XFreePixmap( display, tee_mask );
}

static void init_gcs() {
  /* load dialog font */
  dialog_font = load_font( 3*scale/2 );
  /* yellow for hilighting menu selection */
  selected_gc = create_copy_gc();
  XSetFont( display, selected_gc, menu_font->fid );
  XSetForeground( display, selected_gc, get_pixel( menufont_selected_color[0], 
						   menufont_selected_color[1], 
						   menufont_selected_color[2] ));
  /* grey for menu items */
  menu_gc = create_copy_gc();
  XSetFont( display, menu_gc, menu_font->fid );
  XSetForeground( display, menu_gc, get_pixel( menufont_color[0], menufont_color[1], 
				    menufont_color[2] ));
}

void cleanup_graphics() {
  int color,state;
  delete_animation( alert_animation );
  delete_animation( canon_animation );
  delete_animation( countdown_animation );

  for ( color = 0; color < NB_COLORS; color++ )
    for ( state=0;  state<NB_BUBBLE_STATES; state++ )
       unref_animation( bubble_animation[color][state] );

  XFreeGC( display, dialog_gc );
  XFreeGC( display, dialog_text_gc );
  XFreeGC( display, menu_gc );
  XFreeGC( display, selected_gc );
  XFreePixmap( display, win_bg );
  XFreePixmap( display, board_decoration );
  XFreePixmap( display, board_decoration_mask );
  XFreePixmap( display, cup_on );
  XFreePixmap( display, cup_off );
  XFreePixmap( display, cup_on_mask );
  XFreePixmap( display, cup_off_mask );
  XFreeFont( display, title_font );
  XFreeFont( display, dialog_font );
  XFreeFont( display, menu_font );
}

void splash_screen( double zoom ) {
  Window splash1=(Window)NULL;
  Window splash2;
  int height1;
  int height2;
  const char * title = "XBubble";
  char * subtitle = _("Loading graphics ...");

  const char *config_file = "config.txt";
  FILE *fd = NULL ;
  char line[128];
  char *copy = NULL;
  char *copy_orig = NULL;
  char *token = NULL;
  int lc=0;
  int value = 0;
  int *font_color = NULL;
  int font_nb=0;
  double newzoom = zoom;
  int i;

  fd = fopen( data_file(config_file,1), "r" );
  if(fd) { /* time to change default values... */
    while ( fgets( line, 128, fd ) != NULL ) {
      lc++;
      copy = copy_orig = calloc(strlen(line) + 1, sizeof(char));
      strcpy(copy,line);
      copy[strlen(line)-1]=' ';
      token = strtok(copy," :");
      if(token==NULL) continue;
      if(strcmp(token,"BUBBLE_SIZE")==0) { /* We need to scale */
	token = strtok(NULL," :");
	if(sscanf(token,"%d",&value)!=1)  
	  warn( _("error parsing line %d of file %s."), lc, config_file );
	else 
	  newzoom = zoom * MAX_SCALE / value;
      } else if (((strcmp(token,"TITLE_COLOR")==0)&&((font_nb=1))) ||
		 ((strcmp(token,"MENU_COLOR")==0)&&((font_nb=2))) ||
		 ((strcmp(token,"MENU_SELECTED_COLOR")==0)&&((font_nb=3)))) {
	switch (font_nb) {
	case 1: font_color = titlefont_color; break;
	case 2: font_color = menufont_color; break;
	case 3: font_color = menufont_selected_color; break;
	default: break;
	}
	for(i=0; i<3; i++) {
	  token = strtok(NULL," :");
	  if(sscanf(token,"%d",&font_color[i])!=1) {
	    warn( _("error parsing line %d of file %s."), lc, config_file );
	    break;
	  }
	} 
      } else if (strcmp(token,"NO_TITLE")==0) {
	notitle = 1;
      } else
	warn( _("error parsing line %d of file %s."), lc, config_file );
      free(copy_orig);
    }
    fclose(fd);
  }
  
  load_scaled_image( "xbubble_bg.png", &win_bg, NULL, zoom);
  XSetWindowBackgroundPixmap( display, win, win_bg );
  XMapWindow( display, win );

  /* load title & menu fonts */
  title_font = load_font( 3*scale );
  menu_font = load_font( scale );
  height1 = title_font->ascent + title_font->descent;
  height2 = menu_font->ascent + menu_font->descent;

  /* make GCs */
  dialog_gc = create_copy_gc();
  dialog_text_gc = create_copy_gc();

  /* GC for tiling dialogs background */
  XSetFillStyle( display, dialog_gc, FillTiled );
  XSetTile( display, dialog_gc, win_bg );
  if (!notitle) 
    splash1 = create_dialog( win_width/2, ( win_height - height2 )/2, gettext(title),
			     title_font, get_pixel( titlefont_color[0], titlefont_color[1],
						    titlefont_color[2] ), 0, 1.0 );
  splash2 = create_dialog( win_width/2, ( win_height + height1 )/2, gettext(subtitle),
			   menu_font, get_pixel( menufont_color[0], menufont_color[1], 
						 menufont_color[2] ), 0, 1.0 );
  load_levels( "levels.txt" );
  load_animations(newzoom);
  make_canon_animation(zoom);
  load_images(zoom);
  init_gcs();

  if(!notitle)
    XDestroyWindow( display, splash1 );
  XDestroyWindow( display, splash2 ); 
}
