#include "xlib.h"

s48_ref_t scx_Iconify_Window(s48_call_t call, s48_ref_t  display,
	    s48_ref_t w, s48_ref_t scr) {
  if (!XIconifyWindow(scx_extract_display(call, display),
		      scx_extract_window(call, w),
		      s48_extract_long_2(call, scr)))
    return s48_false_2(call);
  return s48_unspecific_2(call);
}

s48_ref_t scx_Withdraw_Window(s48_call_t call, s48_ref_t display,
	    s48_ref_t w, s48_ref_t scr) {
  if (!XWithdrawWindow(scx_extract_display(call, display),
		       scx_extract_window(call, w),
		       s48_extract_long_2(call, scr)))
    return s48_false_2(call);
  return s48_unspecific_2(call);
}

s48_ref_t scx_Reconfigure_Wm_Window(s48_call_t call, s48_ref_t dpy,
	    s48_ref_t w, s48_ref_t scr, s48_ref_t conf) {
  XWindowChanges WC;
  unsigned long mask;
  mask = scx_extract_window_changes(call, conf, &WC);

  if (!XReconfigureWMWindow(scx_extract_display(call, dpy),
			    scx_extract_window(call, w),
			    s48_extract_long_2(call, scr),
			    mask, &WC))
    return s48_false_2(call);
  return s48_unspecific_2(call);
}

s48_ref_t scx_Get_Wm_Command(s48_call_t call, s48_ref_t dpy, 
	    s48_ref_t w) {
  int i, ac;
  char** av;
  s48_ref_t ret = s48_null_2(call);

  if (!XGetCommand (scx_extract_display(call, dpy),
		    scx_extract_window(call, w),
		    &av, &ac))
    return s48_false_2(call);

  for (i = ac-1; i >= 0; i--)
    ret = s48_cons_2(call, s48_enter_string(call, av[i]), ret);
  if (av != NULL) XFreeStringList(av);
  return ret;
}

s48_ref_t scx_Set_Wm_Command(s48_call_t call, s48_ref_t dpy,
			     s48_ref_t w, s48_ref_t cmd) {
  int i, n = s48_extract_long_2(call, s48_length_2(call, cmd));

  char *argv[n];
  for (i = 0; i < n; i++) {
    s48_ref_t temp = cmd;
    argv[i] = s48_extract_string(call, s48_car_2(call, cmd));
    cmd = s48_cdr_2(call, cmd);
    s48_free_local_ref(call, cmd);
  }
  XSetCommand(scx_extract_display(call, dpy),
	      scx_extract_window(call, w),
	      argv, n);
  return s48_unspecific_2(call);
}

s48_ref_t scx_Get_Wm_Protocols(s48_call_t call, 
            s48_ref_t display, s48_ref_t w) {
  Atom *p;
  int i, n;
  s48_ref_t ret = s48_null_2(call);

  if (!XGetWMProtocols (scx_extract_display(call, display),
			scx_extract_window(call, w), &p, &n))
    return s48_false_2(call);

  for (i = n-1; i >= 0; i--)
    ret = s48_cons_2(call, scx_enter_atom(call, p[i]), ret);

  XFree((char *)p);
  return ret;
}

s48_ref_t scx_Set_Wm_Protocols (s48_call_t call, s48_ref_t display,
	    s48_ref_t w, s48_ref_t v) {
  int i, n = s48_extract_long_2(call, s48_length_2(call, v));

  Atom p[n];

  for (i = 0; i < n; i++) {
    s48_ref_t temp = v;
    p[i] = scx_extract_atom(call, s48_car_2(call, v));
    v = s48_cdr_2(call, v);

    s48_free_local_ref(call, temp);
  }

  if (!XSetWMProtocols (scx_extract_display(call, display),
			scx_extract_window(call, w),
			p, n))
    return s48_false_2(call);

  return s48_unspecific_2(call);
}

s48_ref_t scx_Get_Wm_Class (s48_call_t call, 
	    s48_ref_t display, s48_ref_t w) {
  s48_ref_t ret = s48_false_2(call);
  XClassHint c;

  c.res_name = c.res_class = 0;
  if (!XGetClassHint(scx_extract_display(call, display),
		     scx_extract_window(call, w), &c))
    return s48_false_2(call);

  ret = s48_enter_string(call, c.res_class);
  ret = s48_cons_2(call, s48_enter_string(call, c.res_name), ret);
  XFree(c.res_name);
  XFree(c.res_class);
  return ret;
}

s48_ref_t scx_Set_Wm_Class(s48_call_t call, s48_ref_t  dpy,
	   s48_ref_t w, s48_ref_t name, s48_ref_t class) {
  XClassHint c;
  c.res_name = s48_extract_string(call, name);
  c.res_class = s48_extract_string(call, class);
  XSetClassHint(scx_extract_display(call, dpy),
		scx_extract_window(call, dpy),
		&c);
  return s48_unspecific_2(call);
}

s48_ref_t scx_initial_state_binding = NULL;
#define scx_extract_initial_state(c, x) \
  S48_EXTRACT_ENUM(c, x, scx_initial_state_binding)
s48_ref_t scx_initial_states_binding = NULL;
#define scx_enter_initial_state(c, x) \
  S48_ENTER_ENUM(c, x, scx_initial_states_binding)

s48_ref_t scx_wm_hint_binding = NULL;
#define scx_extract_wm_hint(c, x) \
  S48_EXTRACT_ENUM(c, x, scx_wm_hint_binding)
s48_ref_t scx_wm_hints_binding = NULL;
#define scx_enter_wm_hint(c, x) \
  S48_ENTER_ENUM(c, x, scx_wm_hints_binding)

s48_ref_t scx_enter_wm_hint_alist(s48_call_t call, XWMHints* p) {
  s48_ref_t res = s48_null_2(call), t = s48_false_2(call);
  if (p->flags & InputHint) {
    t = scx_enter_wm_hint(call, 0);
    t = s48_cons_2(call, t, s48_enter_boolean_2(call, p->input));
    res = s48_cons_2(call, t, res);
  }
  if (p->flags & StateHint) {
    t = scx_enter_wm_hint(call, 1);
    t = s48_cons_2(call, t, 
	     scx_enter_initial_state(call, p->initial_state));
    res = s48_cons_2(call, t, res);
  }
  if (p->flags & IconPixmapHint) {
    t = scx_enter_wm_hint(call, 2);
    t = s48_cons_2(call, t, scx_enter_pixmap(call, p->icon_pixmap));
    res = s48_cons_2(call, t, res);
  }
  if (p->flags & IconWindowHint) {
    t = scx_enter_wm_hint(call, 3);
    t = s48_cons_2(call, t, scx_enter_window(call, p->icon_window));
    res = s48_cons_2(call, t, res);
  }
  if (p->flags & IconPositionHint) {
    t = s48_enter_long_2(call, p->icon_y);
    t = s48_cons_2(call, s48_enter_long_2(call, p->icon_x), t);
    t = s48_cons_2(call, scx_enter_wm_hint(call, 4), t);
    res = s48_cons_2(call, t, res);
  }
  if (p->flags & IconMaskHint) {
    t = scx_enter_wm_hint(call, 5);
    t = s48_cons_2(call, t, scx_enter_pixmap(call, p->icon_mask));
    res = s48_cons_2(call, t, res);
  }
  if (p->flags & WindowGroupHint) {
    t = scx_enter_wm_hint(call, 6);
    t = s48_cons_2(call, t, scx_enter_window(call, p->window_group));
    res = s48_cons_2(call, t, res);
  }
  t = scx_enter_wm_hint(call, 8);
  t = s48_cons_2(call, t, 
	     s48_enter_boolean_2(call, p->flags & XUrgencyHint));
  res = s48_cons_2(call, t, res);

  return res;
}

void scx_extract_wm_hint_alist(s48_call_t call, 
       s48_ref_t alist, XWMHints* p) {
  s48_ref_t v = s48_false_2(call);
  p->flags = 0;
  while (!s48_null_p_2(call, alist)) {
    int h = scx_extract_wm_hint(call, 
              s48_car_2(call, s48_car_2(call, alist)));
    v = s48_cdr_2(call, s48_car_2(call, alist));
    p->flags |= (1L << h);
    switch (1L << h) {
    case InputHint:
      p->input = s48_extract_boolean_2(call, v);
      break;
    case StateHint:
      p->initial_state = scx_extract_initial_state(call, v);
      break;
    case IconPixmapHint:
      p->icon_pixmap = scx_extract_pixmap(call, v);
      break;
    case IconWindowHint:
      p->icon_window = scx_extract_window(call, v);
      break;
    case IconPositionHint:
      p->icon_x = s48_extract_long_2(call, s48_car_2(call, v));
      p->icon_y = s48_extract_long_2(call, s48_cdr_2(call, v));
      break;
    case IconMaskHint:
      p->icon_mask = scx_extract_pixmap(call, v);
      break;
    case WindowGroupHint:
      p->window_group = scx_extract_window(call, v);
      break;
    case XUrgencyHint:
      if (v == s48_false_2(call))
	p->flags &= ~XUrgencyHint;
      break;
    }
    alist = s48_cdr_2(call, alist);
  }
}

s48_ref_t scx_Get_Wm_Hints(s48_call_t call, 
	    s48_ref_t dpy, s48_ref_t w) {
  XWMHints* p;
  s48_ref_t res = s48_null_2(call);

  p = XGetWMHints(scx_extract_display(call, dpy),
		  scx_extract_window(call, w));
  if (p == NULL)
    return s48_false_2(call);
  res = scx_enter_wm_hint_alist(call, p);
  XFree(p);
  return res;
}

s48_ref_t scx_Set_Wm_Hints(s48_call_t call, 
            s48_ref_t dpy, s48_ref_t w, s48_ref_t hints) {
  XWMHints WMH;
  scx_extract_wm_hint_alist(call, hints, &WMH);

  XSetWMHints(scx_extract_display(call, dpy),
	      scx_extract_window(call, w),
	      &WMH);
  return s48_unspecific_2(call);
}

s48_ref_t scx_Get_Transient_For(s48_call_t call, 
            s48_ref_t dpy, s48_ref_t w) {
  Window win;
  if (!XGetTransientForHint(scx_extract_display(call, dpy),
			    scx_extract_window(call, w),
			    &win))
    return s48_false_2(call);
  return scx_enter_window(call, win);
}

s48_ref_t scx_Set_Transient_For(s48_call_t call, 
            s48_ref_t dpy, s48_ref_t w, s48_ref_t pw) {
  XSetTransientForHint(scx_extract_display(call, dpy),
		       scx_extract_window(call, w),
		       scx_extract_window(call, pw));
  return s48_unspecific_2(call);
}

s48_ref_t scx_Get_Text_Property(s48_call_t call, 
            s48_ref_t dpy, s48_ref_t w, s48_ref_t a) {
  XTextProperty ret;
  s48_ref_t res = s48_false_2(call);
  if (!XGetTextProperty (scx_extract_display(call, dpy),
			 scx_extract_window(call, w),
			 &ret,
			 scx_extract_atom(call, a)))
    return s48_false_2(call);
  res = scx_enter_property(call, ret.encoding, 
			   ret.format, ret.value, ret.nitems);
  XFree(ret.value);
  return res;
}

s48_ref_t scx_Set_Text_Property(s48_call_t call, s48_ref_t  dpy, s48_ref_t w, s48_ref_t prop,
				s48_ref_t a) {
  XTextProperty p;
  scx_extract_property(call, prop, &p.encoding, &p.format,
		       (char**)&p.value,
		       (int*)&p.nitems);
  XSetTextProperty(scx_extract_display(call, dpy),
		   scx_extract_window(call, w),
		   &p, scx_extract_atom(call, a));
  return s48_unspecific_2(call);
}

s48_ref_t scx_size_hint_binding = NULL;
#define scx_extract_size_hint(c, h) \
          S48_EXTRACT_ENUM(c, h, scx_size_hint_binding)

s48_ref_t scx_size_hints_binding = NULL;
#define scx_enter_size_hint(c, h) \
          S48_ENTER_ENUM(c, h, scx_size_hints_binding)

s48_ref_t scx_enter_size_hint_alist(s48_call_t call, XSizeHints* sh) {
  int i;
  s48_ref_t res = s48_null_2(call);
  s48_ref_t v = s48_false_2(call);
  s48_ref_t t = s48_false_2(call);

  for (i = 0; i < 10; i++) {
    if (sh->flags & (1L << i)) {
      switch (1L << i) {
      case USPosition: case PPosition:
	v = s48_enter_long_2(call, sh->x);
	v = s48_cons_2(call, v, s48_enter_long_2(call, sh->y));
	break;
      case USSize: case PSize:
	v = s48_enter_long_2(call, sh->width);
	v = s48_cons_2(call, v, 
		       s48_enter_long_2(call, sh->height));
	break;
      case PMinSize:
	v = s48_enter_long_2(call, sh->min_width);
	v = s48_cons_2(call, v, 
		       s48_enter_long_2(call, sh->min_height));
	break;
      case PMaxSize:
	v = s48_enter_long_2(call, sh->max_width);
	v = s48_cons_2(call, v, 
		       s48_enter_long_2(call, sh->max_height));
	break;
      case PResizeInc:
	v = s48_enter_long_2(call, sh->width_inc);
	v = s48_cons_2(call, v, 
		       s48_enter_long_2(call, sh->height_inc));
	break;
      case PAspect:
	v = s48_enter_long_2(call, sh->min_aspect.x);
	v = s48_cons_2(call, v, 
		       s48_enter_long_2(call, sh->min_aspect.y));
	t = s48_enter_long_2(call, sh->max_aspect.x);
	t = s48_cons_2(call, t, 
		       s48_enter_long_2(call, sh->max_aspect.y));
	v = s48_cons_2(call, v, t);
	break;
      case PBaseSize:
	v = s48_enter_long_2(call, sh->base_width);
	v = s48_cons_2(call, v, 
		       s48_enter_long_2(call, sh->base_height));
	break;
      case PWinGravity:
	v = scx_enter_win_gravity(call, sh->win_gravity);
	break;
      default: v = s48_false_2(call);
      }
      t = scx_enter_size_hint(call, i);
      t = s48_cons_2(call, t, v);
      res = s48_cons_2(call, t, res);
    }
  }
  return res;
}

void scx_extract_size_hint_alist(s48_call_t call, 
       s48_ref_t l, XSizeHints* sh) {
  s48_ref_t v = s48_false_2(call);
  sh->flags = 0;

  while (!s48_null_p_2(call, l)) {
    int m = scx_extract_size_hint(call, 
              s48_car_2(call, s48_car_2(call, l)));

    s48_ref_t temp = l;
    v = s48_cdr_2(call, s48_car_2(call, l));
    l = s48_cdr_2(call, l);
    s48_free_local_ref(call, temp);

    sh->flags |= (1L << m);
    switch (1L << m) {
    case USPosition: case PPosition:
      sh->x = s48_extract_long_2(call, s48_car_2(call, v));
      sh->y = s48_extract_long_2(call, s48_cdr_2(call, v));
      break;
    case USSize: case PSize:
      sh->width = s48_extract_long_2(call, s48_car_2(call, v));
      sh->height = s48_extract_long_2(call, s48_cdr_2(call, v));
      break;
    case PMinSize:
      sh->min_width = 
	s48_extract_long_2(call, s48_car_2(call, v));
      sh->min_height = 
	s48_extract_long_2(call, s48_cdr_2(call, v));
      break;
    case PMaxSize:
      sh->max_width = 
	s48_extract_long_2(call, s48_car_2(call, v));
      sh->max_height = 
	s48_extract_long_2(call, s48_cdr_2(call, v));
      break;
    case PResizeInc:
      sh->width_inc = 
	s48_extract_long_2(call, s48_car_2(call, v));
      sh->height_inc = 
	s48_extract_long_2(call, s48_cdr_2(call, v));
      break;
    case PAspect:
      sh->min_aspect.x = s48_extract_long_2(call, 
			   s48_car_2(call, s48_car_2(call, v)));
      sh->min_aspect.y = s48_extract_long_2(call, 
			   s48_cdr_2(call, s48_car_2(call, v)));
      sh->max_aspect.x = s48_extract_long_2(call, 
                           s48_car_2(call, s48_cdr_2(call, v)));
      sh->max_aspect.y = s48_extract_long_2(call, 
			   s48_cdr_2(call, s48_cdr_2(call, v)));
      break;
    case PBaseSize:
      sh->base_width = s48_extract_long_2(call, s48_car_2(call, v));
      sh->base_height = s48_extract_long_2(call, s48_cdr_2(call, v));
      break;
    case PWinGravity:
      sh->win_gravity = scx_extract_win_gravity(call, v);
      break;
    }
  }
}

s48_ref_t scx_Get_Wm_Normal_Hints(s48_call_t call, 
            s48_ref_t dpy, s48_ref_t win) {
  XSizeHints SH;
  long supplied_by_user;
  if (!XGetWMNormalHints(scx_extract_display(call, dpy),
			 scx_extract_window(call, win),
			 &SH, &supplied_by_user))
    return s48_false_2(call);
  /* ignoring supplied_by_user ... ?! */
  return scx_enter_size_hint_alist(call, &SH);
}

s48_ref_t scx_Set_Wm_Normal_Hints(s48_call_t call, 
           s48_ref_t dpy, s48_ref_t win, s48_ref_t hints) {
  XSizeHints SH;
  scx_extract_size_hint_alist(call, hints, &SH);

  XSetWMNormalHints(scx_extract_display(call, dpy),
		    scx_extract_window(call, win),
		    &SH);
  return s48_unspecific_2(call);
}

s48_ref_t scx_icon_size_binding = NULL;

s48_ref_t scx_enter_icon_size(s48_call_t call, XIconSize* is) {
  s48_ref_t res = s48_make_record_2(call, scx_icon_size_binding);
  s48_record_set_2(call, res, 0, 
		   s48_enter_long_2(call, is->min_width));
  s48_record_set_2(call, res, 1, 
		   s48_enter_long_2(call, is->min_height));
  s48_record_set_2(call, res, 2, 
		   s48_enter_long_2(call, is->max_width));
  s48_record_set_2(call, res, 3, 
		   s48_enter_long_2(call, is->max_height));
  s48_record_set_2(call, res, 4, 
		   s48_enter_long_2(call, is->width_inc));
  s48_record_set_2(call, res, 5, 
		   s48_enter_long_2(call, is->height_inc));
  return res;
}

void scx_extract_icon_size(s48_call_t call, s48_ref_t r, XIconSize* is) {
  s48_check_record_type_2(call, r, scx_icon_size_binding);
  is->min_width = 
    s48_extract_long_2(call, s48_record_ref_2(call, r, 0));
  is->min_height = 
    s48_extract_long_2(call, s48_record_ref_2(call, r, 1));
  is->max_width = 
    s48_extract_long_2(call, s48_record_ref_2(call, r, 2));
  is->max_height = 
    s48_extract_long_2(call, s48_record_ref_2(call, r, 3));
  is->width_inc = 
    s48_extract_long_2(call, s48_record_ref_2(call, r, 4));
  is->height_inc = 
    s48_extract_long_2(call, s48_record_ref_2(call, r, 5));
}

s48_ref_t scx_Get_Icon_Sizes(s48_call_t call, s48_ref_t dpy, s48_ref_t w) {
  XIconSize *p;
  int i, n;
  s48_ref_t v = s48_null_2(call);

  if (!XGetIconSizes (scx_extract_display(call, dpy),
		      scx_extract_window(call, w),
		      &p, &n))
    return s48_false_2(call);

  for (i = n-1; i >= 0; i--)
    v = s48_cons_2(call, scx_enter_icon_size(call, &p[i]), v);
  XFree((char *)p);
  return v;
}

s48_ref_t scx_Set_Icon_Sizes(s48_call_t call, 
            s48_ref_t dpy, s48_ref_t w, s48_ref_t v) {
  int i, n = s48_extract_long_2(call, s48_length_2(call, v));
  XIconSize p[n];
  for (i = 0; i < n; i++) {
    scx_extract_icon_size(call, s48_car_2(call, v), &p[i]);
    s48_ref_t temp = v;
    v = s48_cdr_2(call, v);
    s48_free_local_ref(call, temp);
  }
  XSetIconSizes(scx_extract_display(call, dpy),
		scx_extract_window(call, w),
		p, n);
  return s48_unspecific_2(call);
}

void scx_init_client() {
  scx_icon_size_binding = 
    s48_get_imported_binding_2("scx-icon-size");
  scx_initial_state_binding = 
    s48_get_imported_binding_2("scx-initial-state");
  scx_initial_states_binding = 
    s48_get_imported_binding_2("scx-initial-states");
  scx_wm_hint_binding = 
    s48_get_imported_binding_2("scx-wm-hint");
  scx_wm_hints_binding = 
    s48_get_imported_binding_2("scx-wm-hints");
  scx_size_hint_binding = 
    s48_get_imported_binding_2("scx-size-hint");
  scx_size_hints_binding = 
    s48_get_imported_binding_2("scx-size-hints");

  S48_EXPORT_FUNCTION(scx_Iconify_Window);
  S48_EXPORT_FUNCTION(scx_Withdraw_Window);
  S48_EXPORT_FUNCTION(scx_Reconfigure_Wm_Window);
  S48_EXPORT_FUNCTION(scx_Get_Wm_Command);
  S48_EXPORT_FUNCTION(scx_Get_Text_Property);
  S48_EXPORT_FUNCTION(scx_Set_Text_Property);
  S48_EXPORT_FUNCTION(scx_Get_Wm_Protocols);
  S48_EXPORT_FUNCTION(scx_Set_Wm_Protocols);
  S48_EXPORT_FUNCTION(scx_Get_Wm_Class);
  S48_EXPORT_FUNCTION(scx_Set_Wm_Class);
  S48_EXPORT_FUNCTION(scx_Set_Wm_Command);
  S48_EXPORT_FUNCTION(scx_Get_Wm_Hints);
  S48_EXPORT_FUNCTION(scx_Set_Wm_Hints);
  S48_EXPORT_FUNCTION(scx_Get_Transient_For);
  S48_EXPORT_FUNCTION(scx_Set_Transient_For);
  S48_EXPORT_FUNCTION(scx_Get_Wm_Normal_Hints);
  S48_EXPORT_FUNCTION(scx_Set_Wm_Normal_Hints);
  S48_EXPORT_FUNCTION(scx_Get_Icon_Sizes);
  S48_EXPORT_FUNCTION(scx_Set_Icon_Sizes);
}
