/*  Motti -- a strategy game
    Copyright (C) 1999 Free Software Foundation

    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
*/
/* This file has general X init routines. The O'Reilly's Xlib
   Programming manual by Adrian Nye has been used as a guide.  */

#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <X11/Xresource.h>
#include <X11/xpm.h>

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

#include "xwin.h"
#include "map.h"
#include "xinit.h"
#include "wrappers.h"

Display *display;
int screen_num;
Colormap colormap;
int depth;

struct db_vals db_val_tab = {0};
XrmDatabase DB = {0};

XColor *player_col;
XColor *occupied_col;
XColor misc_col[last_col_num];
int players;

Window main_win;
Window but_win[NBUTS];
Window map_win;

GC mapgc, capitalgc, crossgc;
Pixmap att_pix[6], def_pix, gue_pix;
Pixmap d_att_pix, d_def_pix, d_gue_pix;

static Visual *visual;
static GC tempgc;

static void init_DB_tab (void);
static int alloc_Xrm_color (char *, char *, char *, XColor *);
static void make_map_stipples (Pixmap, Pixmap, int, int);
static void make_att_buttons (void);
static void make_att_pixs (void);
static void make_def_pixs (void);
static void make_gue_pixs (void);
static void alloc_gcs (void);
static void set_hints (int, int, char **, int);

static void
init_DB_tab ()
{
  register int i;

  enum types
  {
    type_short,
    end
  };
  struct val_tab
  {
    enum types type;
    short *ptr;
    char *name, *class;
    int deflt;
  };
  struct val_tab vals[3] =
  {
    {type_short, &db_val_tab.but_width, "motti.button.width",
     "Motti.Button.Width", 80},
    {type_short, &db_val_tab.but_height, "motti.button.height",
     "Motti.Button.Height", 20},
    {end, NULL, "", "", 0}
  };

  for (i = 0; vals[i].type != end; i++)
    {
      char *str_type;
      XrmValue value;
      if (XrmGetResource (DB, vals[i].name, vals[i].class, &str_type,
			  &value))
	{
	  switch (vals->type)
	    {
	    case type_short:
	      *(vals[i].ptr) = (short) atoi (value.addr);
	    case end:
	    }
	}
      else
	*(vals[i].ptr) = (short) vals->deflt;
    }
}

/* This function searches for possible display name in any files.  */
extern int
open_display (display_name)
     char *display_name;
{
  XrmValue value;
  XrmDatabase displayDB = {0};
  char *str_type;
  XrmInitialize ();

  if (display_name)
    display = XOpenDisplay (display_name);
  else
    {
      XrmCombineFileDatabase ("~/.Xdefaults", &displayDB, False);
      XrmCombineFileDatabase ("/usr/lib/X11/app-defaults/Motti",
			      &displayDB, False);
      if (XrmGetResource (displayDB, "motti.display",
			  "Motti.Display", &str_type, &value))
	display = XOpenDisplay (value.addr);
      else
	display = XOpenDisplay ((char *) NULL);
      XrmDestroyDatabase (displayDB);
    }
  if (!display)
    return 0;
  /* These are defined now for later use.  */
  screen_num = DefaultScreen (display);
  visual = DefaultVisual (display, screen_num);
  colormap = DefaultColormap (display, screen_num);
  depth = DefaultDepth (display, screen_num);
  return 1;
}

extern void
create_DB ()
{
  char filename[1024] = "/usr/lib/X11/app-defaults/Motti";
  char *environment;

  /* First get the application defaults.  */
  DB = XrmGetFileDatabase (filename);

  /* Next get the server defaults.  */
  if (XResourceManagerString (display) != NULL)
    {
      XrmDatabase serverDB;
      serverDB = XrmGetStringDatabase (XResourceManagerString
				       (display));
      XrmMergeDatabases(serverDB, &DB);
    }
  else
    {
      XrmCombineFileDatabase ("~/.Xdefaults", &DB, True);
    }

  environment = getenv ("XENVIRONMENT");
#if HAVE_GETHOSTNAME
  if (!environment)
    {
      environment = filename;
      strcpy (filename, "~/.Xdefaults-");
      gethostname (filename+13, 1011);
    }
#else
  if (environment)
#endif
    XrmCombineFileDatabase (environment, &DB, True);
  init_DB_tab ();
}

static int
alloc_Xrm_color (instance, class, deflt, color)
     char *instance, *class;
     char *deflt;
     XColor *color;
{
  char *msg = NULL, *color_name;
  XrmValue value;
  XColor nullcol;
  char *str_type;

  if (XrmGetResource (DB, instance, class, &str_type, &value))
    color_name = value.addr;
  else
    {
      if (deflt)
	color_name = deflt;
      else
	msg = alloc_err_str (ERR_COLOR_FIND, class);
    }

  if (!msg && !XAllocNamedColor (display, colormap, color_name, color,
				 &nullcol))
    msg = alloc_err_str (ERR_COLOR_ALLOC, color_name);
  else
    return 1;
  fprintf (stderr, "motti: %s\n", msg);
  free (msg);
  return 0;
}

extern int
alloc_colors ()
{
  char *str_type;
  XrmValue value;
  int i;
  XVisualInfo visual_info;

  /* Search for the best visual, use color if possible */
  for (i = 5; !XMatchVisualInfo (display, screen_num, depth, i,
				 &visual_info); i--);
  if (i < StaticColor)
    return 0;

  if (XrmGetResource (DB, "motti.players", "Motti.Players",
		      &str_type, &value))
    {
      players = atoi (value.addr);
      if (players > MAXPLAYERS)
	die ("too many players");
      else if(players < 2)
	die ("too few players");
    }
  else
    players = 2;

  player_col = (XColor *) my_malloc (sizeof(XColor)*players);
  occupied_col = (XColor *) my_malloc (sizeof(XColor)*players);

  if (!(alloc_Xrm_color ("motti.background", "Motti.Background",
			"white", &misc_col[bg_col])
	&& alloc_Xrm_color ("motti.foreground", "Motti.Foreground",
			    "black", &misc_col[fg_col])
	&& alloc_Xrm_color ("motti.color.map.background",
			    "Motti.Map.Background", "RGBi:0/0/0.05",
			    &misc_col[map_bg_col])
	&& alloc_Xrm_color ("motti.color.capital",
			    "Motti.Color.Capital", "gray50",
			    &misc_col[capital_col])
	&& alloc_Xrm_color ("motti.color.cross",
			    "Motti.Color.Cross", "gray70",
			    &misc_col[cross_col])
	&& alloc_Xrm_color ("motti.button.foreground",
			    "Motti.Foreground", "black",
			    &misc_col[but_fg])
	&& alloc_Xrm_color ("motti.button.background",
			    "Motti.Background", "white",
			    &misc_col[but_bg])))
    return 0;

  for (i = 1; i <= players; i++)
    {
      char instancename[256];
      sprintf (instancename, "motti.player%i.color", i);
      if (!alloc_Xrm_color (instancename, "Motti.Player.Color", "",
			  &player_col[i-1]))
	return 0;

      sprintf (instancename, "motti.player%iOcc.color", i);
      if (!alloc_Xrm_color (instancename, "Motti.PlayerOcc.Color", "",
			   &occupied_col[i-1]))
	return 0;
    }
  return 1;
}

static void
make_map_stipples (capital, cross, width, height)
     Pixmap capital, cross;
     int width, height;
{
  XGCValues gcvalues;

  gcvalues.line_width = 1;
  tempgc = XCreateGC (display, capital, GCLineWidth, &gcvalues);

  XSetForeground (display, tempgc, 1);
  XFillRectangle (display, capital, tempgc, 0, 0, width+1, height+1);
  XSetForeground (display, tempgc, 0);
  XFillRectangle (display, capital, tempgc, width/3, height/3,
		  width/2, height/2);

  XFillRectangle (display, cross, tempgc, 0, 0, width+1, height+1);
  XSetForeground (display, tempgc, 1);
  XDrawLine (display, cross, tempgc, width/6, height/6, (5*width)/6,
	     (5*height)/6);
  XDrawLine (display, cross, tempgc, (5*width)/6, height/6, width/6,
	     (5*height)/6);
}

#include "bitmaps/att"
#include "bitmaps/def"
#include "bitmaps/gue"

static void
make_att_buttons ()
{
  int mark_pos, mark_top, mark_height, mark_space, mark_width;
  int pos_fourth; 
  int i = 0;

  mark_top = db_val_tab.but_height / 3;
  mark_height = mark_top;
  mark_space = (db_val_tab.but_width/2 - att_width/2) / 5;
  mark_pos = mark_space;
  mark_width = 4;
  pos_fourth = db_val_tab.but_width/2 + att_width/2 + mark_space;

  XSetFunction (display, tempgc, GXcopy);
  XSetFillStyle (display, tempgc, FillSolid);
  XSetForeground (display, tempgc, misc_col[but_fg].pixel);
  while (1)
    {
      XFillRectangle (display, att_pix[i], tempgc, mark_pos,
		      mark_top, mark_width, mark_height);
      if (i < 5)
	XCopyArea (display, att_pix[i], att_pix[i+1], tempgc, 0, 0,
		   db_val_tab.but_width, db_val_tab.but_height, 0, 0);
      else
	return;
      if (++i == 3)
	mark_pos = pos_fourth;
      else
	mark_pos += mark_space;
    }
}

static void
make_att_pixs ()
{
  Pixmap pix;

  att_pix[0] = XCreatePixmap (display, main_win, db_val_tab.but_width,
			      db_val_tab.but_height, depth);
  att_pix[1] = XCreatePixmap (display, main_win, db_val_tab.but_width,
			      db_val_tab.but_height, depth);
  att_pix[2] = XCreatePixmap (display, main_win, db_val_tab.but_width,
			      db_val_tab.but_height, depth);
  att_pix[3] = XCreatePixmap (display, main_win, db_val_tab.but_width,
			      db_val_tab.but_height, depth);
  att_pix[4] = XCreatePixmap (display, main_win, db_val_tab.but_width,
			      db_val_tab.but_height, depth);
  att_pix[5] = XCreatePixmap (display, main_win, db_val_tab.but_width,
			      db_val_tab.but_height, depth);

  d_att_pix = XCreatePixmap (display, main_win, db_val_tab.but_width,
			     db_val_tab.but_height, depth);

  /* Draw the disabled attack button pixmap.  */
  XFreeGC (display, tempgc);
  tempgc = XCreateGC (display, main_win, 0, NULL);
  XSetFillStyle (display, tempgc, FillSolid);
  XSetForeground (display, tempgc, misc_col[but_fg].pixel);
  XFillRectangle (display, d_att_pix, tempgc, 0, 0,
		  db_val_tab.but_width+1,
		  db_val_tab.but_height+1);
  pix = XCreateBitmapFromData (display, main_win, att_bits, att_width,
			       att_height);
  XSetFillStyle (display, tempgc, FillStippled);
  XSetStipple (display, tempgc, pix);
  XSetTSOrigin (display, tempgc, db_val_tab.but_width/2 - att_width/2,
		db_val_tab.but_height/2 - att_height/2);
  XSetForeground (display, tempgc, misc_col[but_bg].pixel);
  XFillRectangle (display, d_att_pix, tempgc, db_val_tab.but_width/2 -
		  att_width/2, db_val_tab.but_height/2 - att_height/2,
		  att_width, att_height);

  /* Draw the attack button pixmap.  */
  XSetFillStyle (display, tempgc, FillSolid);
  XSetForeground (display, tempgc, misc_col[but_bg].pixel);
  XFillRectangle (display, att_pix[0], tempgc, 0, 0,
		  db_val_tab.but_width+1,
		  db_val_tab.but_height+1);
  XSetFillStyle (display, tempgc, FillStippled);
  XSetForeground (display, tempgc, misc_col[but_fg].pixel);
  XFillRectangle (display, att_pix[0], tempgc, db_val_tab.but_width/2 -
		  att_width/2, db_val_tab.but_height/2 - att_height/2,
		  att_width, att_height);

  XFreePixmap (display, pix);

  /* Draw attack buttons with number of remaining attacks.  */
  make_att_buttons ();
}

static void
make_def_pixs ()
{
  Pixmap pix;

  def_pix = XCreatePixmap (display, main_win, db_val_tab.but_width,
			   db_val_tab.but_height, depth);
  d_def_pix = XCreatePixmap (display, main_win, db_val_tab.but_width,
			     db_val_tab.but_height, depth);

  /* Draw the defend button.  */
  XSetFillStyle (display, tempgc, FillSolid);
  XSetForeground (display, tempgc, misc_col[but_bg].pixel);
  XFillRectangle (display, def_pix, tempgc, 0, 0,
		  db_val_tab.but_width+1,
		  db_val_tab.but_height+1);
  pix = XCreateBitmapFromData (display, main_win, def_bits, def_width,
			       def_height);
  XSetTSOrigin (display, tempgc, db_val_tab.but_width/2 - def_width/2,
		db_val_tab.but_height/2 - def_height/2);
  XSetFillStyle (display, tempgc, FillStippled);
  XSetForeground (display, tempgc, misc_col[but_fg].pixel);
  XSetStipple (display, tempgc, pix);
  XFillRectangle (display, def_pix, tempgc, db_val_tab.but_width/2 -
		  def_width/2, db_val_tab.but_height/2 - def_height/2,
		  def_width, def_height);

  /* Draw the disabled defend button.  */
  XSetFillStyle (display, tempgc, FillSolid);
  XSetForeground (display, tempgc, misc_col[but_fg].pixel);
  XFillRectangle (display, d_def_pix, tempgc, 0, 0,
		  db_val_tab.but_width+1,
		  db_val_tab.but_height+1);
  XSetFillStyle (display, tempgc, FillStippled);
  XSetForeground (display, tempgc, misc_col[but_bg].pixel);
  XFillRectangle (display, d_def_pix, tempgc, db_val_tab.but_width/2 -
		  def_width/2, db_val_tab.but_height/2 - def_height/2,
		  def_width, def_height);

  XFreePixmap (display, pix);
}

static void
make_gue_pixs ()
{
  Pixmap pix;

  gue_pix = XCreatePixmap (display, main_win, db_val_tab.but_width,
			   db_val_tab.but_height, depth);
  d_gue_pix = XCreatePixmap (display, main_win, db_val_tab.but_width,
			     db_val_tab.but_height, depth);

  /* Draw the defend button.  */
  XSetFillStyle (display, tempgc, FillSolid);
  XSetForeground (display, tempgc, misc_col[but_bg].pixel);
  XFillRectangle (display, gue_pix, tempgc, 0, 0,
		  db_val_tab.but_width+1,
		  db_val_tab.but_height+1);
  pix = XCreateBitmapFromData (display, main_win, gue_bits, gue_width,
			       gue_height);
  XSetFillStyle (display, tempgc, FillStippled);
  XSetTSOrigin (display, tempgc, db_val_tab.but_width/2 - gue_width/2,
		db_val_tab.but_height/2 - gue_height/2);
  XSetForeground (display, tempgc, misc_col[but_fg].pixel);
  XSetStipple (display, tempgc, pix);
  XFillRectangle (display, gue_pix, tempgc, db_val_tab.but_width/2 -
		  gue_width/2, db_val_tab.but_height/2 - gue_height/2,
		  gue_width, gue_height);

  /* Draw the disabled defend button.  */
  XSetFillStyle (display, tempgc, FillSolid);
  XSetForeground (display, tempgc, misc_col[but_fg].pixel);
  XFillRectangle (display, d_gue_pix, tempgc, 0, 0,
		  db_val_tab.but_width+1,
		  db_val_tab.but_height+1);
  XSetFillStyle (display, tempgc, FillStippled);
  XSetForeground (display, tempgc, misc_col[but_bg].pixel);
  XFillRectangle (display, d_gue_pix, tempgc, db_val_tab.but_width/2 -
		  gue_width/2, db_val_tab.but_height/2 - gue_height/2,
		  gue_width, gue_height);

  XFreePixmap (display, pix);
}

static void
alloc_gcs ()
{
  XGCValues gcval = {0};
  Pixmap capital_stipple, cross_stipple;

  capital_stipple = XCreatePixmap (display, main_win, MAPSQUARESIZE,
				   MAPSQUARESIZE, 1);
  cross_stipple = XCreatePixmap (display, main_win, MAPSQUARESIZE,
				 MAPSQUARESIZE, 1);

  make_map_stipples (capital_stipple, cross_stipple, MAPSQUARESIZE,
		     MAPSQUARESIZE);

  /* These three are set to draw capitals. */
  gcval.foreground = misc_col[capital_col].pixel;
  gcval.fill_style = FillStippled;
  gcval.stipple = capital_stipple;
  capitalgc = XCreateGC (display, main_win, GCForeground | GCFillStyle
			 | GCStipple, &gcval);
  gcval.foreground = misc_col[cross_col].pixel;
  gcval.stipple = cross_stipple;
  crossgc = XCreateGC (display, main_win, GCForeground | GCFillStyle |
		       GCStipple, &gcval);


  /* No fields need to be set, since foreground color is changed every
   * time */
  /* Note to myself: change this to make BW window. */
  mapgc = XCreateGC (display, main_win, 0, &gcval);
}

int but_width[3];
int but_height;

static void
set_hints (min_width, min_height, argv, argc)
     int min_width, min_height;
     char **argv;
     int argc;
{
  XWMHints *wm_hints;
  XClassHint *class_hints;
  XTextProperty window_name_prop, icon_name_prop;
  /* Add version number.  */
  char name_vers[12] = "motti ";
  char *window_name = name_vers;
  char *icon_name = "motti";
  XSizeHints *size_hints;
  snprintf (&name_vers[6], 6, "%s", VERSION);

  if (!XStringListToTextProperty (&window_name, 1, &window_name_prop)
      || !XStringListToTextProperty (&icon_name, 1, &icon_name_prop))
    die (alloc_err_str(ERR_MALLOC, (char *) NULL));

  /* Allocate needed structures for properties */
  size_hints = XAllocSizeHints ();
  wm_hints = XAllocWMHints ();
  class_hints = XAllocClassHint ();

  /* TODO: IIRC here is something wrong here, not sure what.  */
  size_hints->flags = PPosition | PSize | PMinSize;
  size_hints->min_width = min_width;
  size_hints->min_height = min_height;

  wm_hints->initial_state = NormalState;
  wm_hints->input = True;

  /* TODO: IIRC these shouldn't be hardcoded.  */
  class_hints->res_name = "motti";
  class_hints->res_class = "motti";

  XSetWMProperties (display, main_win, &window_name_prop,
		    &icon_name_prop, argv, argc, size_hints, wm_hints,
		    class_hints);

  /* Free all allocated structures */
  XFree (size_hints);
  XFree (wm_hints);
  XFree (class_hints);
  XFree (window_name_prop.value);
  XFree (icon_name_prop.value);
}

/* TODO: Change this to make BW support. */
extern void
open_windows (argc, argv)
     int argc;
     char **argv;
{
  XSetWindowAttributes win_attribs;
  int x = 0, y = 0, width, height;
  char *str_type;
  XrmValue value;

  if (!db_val_tab.geom_str)
    {
      XrmValue value;
      char *str_type;
      XrmGetResource (DB, "Motti.Geometry", "motti.geometry",
		      &str_type, &value);
      db_val_tab.geom_str = value.addr;
    }
  XParseGeometry (db_val_tab.geom_str, &x, &y, &width, &height);

  if (XrmGetResource (DB, "Motti.Button.Width",
		      "motti.button.width", &str_type, &value))
    db_val_tab.but_width = atoi (value.addr);
  else
    db_val_tab.but_width = 80;
  if (XrmGetResource (DB, "Motti.Button.Height",
		      "motti.button.height", &str_type, &value))
    db_val_tab.but_height = atoi (value.addr);
  else
    db_val_tab.but_height = 20;

  win_attribs.background_pixel = misc_col[bg_col].pixel;
  main_win = XCreateWindow (display, RootWindow(display, screen_num),
			    x, y, width, height, 0, CopyFromParent,
			    InputOutput, CopyFromParent, CWBackPixel,
			    &win_attribs);

  alloc_gcs ();
  make_att_pixs ();
  make_att_buttons ();
  make_def_pixs ();
  make_gue_pixs ();
  XFreeGC (display, tempgc);

  win_attribs.border_pixel = misc_col[but_fg].pixel;
  but_win[ATT] = XCreateWindow (display, main_win, X_MARGIN, Y_MARGIN,
				db_val_tab.but_width,
				db_val_tab.but_height, 1,
				CopyFromParent, InputOutput,
				CopyFromParent, CWBorderPixel,
				&win_attribs);

  but_win[DEF] = XCreateWindow (display, main_win,
				db_val_tab.but_width + X_MARGIN*2,
				Y_MARGIN, db_val_tab.but_width,
				db_val_tab.but_height, 1,
				CopyFromParent, InputOutput,
				CopyFromParent, CWBorderPixel,
				&win_attribs);

  but_win[GUE] = XCreateWindow (display, main_win,
				db_val_tab.but_width*2 + X_MARGIN*3,
				Y_MARGIN, db_val_tab.but_width,
				db_val_tab.but_height, 1,
				CopyFromParent, InputOutput,
				CopyFromParent, CWBorderPixel,
				&win_attribs);

  x = X_MARGIN;
  /* There must be room for action buttons above the map.  */
  y = Y_MARGIN*2 + db_val_tab.but_height;
  /* TODO: Scrollable and resizeable map window.  */
  win_attribs.border_pixel = misc_col[fg_col].pixel;
  win_attribs.background_pixel = misc_col[map_bg_col].pixel;
  map_win = XCreateWindow (display, main_win, x, y, game_map.width *
			   MAPSQUARESIZE, game_map.height *
			   MAPSQUARESIZE, 2, CopyFromParent,
			   InputOutput, CopyFromParent, CWBorderPixel
			   | CWBackPixel, &win_attribs);

  set_hints (x + game_map.width * MAPSQUARESIZE + 2 * X_MARGIN, y +
	     MINMAPHEIGHT*MAPSQUARESIZE + Y_MARGIN, argv, argc);
}

#include "bitmaps/map_pointer_active"
#include "bitmaps/map_pointer_disabled"
#include "bitmaps/map_pointer_mask"
Cursor active_cursor, disabled_cursor;

/* TODO: Make this work.  */
extern void
create_cursors ()
{
  Pixmap mask, active, disabled;
  XColor white, black;
  Colormap default_colormap;

  default_colormap = DefaultColormap (display, screen_num);
  white.pixel = WhitePixel (display, screen_num);
  black.pixel = BlackPixel (display, screen_num);
  XQueryColor (display, default_colormap, &white);
  XQueryColor (display, default_colormap, &black);

  mask = XCreateBitmapFromData (display, main_win,
				map_pointer_mask_bits,
				map_pointer_mask_width,
				map_pointer_mask_height);
  active = XCreateBitmapFromData (display, main_win,
				  map_pointer_active_bits,
				  map_pointer_active_width,
				  map_pointer_active_height);
  disabled = XCreateBitmapFromData (display, main_win,
				    map_pointer_disabled_bits,
				    map_pointer_disabled_width,
				    map_pointer_disabled_height);

  active_cursor = XCreatePixmapCursor (display, active, mask, &black,
				       &white,
				       map_pointer_active_x_hot,
				       map_pointer_active_y_hot);
  disabled_cursor = XCreatePixmapCursor (display, disabled, mask,
					 &black, &white,
					 map_pointer_disabled_x_hot,
					 map_pointer_disabled_y_hot);

  XFreePixmap (display, mask);
  XFreePixmap (display, active);
  XFreePixmap (display, disabled);
}

extern void
kill_x ()
{
  XCloseDisplay (display);
}
