/*
 * static char *rcsid_xutil_c =
 *   "$Id: xutil.c,v 1.29 1997/03/09 04:28:06 master Exp $";
 */

/*
    CrossFire, A Multiplayer game for X-windows

    Copyright (C) 1994 Mark Wedel
    Copyright (C) 1992 Frank Tore Johansen

    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., 675 Mass Ave, Cambridge, MA 02139, USA.

    The author can be reached via e-mail to master@rahul.net
*/

#include <global.h>
#include <funcpoint.h>
#include <loader.h>
#ifdef Xpm_Pix
#include <X11/xpm.h>
#endif

/* This now merges the color alias and color name table together.
 * The first entry is the color as it is referred to in objects,
 * the second is the actual screen/X11 color to use.  NUM_COLORS is now
 * defined in global.h - if adding colors, increase that value, and everything
 * else should work appropriately.
 */
char *colorname[NUM_COLORS][2] = {
{"black",	"Black"},		/* 0  */
{"white",	"White"},		/* 1  */
{"blue",	"Navy"},		/* 2  */
{"red",		"Red"},			/* 3  */
{"orange",	"Orange"},		/* 4  */
{"light_blue",	"DodgerBlue"},		/* 5  */
{"dark_orange",	"DarkOrange2"},		/* 6  */
{"green",	"SeaGreen"},		/* 7  */
{"light_green",	"DarkSeaGreen"},	/* 8  - Used for window background color */
{"grey",	"Grey50"},		/* 9  */
{"brown",	"Sienna"},		/* 10 */
{"yellow",	"Gold"},		/* 11 */
{"khaki",	"Khaki"}		/* 12 */ 
};



/*
 * Converts between Fontindex and XChar2b types.
 * Used in global.h, for draw_face (in face, FontindextoXChars 
 * is defined to this function name
 */

XChar2b fontindex_to_XChar2b(Fontindex s)
{
  XChar2b c;

  c.byte1 = s/256;
  c.byte2 = s%256;

  return c;
}


/*
 * ReadPixmaps(): When color pixmaps are used instead of fonts, this function
 * does the actual reading of pixmap-file.  This function is based largely on
 * the ReadBitmaps function.  By Mark Wedel (master@rahul.net)
 */

/* New method: Pixmaps are stored as a montage on the disk (in several
 * files).  This way, we only need to call XCreatePixmap... a couple
 * times, and the rest are XCopyArea.  This is much faster, since
 * the XPM library does not seem to be especially efficient about loading
 * large numbers of pixmaps.
 *
 * Return true if we have gone to a private colormap.
 */

int ReadPixmaps(Display *gdisp, Pixmap **pixmaps, Pixmap **masks,
    Colormap *cmap) {
#ifdef Xpm_Pix

    int	file_num, x, y, num_files, image_num=0,i, image_left, error,depth;
    char	buf[MAX_BUF];
    Pixmap  pixmap, mask;
    Window	root = RootWindow (gdisp,DefaultScreen(gdisp));
    XpmAttributes xpmatribs;
    int		use_private_cmap=0;

    /* This function is called before the game gc's are created.  So
     * we create one for our own use here.
     */  
    GC	gc= XCreateGC(gdisp, root, 0, NULL), gc1;
    if (*cmap) {
	xpmatribs.valuemask = XpmColormap;
	xpmatribs.colormap=*cmap;
    }
    else  xpmatribs.valuemask=0;

    /* We need a depth 1 gc.  So create a depth 1 pixmap and use that */
    pixmap = XCreatePixmap(gdisp, root, 1, 1, 1);
    gc1= XCreateGC(gdisp, pixmap, 0, NULL);
    XSetGraphicsExposures(gdisp, gc, False);
    XSetGraphicsExposures(gdisp, gc1, False);
    XFreePixmap(gdisp, pixmap);

    depth = DefaultDepth(gdisp,DefaultScreen(gdisp));

    if (!nrofpixmaps)
      nrofpixmaps = ReadBmapNames ();
    image_left = nrofpixmaps;

    /* The processes that create a pixmap already allocates the space for
     * the pixmap data.  Therefor, only space for the pointers to that data
     * needs to be allocated.  The same might apply for the function
     * that creates bitmaps below, but I am not as sure in that case.
     * Mark Wedel (master@rahul.net)
     */

    *pixmaps = (Pixmap *) malloc(sizeof(Pixmap *) * nrofpixmaps);
    *masks = (Pixmap *) malloc(sizeof(Pixmap *) * nrofpixmaps);
    for (i=0; i < nrofpixmaps; i++)
      (*pixmaps)[i] = 0;

    num_files = nrofpixmaps/ (XPM_MONTAGE_X * XPM_MONTAGE_Y);
    if (nrofpixmaps % (XPM_MONTAGE_X * XPM_MONTAGE_Y)) num_files ++;

    LOG(llevDebug,"Building color pixmaps...");
    for (file_num=1; file_num<=num_files; file_num++) {
	sprintf (buf,"%s/%s.pix.%d",LIBDIR, FONTNAME,file_num);
	LOG (llevDebug,"\n\tprocessing file %s",buf);
again:	error=XpmReadFileToPixmap(gdisp, root,
	      buf,&pixmap,&mask, &xpmatribs);
	if (error!=XpmSuccess) {
		if (error==XpmColorFailed && !use_private_cmap) {
			LOG(llevError,"XPM could not allocate colors - trying to switch to private colormap..");
			if (!(*cmap=XCopyColormapAndFree(gdisp, *cmap)))
			    LOG(llevError,"Failed...\n");
			else
			    LOG(llevError,"Successful...\n");
			xpmatribs.colormap=*cmap;
			use_private_cmap=1;
			goto again;
		}
		LOG(llevError,"Error creating pixmap %s, error %d.\n",buf, error);
	}
	else do {
	    x=0;
	    y=0;
	    for (i=0; i<  (XPM_MONTAGE_X * XPM_MONTAGE_Y); i++) {
		if((*pixmaps)[image_num] != 0)
			LOG(llevError,"Warning: two entries in bmaps: %d\n",i);
		else {
		    (*pixmaps)[image_num] = XCreatePixmap(
			gdisp, root, 24, 24, depth);
		    (*masks)[image_num]  = XCreatePixmap(
			gdisp, root, 24, 24, 1);

		    XCopyArea(gdisp, pixmap, (*pixmaps)[image_num],
			gc, x*24, y*24, 24, 24, 0, 0);
		    XCopyArea(gdisp, mask, (*masks)[image_num],
			gc1, x*24, y*24, 24, 24, 0, 0);
		}
		x++;
		if (x==XPM_MONTAGE_X) {
			y++;
			x=0;
		}

		image_num++;
		image_left--;
		if (!image_left) break;
		if(!(image_num % (CHECK_ACTIVE_MAPS/10))) {
			LOG(llevDebug,".");
	          	(*process_active_maps_func)(); /* Process active maps as often as possible */
		}
	    }
	} while (image_num<nroffiles && (image_num % (XPM_MONTAGE_X * XPM_MONTAGE_Y)));
	XFreePixmap(gdisp, pixmap);
	XFreePixmap(gdisp, mask);
    }
    XFreeGC(gdisp, gc);
    XFreeGC(gdisp, gc1);

    /* Check for any holes.  There should not be.  This is an interim check
     * to use until the xbm_values is removed.  The program that 
     * creates the bitmap/pixmap files just adds the bitmaps, so holes should
     * not be in the image file.
     */
    for (i = 0; i < nrofpixmaps; i++)
	if ((*pixmaps)[i] == 0) {
	    LOG(llevDebug, "Warning, pixmap %d is not defined, setting it to blank\n", i);
	    pixmaps[i] = pixmaps[blank_face->number];
	}
    LOG(llevDebug,"done.\n");
    return use_private_cmap;
#else
    return 0;	/* Prevents some warning messages */
#endif
}

/* This frees all the pixmaps.  This not only makes for better code,
 * some XServers may not free the memory used by the pixmaps unless it is done
 * this way.  For color pixmaps, this will be called twice - the
 * first time, the normal pixmaps will be passed through, the second
 * time, it will be called with the masks as the pixmaps argument.
 */

void free_pixmaps(Display *gdisp, Pixmap *pixmaps)
{
    int i;

    for (i=0; i < nrofpixmaps; i++) {
	if (pixmaps[i]!=0) {
	    XFreePixmap(gdisp, pixmaps[i]);
	    pixmaps[i] = 0;
	}
    }
}

/*
 * ReadBitmaps(): When bitmaps are used instead of fonts, this function
 * does the actual reading of bitmap-file, and returns the
 * bitmaps array.  It assumes the bitmap file found in the LIBDIR 
 * directory.
 */
  
  
Pixmap *ReadBitmaps(Display *d) {
    char buf[MAX_BUF];
    FILE *fp;
    int i, count = 0,comp;
    Pixmap *pixmaps;
  
    if (!nrofpixmaps)
	nrofpixmaps = ReadBmapNames ();

    pixmaps = (Pixmap *) malloc(sizeof(Pixmap) * nrofpixmaps);
    for (i=0; i < nrofpixmaps; i++)
	pixmaps[i] = 0;
  
    sprintf (buf,"%s/%s.cfb",LIBDIR, FONTNAME);
    if ((fp = open_and_uncompress(buf,0,&comp))==NULL) {
	perror("Can't open crossfire.cfb file");
	exit(-1);
      }
  
    LOG(llevDebug,"Building ximages...");
    for (i=0; i<nroffiles; i++) {
	if(pixmaps[i] != 0) {
	    LOG(llevError,"Warning: two entries in bmaps: %d\n",i);
	    continue;
	}

	if (fread (buf, 24 * 3, 1, fp) != 1) {
	    LOG(llevError,"Warning: cannot read from file\n");
	    break;
	}

	pixmaps[i] =  XCreateBitmapFromData
	    (d, RootWindow (d, DefaultScreen(d)), buf, 24, 24);

	if (!pixmaps[i]) {
	    LOG(llevError,"Warning: Cannot create Pixmap %d\n",i);
	    pixmaps[i] = 0;
	}

	if(++count > CHECK_ACTIVE_MAPS/10) {
	    printf (".");
	    fflush (stdout);
	    count = 0;
	    (*process_active_maps_func)();
	}
    }
    close_and_delete(fp, comp);

      LOG(llevDebug,"done\n");

    /*
     * Now fill out the unused holes with pointers to a blank pixmap
     * to avoid crashes in case trying to draw a nonexistant pixmap.
     */
    for (i = 0; i < nrofpixmaps; i++)
	if (pixmaps[i] == 0)
	    pixmaps[i] = pixmaps[blank_face->number];
    return pixmaps;
}


/*
 * This function adds the path to the fontpath of the given display.
 * It's mostly copied from the X11R5 distribution.
 */

void set_font_path(Display *dpy, char *path) {
  char **currentList = NULL; int ncurrent = 0;
  char **directoryList = NULL; int ndirs = 0;

  currentList = XGetFontPath (dpy, &ncurrent);
  if(currentList==NULL) {
    LOG(llevError,"Unable to get old font path.\n");
    return;
  }
  {
    register char *cp = path;
    ndirs=1;
    while((cp=strchr(cp, ','))!=NULL)
      ndirs++,cp++;
    directoryList=(char **) malloc(ndirs*sizeof(char *));
    if(!directoryList) {
      LOG(llevError,"Unable to allocate memory for font path directory.\n");
      return;
    }
  }
  {
    int i=0;
    char *cp = path;
    directoryList[i++]=cp;
    while((cp=strchr(cp, ','))!=NULL)
      directoryList[i++]=cp+1,
      *cp++='\0';
    if(i!=ndirs) {
      LOG(llevError,"Internal error, only parsed %d of %d dirs.\n",i,ndirs);
      return;
    }
  }
  {
    int nnew=ndirs+ncurrent;
    char **newList = (char **) malloc (nnew * sizeof(char *));

    if(!newList) {
      LOG(llevError,"Couldn't get memory for new fontpath.\n");
      return;
    }
/* #if defined(SYSV) || defined(SVR4) */
    memcpy((void *)newList,(void *)directoryList,
           (unsigned) (ndirs*sizeof (char *)));
    memcpy((void *) (newList + ndirs), (void *) currentList,
           (unsigned) (ncurrent*sizeof (char *)));
    XSetFontPath(dpy,newList, nnew);
    free((char *)newList);
  }
  if (directoryList)
    free((char *) directoryList);
  if (currentList)
    XFreeFontPath (currentList);
}

/*
 * Checks if "crossfire" is present somewhere in the fontpath of
 * the given display.
 */

int check_font_path(Display *dpy) {
  int count;
  char **list;

  list = XListFonts(dpy, font_graphic, 1, &count);
  LOG(llevDebug, "Matching fonts to %s: %d (%s)\n",
      font_graphic,count,count?*list:"");
  XFreeFontNames(list);
  return count;
}

/*
 * Uses check_font_path() and set_font_path() to check and, if needed
 * fix the fontpath for the player.
 * Function changed around to make it useful for the client.
 * Passing the player struct to this is not required - xio.c
 * can use the return value to set the use_pixmaps value in the
 * player struct.  name is only passed to give better error
 * messages.
 */

int fixfontpath(Display *disp, char *name) {

  if (check_font_path(disp))
    return 0;

  if(settings.fixfontpath) {
      LOG(llevError,"Trying to fix fontpath for display %s.\n",name);
      fflush(logfile);
      set_font_path(disp,settings.fontdir);
      if(check_font_path(disp))
        return 0;
  }
  LOG(llevError,"Failed, switching to pixmaps (this might take a while).\n");
  sprintf(errmsg,"Display %s doesn't have the right fontpath.",name);
  return 1;
}

/*
 * allocate_colors() tries to get enough colors for the game-window.
 * If it fails, it tries to use a private colormap.
 * If that also fails, it return 1, indicating to the calling
 * function that black and white should be used.
 *
 * Function arguments became a bit more complicated in v0.91.5.
 * The functionality is the same, but instead of passing the player
 * structure, just pass the values that are needed.  This enables
 * the new client to use the function also.
 *
 * Original function by Tyler Van Gorder (tvangod@icst.csuchico.edu),
 * June 1, 1992
 */

int allocate_colors(Display *disp, Window w, long screen_num,
	Colormap *colormap, XColor discolor[NUM_COLORS])
{
  int i, tried = 0, depth=0, iscolor;
  Status status;
  Visual *vis;
  XColor exactcolor;

  /* Should perhaps be setable by player? */
  if(settings.blackwhite) {
    return 0;
  }
  iscolor = 1;
  vis = DefaultVisual(disp,screen_num);
  if (vis->class >= StaticColor) {
    *colormap = DefaultColormap(disp,screen_num);
    depth = DefaultDepth(disp,screen_num);
  }
  else {
    *colormap=(Colormap )NULL;
    LOG(llevError,"Switching to black and white.\n");
    LOG(llevError,"You have a black and white terminal.\n");
    return 0;
  }
try_private:
  if (depth > 3 && iscolor) {
    unsigned long pixels[13];
    for (i=0; i<13; i++){
      status = XLookupColor(disp,*colormap, colorname[i][1],&exactcolor,
                            &discolor[i]);
      if (!status){
        LOG(llevError,"Can't find colour %s.\n", colorname[i]);
        LOG(llevError,"Switching to black and white.\n");
        iscolor = 0;
        break;
      }
      status = XAllocColor(disp,*colormap,&discolor[i]);
      if (!status) {
        if (!tried) {
          LOG(llevError, "Not enough colours. Trying a private colourmap.\n");
          XFreeColors(disp, *colormap, pixels, i-1, 0);
          *colormap = XCreateColormap(disp, w, vis, AllocNone);
          XSetWindowColormap(disp, w, *colormap);
          tried = 1;
          goto try_private;
        } else {
          LOG(llevError, "Failed. Switching to black and white.\n");
          iscolor = 0;
          break;
        }
      }
      pixels[i] = discolor[i].pixel;
    }
  }
  return iscolor;
}
