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

#include "xlib.h"

s48_ref_t scx_Change_Keyboard_Mapping(s48_call_t call, s48_ref_t display,
				      s48_ref_t first_keycode,
				      s48_ref_t keysyms_lists) 
{
  int n = s48_extract_long_2(call, s48_length_2(call, keysyms_lists));
  
  int max = 0, i;
  s48_ref_t l = keysyms_lists;
  for (i = 0; i < n; i++) {
    int m = s48_extract_long_2(call, s48_length_2(call, s48_car_2(call, l)));
    if (m > max) max = m;
    l = s48_cdr_2(call, l);
  }
  {
    KeySym ks[max * n];
    l = keysyms_lists;
    for (i = 0; i < n; i++) {
      s48_ref_t l2 = s48_car_2(call, l);
      int j, n2 = s48_extract_long_2(call, s48_length_2(call, l2));
      for (j = 0; j < n2; j++) {
	if (s48_null_p_2(call, l2))
	  ks[i * max + j] = NoSymbol;
	else {
	  ks[i * max + j] = scx_extract_keysym(call, s48_car_2(call, l2));
	  s48_ref_t temp = l2;
	  l2 = s48_cdr_2(call, l2);
	  s48_free_local_ref(call, temp);
	}
      }

      s48_ref_t temp = l;
      l = s48_cdr_2(call, l);
      s48_free_local_ref(call, temp);
    }

    XChangeKeyboardMapping(scx_extract_display(call, display),
			   s48_extract_long_2(call, first_keycode),
			   max, ks, max * n);
  }
  return s48_unspecific_2(call);
}

s48_ref_t scx_Get_Keyboard_Mapping(s48_call_t call, s48_ref_t display, 
           s48_ref_t first_keycode, s48_ref_t count) {
  int kpk, ccount = s48_extract_long_2(call, count);
  KeySym* ks = XGetKeyboardMapping(scx_extract_display(call, display),
				   s48_extract_long_2(call, first_keycode),
				   ccount, &kpk);
  s48_ref_t l = s48_null_2(call), l2 = s48_null_2(call);
  int i;

  for (i = ccount; i > 0; i--) {
    int j;
    l2 = s48_null_2(call);
    for (j = kpk; j > 0; j--) {
      if (ks[i * kpk + j - 1] != NoSymbol)
	l2 = s48_cons_2(call, scx_enter_keysym(call, ks[i * kpk + j - 1]), l2);
    }
    l = s48_cons_2(call, l2, l);
  }

  XFree(ks);
  return l;
}

s48_ref_t scx_Display_Keycodes(s48_call_t call, s48_ref_t display) {
  int min, max;
  XDisplayKeycodes(scx_extract_display(call, display), &min, &max);
  return s48_cons_2(call, s48_enter_long_as_fixnum_2(call, min), 
                    s48_enter_long_as_fixnum_2(call, max));
}

s48_ref_t scx_Set_Modifier_Mapping(s48_call_t call, s48_ref_t display, 
				   s48_ref_t modmap) {
  int max = 0;
  s48_ref_t l = modmap, l2 = s48_false_2(call);
  for (;  !s48_null_p_2(call, l); l = s48_cdr_2(call, l)) {
    int m = s48_extract_long_2(call, 
               s48_length_2(call, s48_cdr_2(call, s48_car_2(call, l))));
    if (m > max) max = m;
  }
  {
    int res;
    KeyCode ks[8*max];
    XModifierKeymap cmap;
    cmap.max_keypermod = max;
    cmap.modifiermap = ks;
    
    for (l = modmap; !s48_null_p_2(call, l); l = s48_cdr_2(call, l)) {
      int mod = scx_extract_state(call, s48_car_2(call, s48_car_2(call, l)));
      int j;
      l2 = s48_cdr_2(call, s48_car_2(call, l));
      j = 0;
      for (j = 0; j < max; j++) {
	if ((mod < 0) || (mod > 7)) continue; /* TODO: error?? */
	if (l2 != s48_null_2(call)) {
	  ks[mod*max + j] = s48_extract_long_2(call, s48_car_2(call, l2));
	  l2 = s48_cdr_2(call, l2);
	} else
	  ks[mod*max + j] = 0;
      }
    }
    res = XSetModifierMapping(scx_extract_display(call, display),
			      &cmap);
    return s48_enter_long_2(call, res);
  }
}

s48_ref_t scx_Get_Modifier_Mapping(s48_call_t call, s48_ref_t display) {
  XModifierKeymap* km;
  s48_ref_t l = s48_null_2(call), l2 = s48_null_2(call);
  int i;
  km = XGetModifierMapping(scx_extract_display(call, display));
  for (i = 7; i >= 0; i--) {
    int j;
    l2 = s48_null_2(call);
    for (j = km->max_keypermod - 1; j >= 0; j--) {
      KeyCode c = km->modifiermap[i*km->max_keypermod + j];
      l2 = s48_cons_2(call, s48_enter_long_2(call, c), l2);
    }
    l2 = s48_cons_2(call, scx_enter_state(call, i), l2);
    l = s48_cons_2(call, l2, l);
  }
  XFreeModifiermap(km);
  return l;
}

s48_ref_t scx_String_To_Keysym(s48_call_t call, s48_ref_t string) {
  return scx_enter_keysym(call, XStringToKeysym(s48_extract_string(call, string)));
}

s48_ref_t scx_Keysym_To_String(s48_call_t call, s48_ref_t ks) {
  char* s = XKeysymToString(scx_extract_keysym(call, ks));
  s48_ref_t res = s48_enter_string(call, s);
  XFree(s);
  return res;
}

s48_ref_t scx_Keycode_To_Keysym(s48_call_t call, s48_ref_t display, 
             s48_ref_t kc, s48_ref_t i) {
  KeySym ks;
  ks = XKeycodeToKeysym(scx_extract_display(call, display),
			s48_extract_long_2(call, kc),
			s48_extract_long_2(call, i));
  return scx_enter_keysym(call, ks);
}

s48_ref_t scx_Keysym_To_Keycode(s48_call_t call, s48_ref_t display, 
            s48_ref_t ks) {
  KeyCode kc;
  kc = XKeysymToKeycode(scx_extract_display(call, display),
				scx_extract_keysym(call, ks));
  return s48_enter_long_2(call, kc);
}

s48_ref_t scx_Convert_Case(s48_call_t call, s48_ref_t keysym) {
  KeySym low, up;
  s48_ref_t res = s48_false_2(call);
  XConvertCase(scx_extract_keysym(call, keysym), &low, &up);
  res = scx_enter_keysym(call, up);
  res = s48_cons_2(call, scx_enter_keysym(call, low), res);
  return res;
}

s48_ref_t scx_Lookup_Keysym(s48_call_t call, s48_ref_t key_event, 
            s48_ref_t index) {
  XKeyEvent ke;
  s48_ref_t res = s48_false_2(call);
  scx_extract_key_event(call, key_event, &ke);
  res = scx_enter_keysym(call, 
          XLookupKeysym(&ke, s48_extract_long_2(call, index)));
  return res;
}

s48_ref_t scx_Refresh_Keyboard_Mapping(s48_call_t call, s48_ref_t mapping_event) {
  XMappingEvent e;
  scx_extract_mapping_event(call, mapping_event, &e);
  XRefreshKeyboardMapping(&e);
  return s48_unspecific_2(call);
}

s48_ref_t scx_Lookup_String(s48_call_t call, s48_ref_t key_event) {
  XKeyEvent e;
  char buf[1024]; 
  int len; 
  KeySym keysym_return; 
  s48_ref_t res = s48_false_2(call);
  
  scx_extract_key_event(call, key_event, &e);
  len = XLookupString(&e, buf, 1023, &keysym_return, NULL);
  buf[len] = 0;

  res = s48_enter_string(call, buf);
  res = s48_cons_2(call, scx_enter_keysym(call, keysym_return), res);

  return res;
}

s48_ref_t scx_Rebind_Keysym(s48_call_t call, s48_ref_t display, 
           s48_ref_t keysym, s48_ref_t mod_keysyms, s48_ref_t str) {
  int i, n = s48_extract_long_2(call, s48_length_2(call, mod_keysyms));
  
  KeySym mods[n];

  for (i = 0; i < n; i++) {
    mods[i] = scx_extract_keysym(call, s48_car_2(call, mod_keysyms));
    mod_keysyms = s48_cdr_2(call, mod_keysyms);
  }
  XRebindKeysym(scx_extract_display(call, display), 
		scx_extract_keysym(call, keysym),
		mods, n, 
		(unsigned char *)s48_extract_string(call, str),
		s48_string_length_2(call, str));
  return s48_unspecific_2(call);
}

void scx_init_key () {
  S48_EXPORT_FUNCTION(scx_Change_Keyboard_Mapping);
  S48_EXPORT_FUNCTION(scx_Get_Keyboard_Mapping);
  S48_EXPORT_FUNCTION(scx_Display_Keycodes);
  S48_EXPORT_FUNCTION(scx_Set_Modifier_Mapping);
  S48_EXPORT_FUNCTION(scx_Get_Modifier_Mapping);
  S48_EXPORT_FUNCTION(scx_String_To_Keysym);
  S48_EXPORT_FUNCTION(scx_Keysym_To_String);
  S48_EXPORT_FUNCTION(scx_Keycode_To_Keysym);
  S48_EXPORT_FUNCTION(scx_Keysym_To_Keycode);
  S48_EXPORT_FUNCTION(scx_Convert_Case);
  S48_EXPORT_FUNCTION(scx_Lookup_Keysym);
  S48_EXPORT_FUNCTION(scx_Refresh_Keyboard_Mapping);
  S48_EXPORT_FUNCTION(scx_Lookup_String);
  S48_EXPORT_FUNCTION(scx_Rebind_Keysym);
}
