/* Copyright (c) Mark J. Kilgard, 1994, 1996, 1997. */

/* This program is freely distributable without licensing fees 
   and is provided without guarantee or warrantee expressed or 
   implied. This program is -not- in the public domain. */

#include <stdlib.h>
#include "glutint.h"

#define CLAMP(i) ((i) > 1.0 ? 1.0 : ((i) < 0.0 ? 0.0 : (i)))

/* CENTRY */
void GLUTAPIENTRY
glutSetColor(int ndx, GLfloat red, GLfloat green, GLfloat blue)
{
  GLUTcolormap *cmap, *newcmap;
  XVisualInfo *vis;
  XColor color;
  int i;

  if (__glutCurrentWindow->renderWin == __glutCurrentWindow->win) {
    cmap = __glutCurrentWindow->colormap;
    vis = __glutCurrentWindow->vis;
  } else {
    cmap = __glutCurrentWindow->overlay->colormap;
    vis = __glutCurrentWindow->overlay->vis;
    if (ndx == __glutCurrentWindow->overlay->transparentPixel) {
      __glutWarning(
        "glutSetColor: cannot set color of overlay transparent index %d\n",
        ndx);
      return;
    }
  }

  if (!cmap) {
    __glutWarning("glutSetColor: current window is RGBA");
    return;
  }
#if defined(_WIN32)
  if (ndx >= 256 ||     /* always assume 256 colors on Win32 */
#else
  if (ndx >= vis->visual->map_entries ||
#endif
    ndx < 0) {
    __glutWarning("glutSetColor: index %d out of range", ndx);
    return;
  }
  if (cmap->refcnt > 1) {
    newcmap = __glutAssociateNewColormap(vis);
    cmap->refcnt--;
    /* Wouldn't it be nice if XCopyColormapAndFree could be
       told not to free the old colormap's entries! */
    for (i = cmap->size - 1; i >= 0; i--) {
      if (i == ndx) {
        /* We are going to set this cell shortly! */
        continue;
      }
      if (cmap->cells[i].component[GLUT_RED] >= 0.0) {
        color.pixel = i;
        newcmap->cells[i].component[GLUT_RED] =
          cmap->cells[i].component[GLUT_RED];
        color.red = (GLfloat) 0xffff *
          cmap->cells[i].component[GLUT_RED];
        newcmap->cells[i].component[GLUT_GREEN] =
          cmap->cells[i].component[GLUT_GREEN];
        color.green = (GLfloat) 0xffff *
          cmap->cells[i].component[GLUT_GREEN];
        newcmap->cells[i].component[GLUT_BLUE] =
          cmap->cells[i].component[GLUT_BLUE];
        color.blue = (GLfloat) 0xffff *
          cmap->cells[i].component[GLUT_BLUE];
        color.flags = DoRed | DoGreen | DoBlue;
#if defined(_WIN32)
        if (IsWindowVisible(__glutCurrentWindow->win)) {
          XHDC = __glutCurrentWindow->hdc;
        } else {
          XHDC = 0;
        }
#endif
        XStoreColor(__glutDisplay, newcmap->cmap, &color);
      } else {
        /* Leave unallocated entries unallocated. */
      }
    }
    cmap = newcmap;
    if (__glutCurrentWindow->renderWin == __glutCurrentWindow->win) {
      __glutCurrentWindow->colormap = cmap;
      __glutCurrentWindow->cmap = cmap->cmap;
    } else {
      __glutCurrentWindow->overlay->colormap = cmap;
      __glutCurrentWindow->overlay->cmap = cmap->cmap;
    }
    XSetWindowColormap(__glutDisplay,
      __glutCurrentWindow->renderWin, cmap->cmap);

#if !defined(_WIN32)
    {
      GLUTwindow *toplevel;

      toplevel = __glutToplevelOf(__glutCurrentWindow);
      if (toplevel->cmap != cmap->cmap) {
        __glutPutOnWorkList(toplevel, GLUT_COLORMAP_WORK);
      }
    }
#endif
  }
  color.pixel = ndx;
  red = CLAMP(red);
  cmap->cells[ndx].component[GLUT_RED] = red;
  color.red = (GLfloat) 0xffff *red;
  green = CLAMP(green);
  cmap->cells[ndx].component[GLUT_GREEN] = green;
  color.green = (GLfloat) 0xffff *green;
  blue = CLAMP(blue);
  cmap->cells[ndx].component[GLUT_BLUE] = blue;
  color.blue = (GLfloat) 0xffff *blue;
  color.flags = DoRed | DoGreen | DoBlue;
#if defined(_WIN32)
  if (IsWindowVisible(__glutCurrentWindow->win)) {
    XHDC = __glutCurrentWindow->hdc;
  } else {
    XHDC = 0;
  }
#endif
  XStoreColor(__glutDisplay, cmap->cmap, &color);
}

GLfloat GLUTAPIENTRY
glutGetColor(int ndx, int comp)
{
  GLUTcolormap *colormap;
  XVisualInfo *vis;

  if (__glutCurrentWindow->renderWin == __glutCurrentWindow->win) {
    colormap = __glutCurrentWindow->colormap;
    vis = __glutCurrentWindow->vis;
  } else {
    colormap = __glutCurrentWindow->overlay->colormap;
    vis = __glutCurrentWindow->overlay->vis;
    if (ndx == __glutCurrentWindow->overlay->transparentPixel) {
      __glutWarning("glutGetColor: requesting overlay transparent index %d\n",
        ndx);
      return -1.0;
    }
  }

  if (!colormap) {
    __glutWarning("glutGetColor: current window is RGBA");
    return -1.0;
  }
#if defined(_WIN32)
#define OUT_OF_RANGE_NDX(ndx) (ndx >= 256 || ndx < 0)
#else
#define OUT_OF_RANGE_NDX(ndx) (ndx >= vis->visual->map_entries || ndx < 0)
#endif
  if (OUT_OF_RANGE_NDX(ndx)) {
    __glutWarning("glutGetColor: index %d out of range", ndx);
    return -1.0;
  }
  return colormap->cells[ndx].component[comp];
}

void GLUTAPIENTRY
glutCopyColormap(int winnum)
{
  GLUTwindow *window = __glutWindowList[winnum - 1];
  GLUTcolormap *oldcmap, *newcmap;
  XVisualInfo *dstvis;

  if (__glutCurrentWindow->renderWin == __glutCurrentWindow->win) {
    oldcmap = __glutCurrentWindow->colormap;
    dstvis = __glutCurrentWindow->vis;
    newcmap = window->colormap;
  } else {
    oldcmap = __glutCurrentWindow->overlay->colormap;
    dstvis = __glutCurrentWindow->overlay->vis;
    if (!window->overlay) {
      __glutWarning("glutCopyColormap: window %d has no overlay", winnum);
      return;
    }
    newcmap = window->overlay->colormap;
  }

  if (!oldcmap) {
    __glutWarning("glutCopyColormap: destination colormap must be color index");
    return;
  }
  if (!newcmap) {
    __glutWarning(
      "glutCopyColormap: source colormap of window %d must be color index",
      winnum);
    return;
  }
  if (newcmap == oldcmap) {
    /* Source and destination are the same; now copy needed. */
    return;
  }
#if !defined(_WIN32)
  /* Play safe: compare visual IDs, not Visual*'s. */
  if (newcmap->visual->visualid == oldcmap->visual->visualid) {
#endif
    /* Visuals match!  "Copy" by reference...  */
    __glutFreeColormap(oldcmap);
    newcmap->refcnt++;
    if (__glutCurrentWindow->renderWin == __glutCurrentWindow->win) {
      __glutCurrentWindow->colormap = newcmap;
      __glutCurrentWindow->cmap = newcmap->cmap;
    } else {
      __glutCurrentWindow->overlay->colormap = newcmap;
      __glutCurrentWindow->overlay->cmap = newcmap->cmap;
    }
    XSetWindowColormap(__glutDisplay, __glutCurrentWindow->renderWin,
      newcmap->cmap);
#if !defined(_WIN32)
    __glutPutOnWorkList(__glutToplevelOf(window), GLUT_COLORMAP_WORK);
  } else {
    GLUTcolormap *copycmap;
    XColor color;
    int i, last;

    /* Visuals different - need a distinct X colormap! */
    copycmap = __glutAssociateNewColormap(dstvis);
    /* Wouldn't it be nice if XCopyColormapAndFree could be
       told not to free the old colormap's entries! */
    last = newcmap->size;
    if (last > copycmap->size) {
      last = copycmap->size;
    }
    for (i = last - 1; i >= 0; i--) {
      if (newcmap->cells[i].component[GLUT_RED] >= 0.0) {
        color.pixel = i;
        copycmap->cells[i].component[GLUT_RED] =
          newcmap->cells[i].component[GLUT_RED];
        color.red = (GLfloat) 0xffff *
          newcmap->cells[i].component[GLUT_RED];
        copycmap->cells[i].component[GLUT_GREEN] =
          newcmap->cells[i].component[GLUT_GREEN];
        color.green = (GLfloat) 0xffff *
          newcmap->cells[i].component[GLUT_GREEN];
        copycmap->cells[i].component[GLUT_BLUE] =
          newcmap->cells[i].component[GLUT_BLUE];
        color.blue = (GLfloat) 0xffff *
          newcmap->cells[i].component[GLUT_BLUE];
        color.flags = DoRed | DoGreen | DoBlue;
        XStoreColor(__glutDisplay, copycmap->cmap, &color);
      }
    }
  }
#endif
}
/* ENDCENTRY */