// Copyright (C) 1999-2014
// Smithsonian Astrophysical Observatory, Cambridge, MA, USA
// For conditions of distribution and use, see copyright notice in "copyright"

#include <tcl.h>

#include "colorbarpseudocolor.h"
#include "util.h"

ColorbarPseudoColor::ColorbarPseudoColor(Tcl_Interp* i, Tk_Canvas c, 
					 Tk_Item* item) 
  : ColorbarBase(i,c,item), Colorbar(i,c,item), ColorbarPseudo(i,c,item)
{
  colormap = 0;
  privateUsed = 0;
}

// initColormap allocates the requested number of color colorCells

int ColorbarPseudoColor::initColormap()
{
  // initialize default or private colormap
  if (((ColorbarBaseOptions*)options)->privateCmap) {
    if (initPrivateMap() == TCL_ERROR)
      return TCL_ERROR;
  }
  else
    if (initDefaultMap() == TCL_ERROR)
      if (initPrivateMap() == TCL_ERROR)
	return TCL_ERROR;

  // by now, we should have a map with at least min colors
  updateColors();

  return TCL_OK;
}

int ColorbarPseudoColor::initDefaultMap()
{
  // grap default colormap
  colormap = Tk_Colormap(tkwin);

  // see if we can allocate at least min number of colors requested
  int minColors = ((ColorbarBaseOptions*)options)->minColors;
  int maxColors = ((ColorbarBaseOptions*)options)->maxColors;

  unsigned long* cells = 
    new unsigned long[((ColorbarBaseOptions*)options)->maxColors];

  for (int k=maxColors; k>=minColors-1; k--)
    if (XAllocColorCells(display, colormap, False, 0, 0, cells, k)) {
      colorCount = k;

      // copy from long to unsign short
      colorIndex = new unsigned short[colorCount];
      for (int i=0; i<colorCount; i++)
	colorIndex[i] = cells[i];
      colorCells = new unsigned char[colorCount*3];

      delete [] cells;
      return TCL_OK;
    }

  delete [] cells;
  return TCL_ERROR;
}

int ColorbarPseudoColor::initPrivateMap()
{
  int privateColors = ((ColorbarBaseOptions*)options)->privateColors;
  int defCount = 254 - 8 - 8 - privateColors;

  // first, grap most of the default colors from the current color map
  Colormap defColormap = Tk_Colormap(tkwin);

  XColor* defColors= new XColor[defCount];
  for (int i=0; i<defCount; i++)
    defColors[i].pixel = i;
  XQueryColors(display, defColormap, defColors, defCount);

  // now, allocate a new private map
  colormap = Tk_GetColormap(interp, tkwin, "new");

  // allocate the default colors
  unsigned long* defCells = new unsigned long[defCount];
  if (!XAllocColorCells(display, colormap, False, 0, 0, defCells, defCount)) {
    Tcl_AppendResult(interp, " unable to allocate default colors",
		     " in private colormap.", NULL);
    return TCL_ERROR;
  }

  // store default colors
  XStoreColors(display, colormap, defColors, defCount);

  // and finally, allocate the dynamic colors
  colorCount = privateColors;
  unsigned long* cells = new unsigned long[colorCount];
  if (!XAllocColorCells(display, colormap, False, 0, 0, cells, colorCount)) {
    Tcl_AppendResult(interp, " unable to allocate maxium number of colors",
		     " in private colormap.", NULL);
    return TCL_ERROR;
  }

  // copy from long to unsign char
  colorIndex = new unsigned short[colorCount];
  for (int j=0; j<colorCount; j++)
    colorIndex[j] = cells[j];
  colorCells = new unsigned char[colorCount*3];

  // and init the first DEFCMAP colors
  privateUsed = 1;
  // assign private map to this window
  Tk_SetWindowColormap(tkwin, colormap); 

  // force the private map to be loaded now
  XInstallColormap(display, colormap); 

  delete [] cells;
  delete [] defCells;
  delete [] defColors;
  return TCL_OK;
}

void ColorbarPseudoColor::updateColors()
{
  XColor* colors = new XColor[colorCount];

  if (cmaps.current())
    for(int ii=0, jj=colorCount-1; ii<colorCount; ii++, jj--) {
      int index = invert ? calcContrastBias(jj) : calcContrastBias(ii);

      // fill in colors
      colors[ii].pixel = colorIndex[ii];
      colors[ii].red = cmaps.current()->getRedShrt(index, colorCount);
      colors[ii].green = cmaps.current()->getGreenShrt(index, colorCount);
      colors[ii].blue = cmaps.current()->getBlueShrt(index, colorCount);
      colors[ii].flags = DoRed | DoGreen | DoBlue;

      // fill in rest of colorCells
      // Note: we fill the array bgr
      colorCells[ii*3] = cmaps.current()->getBlueChar(index, colorCount);
      colorCells[ii*3+1] = cmaps.current()->getGreenChar(index, colorCount);
      colorCells[ii*3+2] = cmaps.current()->getRedChar(index, colorCount);
    }
  
  XStoreColors(display, colormap, colors, colorCount);

  delete [] colors;
}

void ColorbarPseudoColor::setColormapWindowCmd(char* str)
{
  Tk_Window win = Tk_NameToWindow(interp, str, tkwin);

  // Check to see if we have the same visual (and depth)
  if (Tk_Visual(tkwin) == Tk_Visual(win) && Tk_Depth(tkwin) == Tk_Depth(win)) {
    if (win)
      Tk_SetWindowColormap(win, colormap);
    else
      result = TCL_ERROR;
  }
  else
    internalError("Colorbar: Visual mismatch");
}

