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

#include "xlib.h"

s48_ref_t scx_Intern_Atom(s48_call_t call, s48_ref_t display, s48_ref_t name,
			  s48_ref_t only_if_exists) 
{
  Atom a;
  a = XInternAtom(scx_extract_display(call, display), 
		  s48_extract_string(call, name),
		  s48_extract_boolean_2(call, only_if_exists));
  return scx_enter_atom(call, a);
}

s48_ref_t scx_Intern_Atoms(s48_call_t call, s48_ref_t display, s48_ref_t names,
			   s48_ref_t only_if_exists) 
{
  int i, n = s48_extract_long_2(call, s48_length_2(call,names));

  char* cnames[n];
  Atom atoms[n];
  s48_ref_t l = s48_null_2(call);
  for (i = 0; i < n; i++)
    cnames[i] = s48_extract_string(call, names);
  if (!XInternAtoms(scx_extract_display(call, display), 
		    cnames, n,
		    s48_extract_boolean_2(call, only_if_exists),
		    atoms))
    return s48_false_2(call);
  else {
    for (i = n-1; i >= 0; i--)
      l = s48_cons_2(call, scx_enter_atom(call, atoms[i]), l);
    return l;
  }
}

s48_ref_t scx_Get_Atom_Name(s48_call_t call, s48_ref_t display, s48_ref_t a) 
{
  char* s;
  s48_ref_t str;
  s = XGetAtomName(scx_extract_display(call, display),
		   scx_extract_atom(call, a));
  str = s48_enter_string(call, s);
  XFree(s);
  return str;
}

s48_ref_t scx_List_Properties(s48_call_t call, s48_ref_t display, 
	    s48_ref_t window) 
{
  int n, i;
  Atom *atoms;
  s48_ref_t l = s48_null_2(call);
  atoms = XListProperties (scx_extract_display(call, display), 
			   scx_extract_window(call, window), &n);

  for (i = n-1; i >= 0; i--)
    l = s48_cons_2(call, scx_enter_atom(call, atoms[i]), l);
  XFree ((char*)atoms);
  return l;
}

s48_ref_t scx_Rotate_Window_Properties(s48_call_t call, s48_ref_t display, 
            s48_ref_t window, s48_ref_t properties, s48_ref_t delta) 
{
  int i, n = s48_extract_long_2(call, s48_length_2(call, properties));

  Atom p[n];
  for (i = 0; i < n; i++) {
    p[i] = scx_extract_atom(call, s48_car_2(call, (properties)));
    properties = s48_cdr_2(call, (properties));
  }
  XRotateWindowProperties(scx_extract_display(call, display), 
			  scx_extract_window(call, window),
			  p, n,	s48_extract_long_2(call, delta));
  return s48_unspecific_2(call);
}

s48_ref_t scx_Delete_Property(s48_call_t call, s48_ref_t display, 
            s48_ref_t window, s48_ref_t prop) 
{
  XDeleteProperty(scx_extract_display(call, display), 
		  scx_extract_window(call, window),
		  scx_extract_atom(call, prop));
  return s48_unspecific_2(call);
}

s48_ref_t scx_property_binding = NULL;
s48_ref_t scx_property_format_binding = NULL;
s48_ref_t scx_property_formats_binding = NULL;

void scx_extract_property(s48_call_t call, s48_ref_t p, Atom* type, 
       int* format, char** data, int* nelements) 
{
  int i;
  s48_ref_t d = s48_record_ref_2(call, p, 2);
  s48_check_record_type_2(call, p, scx_property_binding);
  *type = scx_extract_atom(call, s48_record_ref_2(call, p, 0));
  *format = S48_EXTRACT_ENUM(call, s48_record_ref_2(call, p, 1),
			     scx_property_format_binding);
  switch (*format) {
  case 0:
    *format = 8;
    *nelements = s48_string_length_2(call, d);
    *data = (char*)malloc(*nelements);
    strcpy(*data, s48_extract_string(call, d));
    break;
  case 1:
    *format = 16;
    *nelements = s48_extract_long_2(call, s48_length_2(call, d));
    *data = (char*)malloc(2 * (*nelements));
    for (i = 0; i < *nelements; i++) {
      (*(short**)data)[i] = s48_extract_long_2(call, s48_car_2(call, (d)));
      d = s48_cdr_2(call, (d));
    }
    break;
  case 2:
    *format = 32;
    *nelements = s48_extract_long_2(call, s48_length_2(call, d));
    *data = (char*)malloc(4 * (*nelements));
    for (i = 0; i < *nelements; i++) {
      (*(long**)data)[i] = s48_extract_long_2(call, s48_car_2(call, (d)));
      d = s48_cdr_2(call, (d));
    }
    break;
  }
}

s48_ref_t scx_enter_property(s48_call_t call, Atom type, int format, 
            char* data, int nelements) {
  s48_ref_t p = s48_make_record_2(call, scx_property_binding);
  s48_ref_t l = s48_null_2(call);
  int i;

  s48_record_set_2(call,  p, 0, scx_enter_atom(call, type));
  switch (format) {
  case 8:
    s48_record_set_2(call,  p, 1, 
		     S48_ENTER_ENUM(call, 0, scx_property_formats_binding));
    s48_record_set_2(call,  p, 2, 
		     s48_enter_byte_substring_2(call, data, nelements));
    break;
  case 16:
    s48_record_set_2(call,  p, 1,
		     S48_ENTER_ENUM(call, 1, scx_property_formats_binding));
    for (i = nelements-1; i >= 0; i--)
      l = s48_cons_2(call, 
		     s48_enter_long_2(call, (((short*)data)[i])), l);
    s48_record_set_2(call,  p, 2, l);
    break;
  case 32:
    s48_record_set_2(call,  p, 1, 
		     S48_ENTER_ENUM(call, 2, scx_property_formats_binding));
    for (i = nelements-1; i >= 0; i--)
      l = s48_cons_2(call, 
		     s48_enter_long_2(call, (((long*)data)[i])), l);
    s48_record_set_2(call,  p, 2, l);
    break;
  default: /* should not be possible */
    s48_record_set_2(call,  p, 1, s48_enter_long_2(call, (format)));
    s48_record_set_2(call,  p, 2, s48_false_2(call));
  }
  return p;
}

s48_ref_t scx_Get_Window_Property(s48_call_t call, s48_ref_t display, 
	    s48_ref_t window,  s48_ref_t atom, s48_ref_t start,
	    s48_ref_t len, s48_ref_t deletep, s48_ref_t req_type) 
{
  Atom actual_type;
  int format;
  unsigned long nitems, bytes_left;
  unsigned char* data = NULL;
  s48_ref_t p = s48_false_2(call), res = s48_null_2(call);

  if (XGetWindowProperty (scx_extract_display(call, display), 
			  scx_extract_window(call, window),
			  scx_extract_atom(call, atom), 
			  s48_extract_long_2(call, start),
			  s48_extract_long_2(call, len),
			  s48_extract_boolean_2(call, deletep), 
			  scx_extract_atom(call, req_type),
			  &actual_type, &format, &nitems,
			  &bytes_left, &data) == Success) {
    p = scx_enter_property(call, actual_type, format, data, nitems);

    XFree(data);
    res = s48_cons_2(call, s48_enter_long_2(call, (bytes_left)), p);
    return res;
  } else
    /* Property does not exists */
    return s48_false_2(call);
}

s48_ref_t scx_change_property_mode_binding = NULL;
#define scx_extract_change_property_mode(c, x) \
  S48_EXTRACT_ENUM(c, x, scx_change_property_mode_binding)

s48_ref_t scx_Change_Property(s48_call_t call, s48_ref_t display, 
           s48_ref_t window, s48_ref_t atom,  s48_ref_t mode, 
           s48_ref_t property) 
{
  Atom type;
  int format, nelements;
  char* data;
  
  scx_extract_property(call, property, &type, &format, &data, &nelements);

  XChangeProperty(scx_extract_display(call, display), 
		  scx_extract_window(call, window),
		  scx_extract_atom(call, atom), type, format,
		  scx_extract_change_property_mode(call, mode),
		  data, nelements);
  free(data);
  return s48_unspecific_2(call);
}

s48_ref_t scx_Set_Selection_Owner(s48_call_t call, s48_ref_t display, 
            s48_ref_t selection, s48_ref_t owner, s48_ref_t time) {
  XSetSelectionOwner(scx_extract_display(call, display), 
		     scx_extract_atom(call, selection),
		     scx_extract_window(call, owner), 
		     scx_extract_time(call, time));
  return s48_unspecific_2(call);
}

s48_ref_t scx_Get_Selection_Owner(s48_call_t call, s48_ref_t display, 
				  s48_ref_t selection) 
{
  Window w;
  w = XGetSelectionOwner(scx_extract_display(call, display),
			 scx_extract_atom(call, selection));
  return scx_enter_window(call, w);
}

s48_ref_t scx_Convert_Selection(s48_call_t call, s48_ref_t display, 
				s48_ref_t selection,
				s48_ref_t target, s48_ref_t property,
				s48_ref_t requestor, s48_ref_t time) 
{
  XConvertSelection(scx_extract_display(call, display), 
		    scx_extract_atom(call, selection),
		    scx_extract_atom(call, target), 
		    scx_extract_atom(call, property),
		    scx_extract_window(call, requestor), 
		    scx_extract_time(call, time));
  return s48_unspecific_2(call);
}

void scx_init_property(void) {
  scx_property_binding = s48_get_imported_binding_2("scx-property");
  scx_property_format_binding = 
    s48_get_imported_binding_2("scx-property-format");
  scx_property_formats_binding = 
    s48_get_imported_binding_2("scx-property-formats");
  scx_change_property_mode_binding = 
    s48_get_imported_binding_2("scx-change-property-mode");

  S48_EXPORT_FUNCTION(scx_Intern_Atom);
  S48_EXPORT_FUNCTION(scx_Intern_Atoms);
  S48_EXPORT_FUNCTION(scx_Get_Atom_Name);
  S48_EXPORT_FUNCTION(scx_List_Properties);
  S48_EXPORT_FUNCTION(scx_Rotate_Window_Properties);
  S48_EXPORT_FUNCTION(scx_Delete_Property);
  S48_EXPORT_FUNCTION(scx_Get_Window_Property);
  S48_EXPORT_FUNCTION(scx_Change_Property);
  S48_EXPORT_FUNCTION(scx_Set_Selection_Owner);
  S48_EXPORT_FUNCTION(scx_Get_Selection_Owner);
  S48_EXPORT_FUNCTION(scx_Convert_Selection);
}
