#include "xlib.h"

s48_ref_t scx_colormap_alloc_binding = NULL;
#define scx_extract_colormap_alloc(c, x) \
	  S48_EXTRACT_ENUM(c, x, scx_colormap_alloc_binding)

double s48_extract_number(s48_call_t call, s48_ref_t v) {
  if (s48_double_p_2(call, v))
    return s48_extract_double_2(call, v);
  else return s48_extract_long_2(call, v);
}

void scx_extract_color(s48_call_t call, s48_ref_t v, XColor* c) {
  s48_check_record_type_2(call, v, scx_color);
  c->pixel = scx_extract_pixel(call, s48_record_ref_2(call, v, 0));
  c->flags = 0;

  if (!s48_false_p_2(call, s48_record_ref_2(call, v, 1))) {
    c->flags |= DoRed;
    c->red =
      s48_extract_number(call, s48_record_ref_2(call, v, 1)) * 65536;
  }

  if (!s48_false_p_2(call, s48_record_ref_2(call, v, 2))) {
    c->flags |= DoGreen;
    c->green =
      s48_extract_number(call, s48_record_ref_2(call, v, 2)) * 65536;
  }
  if (!s48_false_p_2(call, s48_record_ref_2(call, v, 3))) {
    c->flags |= DoBlue;
    c->blue =
      s48_extract_number(call, s48_record_ref_2(call, v, 3)) * 65536;
  }
}

void scx_copy_color(s48_call_t call, const XColor* c, s48_ref_t v) {
  s48_check_record_type_2(call, v, scx_color);
  s48_record_set_2(call, v, 0, scx_enter_pixel(call, c->pixel));
  s48_record_set_2(call, v, 1, (c->flags & DoRed) ?
	       s48_enter_double_2(call, (double)c->red / 65636.0) :
	       s48_false_2(call));
  s48_record_set_2(call, v, 2, (c->flags & DoGreen) ?
	       s48_enter_double_2(call, (double)c->green / 65636.0) :
	       s48_false_2(call));
  s48_record_set_2(call, v, 3, (c->flags & DoBlue) ?
	       s48_enter_double_2(call, (double)c->blue / 65636.0) :
	       s48_false_2(call));
}

s48_ref_t scx_enter_color(s48_call_t call, const XColor* c) {
  s48_ref_t res = s48_make_record_2(call, scx_color);
  scx_copy_color(call, c, res);
  return res;
}

s48_ref_t scx_Create_Colormap (s48_call_t call, s48_ref_t display,
	    s48_ref_t window, s48_ref_t visual, s48_ref_t alloc) {
  Colormap cm;
  cm = XCreateColormap(scx_extract_display(call, display),
		       scx_extract_window(call, window),
		       scx_extract_visual(call, visual),
		       scx_extract_colormap_alloc(call, alloc));
  return scx_enter_colormap(call, cm);
}

s48_ref_t scx_Copy_Colormap_And_Free(s48_call_t call,
	    s48_ref_t display, s48_ref_t colormap) {
  Colormap cm;
  cm = XCopyColormapAndFree(scx_extract_display(call, display),
			    scx_extract_colormap(call, colormap));
  return scx_enter_colormap(call, cm);
}

s48_ref_t scx_Free_Colormap (s48_call_t call,
	    s48_ref_t display, s48_ref_t colormap) {
  XFreeColormap(scx_extract_display(call, display),
		scx_extract_colormap(call, colormap));
  return s48_unspecific_2(call);
}

s48_ref_t scx_Alloc_Color(s48_call_t call,
	    s48_ref_t display, s48_ref_t colormap,
			  s48_ref_t color) {
  XColor cp;
  scx_extract_color(call, color, &cp);

  if (!XAllocColor(scx_extract_display(call, display),
		   scx_extract_colormap(call, colormap), &cp))
    return s48_false_2(call);
  else {
    scx_copy_color(call, &cp, color);
    return s48_unspecific_2(call);
  }
}

s48_ref_t scx_Alloc_Named_Color(s48_call_t call, s48_ref_t display,
	    s48_ref_t colormap, s48_ref_t color_name) {
  XColor screen, exact;
  int r;
  s48_ref_t s = s48_null_2(call), e = s48_null_2(call);

  r = XAllocNamedColor(scx_extract_display(call, display),
		       scx_extract_colormap(call, colormap),
		       s48_extract_string(call, color_name),
		       &screen, &exact);

  if (r != 0) {
    s = scx_enter_color(call, &screen);
    e = scx_enter_color(call, &exact);
    return s48_cons_2(call, s, e);
  } else
    return s48_false_2(call);
}

s48_ref_t scx_Alloc_Color_Cells (s48_call_t call, s48_ref_t display,
	    s48_ref_t colormap, s48_ref_t contig, s48_ref_t nplanes,
	    s48_ref_t npixels) {
  int npl = s48_extract_long_2(call, nplanes);
  int npx = s48_extract_long_2(call, npixels);
  unsigned long plane_masks[npl];
  unsigned long pixels[npx];
  s48_ref_t pls = s48_null_2(call), pxs = s48_null_2(call);

  if (XAllocColorCells(scx_extract_display(call, display),
		       scx_extract_colormap(call, colormap),
		       !s48_false_p_2(call, contig),
		       plane_masks, npl,
		       pixels, npx)) {
    int i;
    for (i = npl-1; i >= 0; i--)
      pls = s48_cons_2(call,
		       s48_enter_long_2(call, plane_masks[i]), pls);
    for (i = npx-1; i >= 0; i--)
      pxs = s48_cons_2(call, scx_enter_pixel(call, pixels[i]), pxs);
    return s48_cons_2(call, pls, pxs);
  } else
    return s48_false_2(call);
}

s48_ref_t scx_Alloc_Color_Planes(s48_call_t call,
	    s48_ref_t display, s48_ref_t colormap, s48_ref_t contig,
	    s48_ref_t ncolors, s48_ref_t nreds, s48_ref_t ngreens,
	    s48_ref_t nblues) {
  int npx = s48_extract_long_2(call, ncolors);
  int nre = s48_extract_long_2(call, nreds);
  int ngr = s48_extract_long_2(call, ngreens);
  int nbl = s48_extract_long_2(call, nblues);
  unsigned long pixels[npx];
  unsigned long rmask, gmask, bmask;
  s48_ref_t pxs = s48_null_2(call);
  s48_ref_t res = s48_null_2(call);

  if (XAllocColorPlanes(scx_extract_display(call, display),
			scx_extract_colormap(call, colormap),
			!s48_false_p_2(call, contig),
			pixels, npx,
			nre, ngr, nbl,
			&rmask, &gmask, &bmask)) {
    int i;
    for (i = npx-1; i >= 0; i--)
      pxs = s48_cons_2(call, scx_enter_pixel(call, pixels[i]), pxs);
    res = s48_cons_2(call, s48_enter_long_2(call, bmask), res);
    res = s48_cons_2(call, s48_enter_long_2(call, gmask), res);
    res = s48_cons_2(call, s48_enter_long_2(call, rmask), res);
    res = s48_cons_2(call, pxs, res);
    return res;
  } else
    return s48_false_2(call);
}

s48_ref_t scx_Free_Colors(s48_call_t call, s48_ref_t display,
	    s48_ref_t colormap, s48_ref_t pixels, s48_ref_t planes) {
  int i, n = s48_extract_long_2(call, s48_length_2(call, pixels));
  unsigned long cpixels[n];
  s48_ref_t l = pixels;

  for (i = 0; i < n; i++) {
    cpixels[i] = scx_extract_pixel(call, s48_car_2(call, l));
    l = s48_cdr_2(call, l);
  }

  XFreeColors(scx_extract_display(call, display),
	      scx_extract_colormap(call, colormap),
	      cpixels, n, s48_extract_long_2(call, planes));

  return s48_unspecific_2(call);
}

s48_ref_t scx_Query_Colors(s48_call_t call, s48_ref_t display,
	    s48_ref_t colormap, s48_ref_t colors) {
  int i, n = s48_extract_long_2(call, s48_length_2(call, colors));

  XColor ccolors[n];
  s48_ref_t l = colors;
  for (i = 0; i < n; i++) {
    scx_extract_color(call, s48_car_2(call, l), &ccolors[i]);
    l = s48_cdr_2(call, l);
  }
  XQueryColors(scx_extract_display(call, display), 
	       scx_extract_colormap(call, colormap),
	       ccolors, n);
  l = colors;
  for (i = 0; i < n; i++) {
    s48_ref_t temp = l;
    scx_copy_color(call, &ccolors[i], s48_car_2(call, l));
    l = s48_cdr_2(call, l);
    s48_free_local_ref(call, temp);
  }
  return s48_unspecific_2(call);
}

s48_ref_t scx_Lookup_Color(s48_call_t call, s48_ref_t display, 
            s48_ref_t colormap, s48_ref_t color_name) {
  XColor cexact, cscreen;
  s48_ref_t r = s48_null_2(call);
  int res;
  res = XLookupColor(scx_extract_display(call, display),
			 scx_extract_colormap(call, colormap),
			 s48_extract_string(call, color_name),
			 &cexact, &cscreen);
  if (res == 0) return s48_false_2(call);
  r = scx_enter_color(call, &cscreen);
  r = s48_cons_2(call, scx_enter_color(call, &cexact), r);
  return r;
}

s48_ref_t scx_Parse_Color(s48_call_t call, s48_ref_t display, 
            s48_ref_t colormap, s48_ref_t spec) {
  XColor ret;
  if (XParseColor(scx_extract_display(call, display),
		  scx_extract_colormap(call, colormap),
		  s48_extract_string(call, spec),
		  &ret)) {
    return scx_enter_color(call, &ret);
  } else
    return s48_false_2(call);
}

s48_ref_t scx_Store_Colors(s48_call_t call, s48_ref_t display, 
	    s48_ref_t colormap, s48_ref_t colors) {
  int i, n = s48_extract_long_2(call, s48_length_2(call, colors));
  XColor ccolors[n];
  s48_ref_t l = colors;
  for (i = 0; i < n; i++) {
    s48_ref_t temp = l;
    scx_extract_color(call, s48_car_2(call, l), &ccolors[i]);
    l = s48_cdr_2(call, l);
    s48_free_local_ref(call, temp);
  }

  XStoreColors(scx_extract_display(call, display),
	       scx_extract_colormap(call, colormap),
	       ccolors, n);
  return s48_unspecific_2(call);
}

s48_ref_t scx_Store_Named_Color(s48_call_t call, s48_ref_t display, 
            s48_ref_t colormap, s48_ref_t color_name, s48_ref_t pixel,
	    s48_ref_t do_red, s48_ref_t do_green, s48_ref_t do_blue) {
  XStoreNamedColor(scx_extract_display(call, display),
		   scx_extract_colormap(call, colormap),
		   s48_extract_string(call, color_name),
		   scx_extract_pixel(call, pixel),
		   (s48_extract_boolean_2(call, do_red) ? DoRed : 0) |
		   (s48_extract_boolean_2(call, do_green) ? DoGreen : 0) |
		   (s48_extract_boolean_2(call, do_blue) ? DoBlue : 0));
  return s48_unspecific_2(call);
}

void scx_init_colormap(void) {
  scx_colormap_alloc_binding = 
    s48_get_imported_binding_2("scx-colormap-alloc");

  S48_EXPORT_FUNCTION(scx_Create_Colormap);
  S48_EXPORT_FUNCTION(scx_Copy_Colormap_And_Free);
  S48_EXPORT_FUNCTION(scx_Free_Colormap);
  S48_EXPORT_FUNCTION(scx_Alloc_Color);
  S48_EXPORT_FUNCTION(scx_Alloc_Named_Color);
  S48_EXPORT_FUNCTION(scx_Alloc_Color_Cells);
  S48_EXPORT_FUNCTION(scx_Alloc_Color_Planes);
  S48_EXPORT_FUNCTION(scx_Free_Colors);
  S48_EXPORT_FUNCTION(scx_Query_Colors);
  S48_EXPORT_FUNCTION(scx_Lookup_Color);
  S48_EXPORT_FUNCTION(scx_Parse_Color);
  S48_EXPORT_FUNCTION(scx_Store_Colors);
  S48_EXPORT_FUNCTION(scx_Store_Named_Color);
}
