# include "Ximage.h"

# define SETVALUE(VAR,VALUE,MINVAL,MAXVAL) { \
  float tmp = (VALUE); \
  if (tmp < MINVAL) { \
    VAR = MINVAL; \
  } else if (tmp > MAXVAL) { \
    VAR = MAXVAL; \
  } else { \
    VAR = tmp; \
  } }   

int SetColormap (char *name) {

  int i, red, blue, green;
  float scale, blueRef, redRef, greenRef;
  Graphic *graphic;

  graphic = GetGraphic();

  // the "fullcolor" colormap is uniquely defined for each image;
  // defer to the 'SetColorScale' step
  if (!strcasecmp (name, "fullcolor")) {
    graphic[0].ColorScaleMode = KAPA_SCALE_3D_FULL;
    if (SetColorCubeHistogram ()) return TRUE;
  }

  // very simple color model: evenly spaced cube 
  if (!strcasecmp (name, "ruffcolor")) {
      graphic[0].nRed  = pow (graphic[0].Npixels, 0.333);
      graphic[0].nBlue = pow (graphic[0].Npixels, 0.333);
      graphic[0].nGreen = graphic[0].Npixels / (graphic[0].nRed * graphic[0].nBlue);

      // red,green,blue are values in range 0x0000 to 0xffff
      float redScale = 0xffff / (graphic[0].nRed - 1);
      float blueScale = 0xffff / (graphic[0].nBlue - 1);
      float greenScale = 0xffff / (graphic[0].nGreen - 1);

      i = 0;
      for (red = 0; red < graphic[0].nRed; red++) {  
	  for (blue = 0; blue < graphic[0].nBlue; blue++) {  
	      for (green = 0; green < graphic[0].nGreen; green++, i++) {  
		  SETVALUE (graphic[0].cmap[i].red,   red*redScale, 0, 0xffff);
		  SETVALUE (graphic[0].cmap[i].blue,  blue*blueScale, 0, 0xffff);
		  SETVALUE (graphic[0].cmap[i].green, green*greenScale, 0, 0xffff);
		  graphic[0].cmap[i].flags = DoRed | DoGreen | DoBlue;
	      }
	  }
      }

      // all other modes are 1D: set the flag:
      graphic[0].ColorScaleMode = KAPA_SCALE_3D_RUFF;
      goto store_colors;
  }

  // all other modes are 1D: set the flag:
  graphic[0].ColorScaleMode = KAPA_SCALE_1D;

  // red,green,blue are values in range 0x0000 to 0xffff
  scale = 0xffff / (graphic[0].Npixels - 1);

  /* greyscale */
  if ((!strcasecmp (name, "grayscale")) || (!strcasecmp (name, "greyscale"))) {
    for (i = 0; i < graphic[0].Npixels; i++) {  
      SETVALUE (graphic[0].cmap[i].red,   0xffff - i*scale, 0, 0xffff);
      SETVALUE (graphic[0].cmap[i].green, 0xffff - i*scale, 0, 0xffff);
      SETVALUE (graphic[0].cmap[i].blue,  0xffff - i*scale, 0, 0xffff);
      graphic[0].cmap[i].flags = DoRed | DoGreen | DoBlue;
    }
    goto store_colors;
  }
  /* -grayscale */
  if ((!strcasecmp (name, "-grayscale")) || (!strcasecmp (name, "-greyscale"))) {
    for (i = 0; i < graphic[0].Npixels; i++) {  
      SETVALUE (graphic[0].cmap[i].red,   i*scale, 0, 0xffff);
      SETVALUE (graphic[0].cmap[i].green, i*scale, 0, 0xffff);
      SETVALUE (graphic[0].cmap[i].blue,  i*scale, 0, 0xffff);
      graphic[0].cmap[i].flags = DoRed | DoGreen | DoBlue;
    }
    goto store_colors;
  }
  /* heat */
  if (!strcasecmp (name, "Heat")) {
    greenRef = 0.25*graphic[0].Npixels*scale*2.0;
    blueRef  = 0.50*graphic[0].Npixels*scale*2.0;
    for (i = 0; i < (int)(0.25*graphic[0].Npixels); i++) {  
      SETVALUE (graphic[0].cmap[i].red,   2*i*scale, 0, 0xffff);
      graphic[0].cmap[i].green = 0;
      graphic[0].cmap[i].blue  = 0;
      graphic[0].cmap[i].flags = DoRed | DoGreen | DoBlue;
    }
    for (i = 0.25*graphic[0].Npixels; i < 0.50*graphic[0].Npixels; i++) {  
      SETVALUE (graphic[0].cmap[i].red,   2*i*scale,            0, 0xffff);
      SETVALUE (graphic[0].cmap[i].green, 2*i*scale - greenRef, 0, 0xffff);
      graphic[0].cmap[i].blue = 0;
      graphic[0].cmap[i].flags = DoRed | DoGreen | DoBlue;
    }
    for (i = (int)(0.50*graphic[0].Npixels); i < (int)(0.75*graphic[0].Npixels); i++) {  
      graphic[0].cmap[i].red = 0xffff;
      SETVALUE (graphic[0].cmap[i].green, 2*i*scale - greenRef, 0, 0xffff);
      SETVALUE (graphic[0].cmap[i].blue,  2*i*scale - blueRef,  0, 0xffff);
      graphic[0].cmap[i].flags = DoRed | DoGreen | DoBlue;
    }
    for (i = (int)(0.75*graphic[0].Npixels); i < graphic[0].Npixels; i++) {  
      graphic[0].cmap[i].red   = 0xffff;
      graphic[0].cmap[i].green = 0xffff;
      SETVALUE (graphic[0].cmap[i].blue,  2*i*scale - blueRef,  0, 0xffff);
      graphic[0].cmap[i].flags = DoRed | DoGreen | DoBlue;
    }
    goto store_colors;
  }
  /* rainbow */
  if (!strcasecmp (name, "Rainbow")) {
    redRef   = 0.25*graphic[0].Npixels*scale*4.0;
    greenRef = 0.50*graphic[0].Npixels*scale*4.0;
    blueRef  = 0.50*graphic[0].Npixels*scale*4.0;
    for (i = 0; i < (int)(0.25*graphic[0].Npixels); i++) {  
      graphic[0].cmap[i].red   = 0;
      graphic[0].cmap[i].green = 0;
      SETVALUE (graphic[0].cmap[i].blue,  4*i*scale,           0, 0xffff);
      graphic[0].cmap[i].flags = DoRed | DoGreen | DoBlue;
    }
    for (i = (int)(0.25*graphic[0].Npixels); i < (int)(0.50*graphic[0].Npixels); i++) {  
      SETVALUE (graphic[0].cmap[i].red,   4*i*scale - redRef,  0, 0xffff);
      graphic[0].cmap[i].green = 0;
      SETVALUE (graphic[0].cmap[i].blue,  blueRef - 4*i*scale, 0, 0xffff);
      graphic[0].cmap[i].flags = DoRed | DoGreen | DoBlue;
    }
    for (i = (int)(0.50*graphic[0].Npixels); i < (int)(0.75*graphic[0].Npixels); i++) {  
      graphic[0].cmap[i].red  = 0xffff;
      SETVALUE (graphic[0].cmap[i].green,  4*i*scale - greenRef, 0, 0xffff);
      graphic[0].cmap[i].blue = 0;
      graphic[0].cmap[i].flags = DoRed | DoGreen | DoBlue;
    }
    blueRef  = 0.75*graphic[0].Npixels*scale*4.0;
    for (i = (int)(0.75*graphic[0].Npixels); i < graphic[0].Npixels; i++) {  
      graphic[0].cmap[i].red   = 0xffff;
      graphic[0].cmap[i].green = 0xffff;
      SETVALUE (graphic[0].cmap[i].blue,  4*i*scale - blueRef, 0, 0xffff);
      graphic[0].cmap[i].flags = DoRed | DoGreen | DoBlue;
    }
    goto store_colors;
  }
  return (FALSE);

 store_colors:
  if (!USE_XWINDOW) return (TRUE);
  if (graphic[0].visualclass) {
    XStoreColors(graphic[0].display, graphic[0].colormap, graphic[0].cmap, graphic[0].Npixels);
  } else {
    for (i = 0; i < graphic[0].Npixels; i++) {
      if (XAllocColor (graphic[0].display, graphic[0].colormap, &graphic[0].cmap[i]) == 0) {
	fprintf (stderr, "error on %d\n", i);
      }
    }
  }
  return (TRUE);
}

