/* Copyright 2001-2003 by Norbert Freudemann, David Frese */

#include "xlib.h"

s48_ref_t scx_gc_function_binding = NULL;
s48_ref_t scx_line_style_binding = NULL;
s48_ref_t scx_cap_style_binding = NULL;
s48_ref_t scx_join_style_binding = NULL;
s48_ref_t scx_fill_style_binding = NULL;
s48_ref_t scx_fill_rule_binding = NULL;
s48_ref_t scx_subwindow_mode_binding = NULL;
s48_ref_t scx_arc_mode_binding = NULL;
#define scx_extract_gc_function(c, x) \
  S48_EXTRACT_ENUM(c, x, scx_gc_function_binding)
#define scx_extract_line_style(c, x) \
  S48_EXTRACT_ENUM(c, x, scx_line_style_binding)
#define scx_extract_cap_style(c, x) \
  S48_EXTRACT_ENUM(c, x, scx_cap_style_binding)
#define scx_extract_join_style(c, x) \
  S48_EXTRACT_ENUM(c, x, scx_join_style_binding)
#define scx_extract_fill_style(c, x) \
  S48_EXTRACT_ENUM(c, x, scx_fill_style_binding)
#define scx_extract_fill_rule(c, x) \
  S48_EXTRACT_ENUM(c, x, scx_fill_rule_binding)
#define scx_extract_subwindow_mode(c, x) \
  S48_EXTRACT_ENUM(c, x, scx_subwindow_mode_binding)
#define scx_extract_arc_mode(c, x) \
  S48_EXTRACT_ENUM(c, x, scx_arc_mode_binding)

s48_ref_t scx_gc_functions_binding = NULL;
s48_ref_t scx_line_styles_binding = NULL;
s48_ref_t scx_cap_styles_binding = NULL;
s48_ref_t scx_join_styles_binding = NULL;
s48_ref_t scx_fill_styles_binding = NULL;
s48_ref_t scx_fill_rules_binding = NULL;
s48_ref_t scx_subwindow_modes_binding = NULL;
s48_ref_t scx_arc_modes_binding = NULL;
#define scx_enter_gc_function(c, x) \
  S48_ENTER_ENUM(c, x, scx_gc_functions_binding)
#define scx_enter_line_style(c, x) \
  S48_ENTER_ENUM(c, x, scx_line_styles_binding)
#define scx_enter_cap_style(c, x) \
  S48_ENTER_ENUM(c, x, scx_cap_styles_binding)
#define scx_enter_join_style(c, x) \
  S48_ENTER_ENUM(c, x, scx_join_styles_binding)
#define scx_enter_fill_style(c, x) \
  S48_ENTER_ENUM(c, x, scx_fill_styles_binding)
#define scx_enter_fill_rule(c, x) \
  S48_ENTER_ENUM(c, x, scx_fill_rules_binding)
#define scx_enter_subwindow_mode(c, x) \
  S48_ENTER_ENUM(c, x, scx_subwindow_modes_binding)
#define scx_enter_arc_mode(c, x) \
  S48_ENTER_ENUM(c, x, scx_arc_modes_binding)

s48_ref_t scx_gc_value_set_binding = NULL;
#define scx_extract_gc_value_set(c, x) \
  S48_EXTRACT_ENUM_SET(c, x, scx_gc_value_set_binding)
#define scx_enter_gc_value_set(c, x) \
  s48_enter_enum_set(c, x, scx_gc_value_set_binding)

s48_ref_t scx_char_struct_binding = NULL;

s48_ref_t scx_enter_charstruct(s48_call_t call, XCharStruct* cs) {
  s48_ref_t res = s48_make_record_2(call, scx_char_struct_binding);
  s48_record_set_2(call, res, 0, 
		   s48_enter_long_as_fixnum_2(call, cs->lbearing));
  s48_record_set_2(call, res, 1, 
		   s48_enter_long_as_fixnum_2(call, cs->rbearing));
  s48_record_set_2(call, res, 2, 
		   s48_enter_long_as_fixnum_2(call, cs->width));
  s48_record_set_2(call, res, 3, 
		   s48_enter_long_as_fixnum_2(call, cs->ascent));
  s48_record_set_2(call, res, 4, 
		   s48_enter_long_as_fixnum_2(call, cs->descent));
  s48_record_set_2(call, res, 5,
		   s48_enter_long_2(call, cs->attributes));
  return res;
}

s48_ref_t scx_enter_fontstruct(s48_call_t call, XFontStruct* fs) {
  int i;
  s48_ref_t plist = s48_null_2(call), t = s48_null_2(call);
  s48_ref_t res = s48_make_record_2(call, scx_fontstruct);
  s48_record_set_2(call, res, 0, s48_enter_pointer_2(call, fs));
  s48_record_set_2(call, res, 1, scx_enter_font(call, fs->fid));
  s48_record_set_2(call, res, 2, scx_enter_font_direction(call, fs->direction));
  s48_record_set_2(call, res, 3, s48_enter_long_2(call, fs->min_char_or_byte2));
  s48_record_set_2(call, res, 4, s48_enter_long_2(call, fs->max_char_or_byte2));
  s48_record_set_2(call, res, 5, s48_enter_long_2(call, fs->min_byte1));
  s48_record_set_2(call, res, 6, s48_enter_long_2(call, fs->max_byte1));
  s48_record_set_2(call, res, 7, s48_enter_boolean_2(call, fs->all_chars_exist));
  
  s48_record_set_2(call, res, 8, s48_enter_long_2(call, fs->default_char));
  for (i = fs->n_properties-1; i >= 0; i--) {
    t = s48_cons_2(call, scx_enter_atom(call, fs->properties[i].name),
		 s48_enter_long_2(call, fs->properties[i].card32));
    plist = s48_cons_2(call, t, plist);
  }
  
  s48_record_set_2(call, res, 9, plist);
  s48_record_set_2(call, res, 10, scx_enter_charstruct(call, &fs->min_bounds));
  s48_record_set_2(call, res, 11, scx_enter_charstruct(call, &fs->max_bounds));
  
  {
    int count;
    if ((fs->min_byte1 == 0) && (fs->max_byte1 == 0))
      count = (fs->max_char_or_byte2 - fs->min_char_or_byte2);
    else
      count = (fs->max_char_or_byte2 - fs->min_char_or_byte2) * 256
	- fs->min_byte1 + fs->max_byte1;
    t = s48_make_vector_2(call, count, s48_false_2(call));
    for (i = 0; i < count; i++)
      s48_vector_set_2(call, t, i, scx_enter_charstruct(call, &fs->per_char[i]));
    s48_record_set_2(call, res, 12, t);
  }

  s48_record_set_2(call, res, 13, s48_enter_long_2(call, fs->ascent));
  s48_record_set_2(call, res, 14, s48_enter_long_2(call, fs->descent));

  return res;
}

s48_ref_t scx_enter_gc(s48_call_t call, GC gc) {
  s48_ref_t v = s48_make_record_2(call, scx_gc);
  s48_record_set_2(call, v, 0, s48_enter_pointer_2(call, gc));
  return v;
}

unsigned long scx_extract_gc_value_alist(s48_call_t call, 
	        s48_ref_t values, XGCValues* GCV) {
  unsigned long mask = 0;
  s48_ref_t v = s48_false_2(call);
  while (!s48_null_p_2(call, values)) {
    int mv = 
      scx_extract_gc_value(call, s48_car_2(call, s48_car_2(call, values)));
    v = s48_cdr_2(call, s48_car_2(call, values));

    s48_ref_t temp = values;
    values = s48_cdr_2(call, values);
    s48_free_local_ref(call, temp);

    mask = mask | (1L << mv);
    switch (1L << mv) {
    case GCFunction:
      GCV->function = scx_extract_gc_function(call, v); break;
    case GCPlaneMask:
      GCV->plane_mask = scx_extract_pixel(call, v); break;
    case GCForeground:
      GCV->foreground = scx_extract_pixel(call, v); break;
    case GCBackground:
      GCV->background = scx_extract_pixel(call, v); break;
    case GCLineWidth:
      GCV->line_width = s48_extract_long_2(call, v); break;
    case GCLineStyle:
      GCV->line_style = scx_extract_line_style(call, v); break;
    case GCCapStyle:
      GCV->cap_style = scx_extract_cap_style(call, v); break;
    case GCJoinStyle:
      GCV->join_style = scx_extract_join_style(call, v); break;
    case GCFillStyle:
      GCV->fill_style = scx_extract_fill_style(call, v); break;
    case GCFillRule:
      GCV->fill_rule = scx_extract_fill_rule(call, v); break;
    case GCTile:
      GCV->tile = scx_extract_pixmap(call, v); break;
    case GCStipple:
      GCV->stipple = scx_extract_pixmap(call, v); break;
    case GCTileStipXOrigin:
      GCV->ts_x_origin = s48_extract_long_2(call, v); break;
    case GCTileStipYOrigin:
      GCV->ts_y_origin = s48_extract_long_2(call, v); break;
    case GCFont:
      GCV->font = scx_extract_font(call, v); break;
    case GCSubwindowMode:
      GCV->subwindow_mode = scx_extract_subwindow_mode(call, v); break;
    case GCGraphicsExposures:
      GCV->graphics_exposures = s48_extract_boolean_2(call, v); break;
    case GCClipXOrigin:
      GCV->clip_x_origin = s48_extract_long_2(call, v); break;
    case GCClipYOrigin:
      GCV->clip_y_origin = s48_extract_long_2(call, v); break;
    case GCClipMask:
      GCV->clip_mask = scx_extract_pixmap(call, v); break;
    case GCDashOffset:
      GCV->dash_offset = s48_extract_long_2(call, v); break;
    case GCDashList:
      GCV->dashes = (char)s48_extract_long_2(call, v); break;
    case GCArcMode:
      GCV->arc_mode = scx_extract_arc_mode(call, v); break;
    }
  }
  return mask;
}

static s48_ref_t scx_enter_gc_value_alist(s48_call_t call, 
		   s48_ref_t values, XGCValues* GCV) {
  s48_ref_t res = s48_null_2(call);
  s48_ref_t v = s48_false_2(call);
  
  while (!s48_null_p_2(call, values)) {
    int mv = scx_extract_gc_value(call, s48_car_2(call, values));
    switch (1L << mv) {
    case GCFunction:
      v = scx_enter_gc_function(call, GCV->function); break;
    case GCPlaneMask:
      v = scx_enter_pixel(call, GCV->plane_mask); break;
    case GCForeground:
      v = scx_enter_pixel(call, GCV->foreground); break;
    case GCBackground:
      v = scx_enter_pixel(call, GCV->background); break;
    case GCLineWidth:
      v = s48_enter_long_2(call, GCV->line_width); break;
    case GCLineStyle:
      v = scx_enter_line_style(call, GCV->line_style); break;
    case GCCapStyle:
      v = scx_enter_cap_style(call, GCV->cap_style); break;
    case GCJoinStyle:
      v = scx_enter_join_style(call, GCV->join_style); break;
    case GCFillStyle:
      v = scx_enter_fill_style(call, GCV->fill_style); break;
    case GCFillRule:
      v = scx_enter_fill_rule(call, GCV->fill_rule); break;
    case GCTile:
      v = scx_enter_pixmap(call, GCV->tile); break;
    case GCStipple:
      v = scx_enter_pixmap(call, GCV->stipple); break;
    case GCTileStipXOrigin:
      v = s48_enter_long_2(call, GCV->ts_x_origin); break;
    case GCTileStipYOrigin:
      v = s48_enter_long_2(call, GCV->ts_y_origin); break;
    case GCFont:
      v = scx_enter_font(call, GCV->font); break;
    case GCSubwindowMode:
      v = scx_enter_subwindow_mode(call, GCV->subwindow_mode); break;
    case GCGraphicsExposures:
      v = s48_enter_boolean_2(call, GCV->graphics_exposures); break;
    case GCClipXOrigin:
      v = s48_enter_long_2(call, GCV->clip_x_origin); break;
    case GCClipYOrigin:
      v = s48_enter_long_2(call, GCV->clip_y_origin); break;
    case GCClipMask:
      v = scx_enter_pixmap(call, GCV->clip_mask); break;
    case GCDashOffset:
      v = s48_enter_long_2(call, GCV->dash_offset); break;
    case GCDashList:
      v = s48_enter_long_2(call, GCV->dashes); break;
    case GCArcMode:
      v = scx_enter_arc_mode(call, GCV->arc_mode); break;
    }
    v = s48_cons_2(call, s48_car_2(call, values), v);
    res = s48_cons_2(call, v, res);

    s48_ref_t temp = values; 
    values = s48_cdr_2(call, values);
    s48_free_local_ref(call, temp);
  }
  return res;
}

s48_ref_t scx_Create_Gc(s48_call_t call, s48_ref_t display, 
            s48_ref_t drawable, s48_ref_t values) {
  XGCValues GCV;
  unsigned long mask;
  GC gc;
  mask = scx_extract_gc_value_alist(call, values, &GCV);
  gc = XCreateGC(scx_extract_display(call, display), 
		 scx_extract_drawable(call, drawable), 
		 mask, &GCV);
  return scx_enter_gc(call, gc);
}

s48_ref_t scx_Copy_Gc(s48_call_t call, s48_ref_t display, 
	    s48_ref_t source, s48_ref_t mask, s48_ref_t dest) {
  XCopyGC(scx_extract_display(call, display), scx_extract_gc(call, source), 
	  scx_extract_gc_value_set(call, mask), scx_extract_gc(call, dest));
  return s48_unspecific_2(call);
}

s48_ref_t scx_Change_Gc(s48_call_t call, s48_ref_t display, 
            s48_ref_t gc, s48_ref_t values) {
  XGCValues GCV;
  unsigned long mask;
  mask = scx_extract_gc_value_alist(call, values, &GCV);
  XChangeGC(scx_extract_display(call, display), scx_extract_gc(call, gc),
	    mask, &GCV);
  return s48_unspecific_2(call);
}


#define ValidGCValuesBits \
    (GCFunction | GCPlaneMask | GCForeground | GCBackground | GCLineWidth |\
    GCLineStyle | GCCapStyle | GCJoinStyle | GCFillStyle | GCFillRule |\
    GCTile | GCStipple | GCTileStipXOrigin | GCTileStipYOrigin | GCFont |\
    GCSubwindowMode | GCGraphicsExposures | GCClipXOrigin | GCClipYOrigin |\
    GCDashOffset | GCArcMode)

s48_ref_t scx_Get_Gc_Values(s48_call_t call, s48_ref_t display, s48_ref_t gc,
			    s48_ref_t values) {
  unsigned long mask = 0;
  XGCValues GCV;
  while (!s48_null_p_2(call, values)) {
    mask |= (1L << scx_extract_gc_value(call, s48_car_2(call, values)));
    s48_ref_t temp = values;
    values = s48_cdr_2(call, values);
    s48_free_local_ref(call, temp);
  }

  if (!XGetGCValues(scx_extract_display(call, display), 
		    scx_extract_gc(call, gc), 
		    mask, &GCV))
    return s48_false_2(call);
  else
    return scx_enter_gc_value_alist(call, values, &GCV);
}

s48_ref_t scx_Free_Gc(s48_call_t call, s48_ref_t display, s48_ref_t gc) {
  XFreeGC(scx_extract_display(call, display), scx_extract_gc(call, gc));
  return s48_unspecific_2(call);
}

s48_ref_t scx_GContext_From_Gc(s48_call_t call, s48_ref_t gc) {
  return scx_enter_gcontext(call, XGContextFromGC(scx_extract_gc(call, gc)));
}

s48_ref_t scx_Set_Dashes(s48_call_t call, s48_ref_t display, 
            s48_ref_t gc, s48_ref_t dashoffset,
			 s48_ref_t dashlist) {
  int i, n = s48_extract_long_2(call, s48_length_2(call, dashlist));
  char dl[n];
  for (i = 0; i < n; i++) {
    dl[i] = s48_extract_long_2(call, s48_car_2(call, dashlist));
    s48_ref_t temp = dashlist;
    dashlist = s48_cdr_2(call, dashlist);
    s48_free_local_ref(call, temp);
  }
  XSetDashes(scx_extract_display(call, display), scx_extract_gc(call, gc),
	     s48_extract_long_2(call, dashoffset),
	     dl, n);
  return s48_unspecific_2(call);
}

s48_ref_t scx_rectangle_ordering_binding = NULL;
#define scx_extract_rectangle_ordering(c, x) \
  S48_EXTRACT_ENUM(c, x, scx_rectangle_ordering_binding)

s48_ref_t scx_Set_Clip_Rectangles(s48_call_t call, s48_ref_t display, 
            s48_ref_t gc, s48_ref_t x_origin, s48_ref_t y_origin,
	    s48_ref_t rects, s48_ref_t ordering) {
  int i, n = s48_extract_long_2(call, s48_length_2(call, rects));

  XRectangle crects[n];
  s48_ref_t r = s48_false_2(call);

  for (i = 0; i < n; i++) {
    r = s48_car_2(call, rects);
    crects[i].x = s48_extract_long_2(call, s48_car_2(call, r)); 

    r = s48_cdr_2(call, r);
    crects[i].y = s48_extract_long_2(call, s48_car_2(call, r)); 

    r = s48_cdr_2(call, r);
    crects[i].width = s48_extract_long_2(call, s48_car_2(call, r)); 

    r = s48_cdr_2(call, r);
    crects[i].height = s48_extract_long_2(call, s48_car_2(call, r)); 

    r = s48_cdr_2(call, r);
    
    s48_ref_t temp = rects;
    rects = s48_cdr_2(call, rects);
    s48_free_local_ref(call, temp);
  }
  XSetClipRectangles(scx_extract_display(call, display), 
		     scx_extract_gc(call, gc),
		     s48_extract_long_2(call, x_origin),
		     s48_extract_long_2(call, y_origin),
		     crects, n, scx_extract_rectangle_ordering(call, ordering));
  return s48_unspecific_2(call);
}

s48_ref_t scx_Query_Best_Size(s48_call_t call, s48_ref_t screen, 
	    s48_ref_t class, s48_ref_t width, s48_ref_t height) {
  unsigned int rw, rh;
  Screen* s;
  s = scx_extract_screen(call, screen);

  if (!XQueryBestSize(s->display, 
		      s48_extract_long_2(call, class),
		      s->root,
		      (int)s48_extract_long_2(call, width), 
		      (int)s48_extract_long_2(call, height), 
                      &rw, &rh))
    return s48_false_2(call);
  else
    return s48_cons_2(call, s48_enter_long_as_fixnum_2(call, rw), 
                            s48_enter_long_as_fixnum_2(call, rh));
}

void scx_init_gcontext(void) {
  scx_gc_function_binding = s48_get_imported_binding_2("scx-gc-function");
  scx_line_style_binding = s48_get_imported_binding_2("scx-line-style");
  scx_cap_style_binding = s48_get_imported_binding_2("scx-cap-style");
  scx_join_style_binding = s48_get_imported_binding_2("scx-join-style");
  scx_fill_style_binding = s48_get_imported_binding_2("scx-fill-style");
  scx_fill_rule_binding = s48_get_imported_binding_2("scx-fill-rule");
  scx_subwindow_mode_binding = s48_get_imported_binding_2("scx-subwindow-mode");
  scx_arc_mode_binding = s48_get_imported_binding_2("scx-arc-mode");
  scx_gc_functions_binding = s48_get_imported_binding_2("scx-gc-functions");
  scx_line_styles_binding = s48_get_imported_binding_2("scx-line-styles");
  scx_cap_styles_binding = s48_get_imported_binding_2("scx-cap-styles");
  scx_join_styles_binding = s48_get_imported_binding_2("scx-join-styles");
  scx_fill_styles_binding = s48_get_imported_binding_2("scx-fill-styles");
  scx_fill_rules_binding = s48_get_imported_binding_2("scx-fill-rules");
  scx_subwindow_modes_binding = s48_get_imported_binding_2("scx-subwindow-modes");
  scx_arc_modes_binding = s48_get_imported_binding_2("scx-arc-modes");

  scx_gc_value_set_binding = s48_get_imported_binding_2("scx-gc-value-set");
  scx_char_struct_binding = s48_get_imported_binding_2("scx-char-struct");
  scx_rectangle_ordering_binding = 
    s48_get_imported_binding_2("scx-rectangle-ordering");

  S48_EXPORT_FUNCTION(scx_Create_Gc);
  S48_EXPORT_FUNCTION(scx_Copy_Gc);
  S48_EXPORT_FUNCTION(scx_Change_Gc);
  S48_EXPORT_FUNCTION(scx_Get_Gc_Values);
  S48_EXPORT_FUNCTION(scx_Free_Gc);
  S48_EXPORT_FUNCTION(scx_GContext_From_Gc);
  S48_EXPORT_FUNCTION(scx_Set_Dashes);
  S48_EXPORT_FUNCTION(scx_Set_Clip_Rectangles);
  S48_EXPORT_FUNCTION(scx_Query_Best_Size);
}
