/** File zc.c
 * zc : an abbrevation for Z Colors or Z Cmaps
 *  used to draw SET_XYCMAP  sets 
 */

#include <stdio.h>
#include <string.h>
#include <cmath.h>

#include <gtk/gtk.h>


#include "globals.h"

#include "draw.h"
#include "graphs.h"
#include "utils.h"
#include "noxprotos.h"
#include "device.h"

#include "ng_objects.h"
#include "nn_tree.h"

#include "zc.h"
#include "zcP.h"

#define ZC_MAX 20

extern GdkColor  gg_colors[MAXCOLORS];  
extern GdkGC    *gg_gc;

extern void nicesegment (double xmi ,double xma ,double nb ,double *x0 ,double *x1);
extern void sprintf_with_niceprec (char *buf ,double x ,double x1 ,double x2);

static int max_colors = 6;   /* Actually zcmap_predef[0].ncolors */

static ZCmap *pzcmap = NULL;
static int    max_colors;
static double fc_min ,fc_max ,fc_0 ,slope;
static ZcColorbarPlace zc_just = ZC_COLORBAR_RIGHT;


/* ---------------------- Color maps ---------------------- */

/**
 *  Copy  nodes of map "n" in pzcmap to use for drawing
 */
void zc_cmap_set_map (int n)
{
  CMap_entry *cmap;
  int i ,j;
  fRGB *frgb;  
  if (pzcmap == NULL) {
    pzcmap = xmalloc (sizeof (ZCmap));
    pzcmap->map = NULL;
  }
  if (n < 0 || n > MAX_PREDEF_CMAP) {
    errmsg ("zc_init : unknown color map: fall back to GraceGTK colormap");
    n = 0;
  }
  max_colors = (n == 0) ? number_of_main_colors () : zcmap_predef[n].ncolors;
  pzcmap->ncolors = max_colors;
  pzcmap->map     = xrealloc (pzcmap->map ,(max_colors+1) * sizeof(fRGB));
  if (n == 0) {
    pzcmap->n       = -1;
    strncpy (pzcmap->name ,"GraceGTK cmap" ,63);
    for (i = 0 ,j = 0; i < number_of_colors (); i++) {
      cmap = get_cmap_entry (i);
      if (cmap->ctype == COLOR_MAIN) {
 	frgb = get_frgb (i);
	pzcmap->map[j].red   = frgb->red;
	pzcmap->map[j].green = frgb->green;
	pzcmap->map[j].blue  = frgb->blue;
 	j++;
      }
    }
  } else {
    pzcmap->n       = n;
    strncpy (pzcmap->name ,zcmap_predef[n].name ,63);
    for (i = 0; i <= max_colors; i++) {
      pzcmap->map[i].red   = zcmap_predef[n].map[i].red;
      pzcmap->map[i].green = zcmap_predef[n].map[i].green;
      pzcmap->map[i].blue  = zcmap_predef[n].map[i].blue;
    }
  }
}

int zc_cmap_get_nb_of_colors (int n)
{
  if (n == 0) return number_of_main_colors ();
  return zcmap_predef[n].ncolors;
}

char *zc_cmap_get_name (int n)
{
  if (n == 0) {
    return "GraceGTK map";
  } else if (n < MAX_PREDEF_CMAP) {
    return zcmap_predef[n].name;
  } else {
    return NULL;
  }
}

int zc_cmap_get_by_name (char *s)
{
  int i;
  for (i = 0; i < MAX_PREDEF_CMAP; i++) {
    if (compare_strings (zcmap_predef[i].name ,s) == TRUE) {
      return i;
    }
  }
  errmsg("Invalid Colormap name");
  return -1;
}

/*---------------------- Drawing utils ---------------------- */


/*
 * Computes parameters for the linear interpolation needed
 * to choose the RGB values
 */
int zc_init (int gno ,int setno ,plotarr *p)
{
  double z1 = p->contour.z1;
  double z2 = p->contour.z2;
  if (p->contour.z_scaled == FALSE) {
    int izmin ,izmax;
    double *z = getcol (gno ,setno ,DATA_Y1);
    int   len = getsetlength (gno ,setno);            /* needed by minmax */
    if (len > 0) {
      minmax (z ,len ,&z1 ,&z2 ,&izmin ,&izmax);
      if (z1 == z2) return FALSE;
      p->contour.z_scaled = TRUE;
      p->contour.z1       = z1;
      p->contour.z2       = z2;
      set_graph_plotarr (gno ,setno ,p);
    } else {
      return FALSE;
    }
  }
  zc_cmap_set_map (p->contour.nmap);
  max_colors = pzcmap->ncolors;
  fc_min = 0.0;                     	  /* origin of the color space */
  fc_max = (double)(max_colors-1);  	  /* end    "    "    "    "   */
  slope =  (fc_max - fc_min) / (z2 - z1);
  fc_0 = - slope *  z1;           	  /* value of fc for z=0 */
  return TRUE;
}

/**
 * Interpolate RGB color as a function of z
 *  Use fc_0 , fc_min  and slope
 */
void zc_interp (double z)
{
  double fred ,fgreen ,fblue;
  double fc    = fc_0 + slope*z;    
  int    c     = (int)(fc_min+fc);    	   /* we interpolate between c and c+1 */
  if (c<0 || c>max_colors-1) {
    errmsg ("zc_interp : internal error");
    fprintf (stderr ,"c = %d\n" ,c);
    return;
  }
  double shift = fc - (double)c;           /*    with this shift */
  fRGB pc1  = pzcmap->map[c];
  fRGB pc2  = pzcmap->map[c+1];
  fred	    = pc1.red   +  shift * (pc2.red - pc1.red);   
  fgreen    = pc1.green +  shift * (pc2.green - pc1.green);     
  fblue	    = pc1.blue  +  shift * (pc2.blue - pc1.blue);  
  devsetfrgbcolor (fred ,fgreen ,fblue);
}

void zc_autoscale (int iobj)
{
  int izmin ,izmax;
  double zmin ,zmax ,z1 ,z2;
  QDobject *po;
  int gno ,setno;
  plotarr p;
  if (obj_get_plotarr (iobj ,&po ,&gno ,&setno ,&p) == RETURN_SUCCESS) {
    int len      	 = p.data.en[DATA_Y1];                 /* needed by minmax */
    double *z    	 = p.data.ex[DATA_Y1];
    minmax (z ,len ,&zmin ,&zmax ,&izmin ,&izmax);
    nicesegment (zmin ,zmax ,p.contour.nticks ,&z1 ,&z2);
    p.contour.z_scaled = TRUE;
    p.contour.z1 = z1;
    p.contour.z2 = z2;
    set_graph_plotarr (gno ,setno ,&p);
  }
}

/* ---------------------- Q_Colorbar ---------------------- */
void zc_colorbar_fill (int iobj)
{
  int i;
  double z ,dy ,dz ,fnt;
  VPoint vp1 ,vp2;
  char buf[32];
  QDobject *po;
  int gno ,setno;
  plotarr p;
  Contour *pc = &(p.contour);
  if (objs[iobj].hidden)     return;
  zc_just = objs[iobj].just;
  if (obj_get_plotarr (iobj ,&po ,&gno ,&setno ,&p) == RETURN_SUCCESS) {
    /* Fill the colorbar */
    if (zc_init (gno ,setno ,&p) == FALSE) return;
    if (po->y2 < po->y1) fswap (&po->y1 ,&po->y2);
    dy = (po->y2 - po->y1) / 100.0;
    dz = (pc->z2 - pc->z1) / 100.0;
    vp1.x     = po->x1;
    vp2.x     = po->x2;
    setclipping (FALSE);
    for (i = 0; i < 100; i++) {
      vp1.y = po->y1 + i * dy;
      vp2.y = vp1.y  + dy;
      z     = pc->z1  + i * dz;
      zc_interp (z);
      FillRect (vp1 ,vp2);
    }
    /* Draw ticks and write labels */
    /* ticks and labels are not in the colorbar bounding box */
    activate_bbox (BBOX_TYPE_TEMP, FALSE);
    devsetfrgbcolor (0.0 ,0.0, 0.0);
    setcolor    (po->color);
    setcharsize (po->charsize);
    setfont     (po->font);
    fnt = (double)(pc->nticks - 1);
    dy = (po->y2 - po->y1) / fnt;
    dz = (pc->z2  - pc->z1)  / fnt;
    if (zc_just == ZC_COLORBAR_RIGHT) {
      vp1.x = (po->x2 < po->x1) ? po->x1 : po->x2;
      vp2.x = vp1.x + 0.01;
      for (i = 0; i < pc->nticks; i++) {
	vp1.y = vp2.y = po->y1 + i * dy;
	DrawLine (vp1 ,vp2);
	z     = pc->z1 + i * dz;
	sprintf_with_niceprec (buf ,z ,pc->z1 ,pc->z2);
	WriteString (vp2 ,0 ,JUST_LEFT | JUST_MIDDLE ,buf);
      }
    } else if (zc_just == ZC_COLORBAR_LEFT) {
      vp2.x = (po->x2 < po->x1) ? po->x2 : po->x1;
      vp1.x = vp2.x - 0.01;
      for (i = 0; i < pc->nticks; i++) {
	vp1.y = vp2.y = po->y1 + i * dy;
	z     = pc->z1 + i * dz;
	sprintf_with_niceprec (buf ,z ,pc->z1 ,pc->z2);
	WriteString (vp1 ,0 ,JUST_RIGHT | JUST_MIDDLE ,buf);
	DrawLine (vp1 ,vp2);
      }
    }
    activate_bbox (BBOX_TYPE_TEMP, TRUE);
  }
  /* reset gc */
  devsetfrgbcolor (0. ,0. ,0.);
  setcolor (1);
}

/* ---------------------- SET_XYCMAP ---------------------- */

static int nxc = 0;
static int nyc = 0;

void zc_set_map_nxy (int nx ,int ny)
{
  nxc = nx;
  nyc = ny;
}
/**
 *  calld by xyplot ()
 */
void zc_draw_set_xycmap (int gno ,int setno ,plotarr *p ,int layer)
{
  int  i ,nz;
  double *x, *y, *z;
  double xmin ,xmax ,ymin ,ymax ,dx ,dy;
  VPoint vp1 ,vp2;
  int iobj = p->contour.icolorbar;
  set_contour_nxy (gno ,setno ,nxc ,nyc);
  setclipping (TRUE);
  x  = p->data.ex[DATA_X];
  y  = p->data.ex[DATA_Y];
  z  = p->data.ex[DATA_Y1];
  nz = p->data.en[DATA_Y1];
  /* Compute cell dimensions in wc */
  getsetminmax (gno ,setno ,&xmin, &xmax, &ymin, &ymax);
  dx = 0.5 * (xmax-xmin) / (double)(nxc-1);
  dy = 0.5 * (ymax-ymin) / (double)(nyc-1);
  /* Draw coloured rectangles */
  for (i = 0; i < nz; i++) {
    world2view (x[i]-dx	,y[i]-dx ,&vp1.x ,&vp1.y);
    world2view (x[i]+dx ,y[i]+dy ,&vp2.x ,&vp2.y);
    /* avoid rounding effects */
    vp2.x += 0.001;
    vp2.y += 0.001;
    zc_interp (z[i]); 
    FillRect (vp1 ,vp2);
  }
  /* reset gc */
  devsetfrgbcolor (0. ,0. ,0.);
  setcolor (1);
#if defined (HAVE_CONTOURS)
  if (p->contour.hidden == FALSE) {
    zc_draw_set_xycontours (gno ,setno);
  }
#endif  /* HAVE_CONTOURS */
  if (iobj < 0) return;
  if (objs[iobj].layer == layer) {
    zc_colorbar_fill (iobj);
  }
}

