/* Colormap management library
   Copyright 1998 Cuspy Solutions, Inc.

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 <assert.h>
#include <stdlib.h>
#include "config.h"       /* for alloca */
#include "cmap_mgr.h"

typedef struct cmap {
  int using_private_cmap;
  Colormap colormap;
  Display *display;
  int num_cells;
  int *colors;
} PCMAP;

void CMAP_SetColor(CMAP cmap, Pixel index, XColor *color)
{
  XColor tmp;

  cmap->colors[index] = 1;
  color->pixel = index;
  tmp = *color;
  tmp.flags = DoRed | DoGreen | DoBlue;
  XStoreColor(cmap->display, cmap->colormap, &tmp);
}

Colormap CMAP_GetColormap(CMAP cmap)
{
  return cmap->colormap;
}

CMAP CMAP_Create(Display *display, int screen, Visual *visual, int alloc,
		 int use_pcmap)
{
  CMAP cmap = (CMAP)malloc(sizeof(PCMAP));

  cmap->display = display;
  if (use_pcmap) {
    XVisualInfo info, *new_info;
    int n;

    info.visualid = XVisualIDFromVisual(visual);
    new_info = XGetVisualInfo(display, VisualIDMask, &info, &n);
    if (new_info->class != PseudoColor)
      fprintf(stderr, "Was not able to get a private colormap.\n");
    else {
      XColor white, black;
      cmap->using_private_cmap = 1;
      cmap->num_cells = DisplayCells(display, screen);
      cmap->colors = (int *)calloc(cmap->num_cells, sizeof(Pixel));
      cmap->colormap = XCreateColormap(display, RootWindow(display, screen),
                                       visual, alloc);

      XParseColor(display, CMAP_GetColormap(cmap), "black", &black);
      CMAP_SetColor(cmap, BlackPixel(display, screen), &black);
      XParseColor(display, CMAP_GetColormap(cmap), "white", &white);
      CMAP_SetColor(cmap, WhitePixel(display, screen), &white);

      return cmap;
    }
  }

  cmap->colormap = DefaultColormap(display, screen);
  cmap->using_private_cmap = 0;

  return cmap;
}

int CMAP_AllocColor(CMAP cmap, XColor *color)
{
  if (!cmap->using_private_cmap)
    return !!XAllocColor(cmap->display, cmap->colormap, color);
  else {
    int i;
    XColor *colors = alloca(sizeof(XColor) * cmap->num_cells);

    for (i = 0; i < cmap->num_cells; i++) {
      colors[i].pixel = i;
      colors[i].flags = DoRed | DoGreen | DoBlue;
    }
    XQueryColors(cmap->display, cmap->colormap, colors, cmap->num_cells);

    for (i = 0; i < cmap->num_cells; i++)
      if (cmap->colors[i] && colors[i].red == color->red &&
          colors[i].green == colors->green && colors[i].blue == color->blue) {
        color->pixel = i;
        return 1;
      }

    for (i = 0; i < cmap->num_cells; i++)
      if (!cmap->colors[i]) {
        CMAP_SetColor(cmap, i, color);
        return 1;
      }
  }

  return 0;
}

int CMAP_AllocNamedColor(CMAP cmap, char *colorname, XColor *color)
{
  if (!XParseColor(cmap->display, cmap->colormap, colorname, color))
    return 0;
  return CMAP_AllocColor(cmap, color);
}

/* We need to have a version of Xpm the supports the colormap closures.  The
 * earliest one that does this is 4.9 which is also known as 3.4i.
 */
#if XpmIncludeVersion < 30409

static int XPMAllocColor(Display *display, Colormap colormap, char *colorname,
                         XColor *color, void *closure)
{
  CMAP cmap = (CMAP)closure;

  assert(display == cmap->display);
  assert(colormap == cmap->colormap);
  if (colorname)
    if (!XParseColor(cmap->display, cmap->colormap, colorname, color))
      return -1;
  return CMAP_AllocColor(cmap, color);
}

static int XPMFreeColors(Display *display, Colormap colormap, Pixel *pixels,
			 int npixels, void *closure)
{
  int i;
  CMAP cmap = (CMAP)closure;

  for (i = 0; i < npixels; i++)
    cmap->colors[pixels[i]] = 0;
  return 0;
}

XpmAllocColorFunc CMAP_GetXPMAllocColorFunc(void)
{
  return XPMAllocColor;
}

XpmFreeColorsFunc CMAP_GetXPMFreeColorsFunc(void)
{
  return XPMFreeColors;
}

void *CMAP_GetXPMClosure(CMAP cmap)
{
  return (void *)cmap;
}

#endif
