#include "xlib.h"
#include <stdio.h>

s48_ref_t scx_error_code_binding = NULL;
s48_ref_t scx_error_codes_binding = NULL;

s48_ref_t scx_enter_error_code(s48_call_t call, int code) {
  s48_ref_t v = s48_shared_binding_ref_2(call, scx_error_codes_binding);
  if (code < s48_vector_length_2(call, v))
    return s48_vector_ref_2(call, v, code);
  else
    return s48_enter_long_2(call, code); /* Extension Errors */
}

long scx_extract_error_code(s48_call_t call, s48_ref_t code) {
  if (s48_record_p_2(call, code))
    return s48_extract_long_2(call, s48_checked_record_ref_2(call, code, 1,
						      scx_error_code_binding));
  else
    return s48_extract_long_2(call, code);
}

s48_ref_t scx_x_error_binding = NULL;

s48_ref_t scx_enter_x_error(s48_call_t call, XErrorEvent* xe) {
  s48_ref_t e = s48_make_record_2(call, scx_x_error_binding);
  char s[1024];
  
  s48_record_set_2(call, e, 0, scx_enter_display(call, scx_get_display_bundle(call, xe->display)));
  s48_record_set_2(call, e, 1, s48_enter_long_2(call, xe->serial));
  s48_record_set_2(call, e, 2, scx_enter_error_code(call, xe->error_code));
  s48_record_set_2(call, e, 3, s48_enter_long_2(call, xe->request_code));
  s48_record_set_2(call, e, 4, s48_enter_long_2(call, xe->minor_code));
  s48_record_set_2(call, e, 5, s48_enter_long_2(call, xe->resourceid));
  XGetErrorText(xe->display, xe->error_code, s, 1023);
  s48_record_set_2(call, e, 6, s48_enter_string(call, s));
  
  return e;
}

void scx_extract_x_error(s48_call_t call, s48_ref_t e, XErrorEvent* xe) {
  s48_check_record_type_2(call, e, scx_x_error_binding);
  xe->type = 1;
  xe->display = scx_extract_display(call, s48_record_ref_2(call, e, 0));
  xe->serial = s48_extract_long_2(call, s48_record_ref_2(call, e, 1));
  xe->error_code = scx_extract_error_code(call, s48_record_ref_2(call, e, 2));
  xe->request_code = s48_extract_long_2(call, s48_record_ref_2(call, e, 3));
  xe->minor_code = s48_extract_long_2(call, s48_record_ref_2(call, e, 4));
  xe->resourceid = s48_extract_long_2(call, s48_record_ref_2(call, e, 5));
}

/* Default error handlers of the Xlib */
extern int _XDefaultIOError();   
extern int _XDefaultError();

static s48_ref_t internal_x_error_handler_binding = NULL;

static int error_handler_wrapper(Display* dpy, XErrorEvent* e) {
  s48_call_t call = s48_get_current_call();
  
  char handled = 0;
  if ((!s48_false_p_2(call, internal_x_error_handler_binding)) &&
      (!s48_false_p_2(call, 
	 s48_shared_binding_ref_2(call, 
				  internal_x_error_handler_binding)))){
    s48_ref_t v = 
      s48_false_2(call), display = s48_false_2(call), err = s48_false_2(call);
    display = scx_enter_display(call,  scx_get_display_bundle(call, dpy));
    err = scx_enter_x_error(call, e);

    v = s48_call_scheme_2(call, 
       s48_shared_binding_ref_2(call, internal_x_error_handler_binding),
			  2, display, err);
    handled = (!s48_false_p_2(call, v));
  }
  if (!handled)
    _XDefaultError(dpy, e);
  return 0;
}

s48_ref_t scx_Get_Error_Text(s48_call_t call, s48_ref_t display, s48_ref_t code) {
  char buf[1024];
  XGetErrorText(scx_extract_display(call, display), 
		scx_extract_error_code(call, code),
		buf, 1024);
  buf[1023] = 0;
  return s48_enter_string(call, buf);
}

s48_ref_t scx_Get_Error_Database_Text(s48_call_t call, s48_ref_t display, 
	    s48_ref_t name, s48_ref_t message, s48_ref_t def) {
  char buf[1024];
  XGetErrorDatabaseText(scx_extract_display(call, display),
			s48_extract_string(call, name),
			s48_extract_string(call, message),
			s48_extract_string(call, def),
			buf, 1024);
  buf[1023] = 0;
  return s48_enter_string(call, buf);
}

s48_ref_t internal_x_fatal_error_handler_binding = NULL;

static int fatal_error_handler_wrapper(Display* d) {
  s48_call_t call = s48_get_current_call();
  /* call the scheme-func internal-x-fatal-error-handler, which does
     the rest. */
  
  if ((!s48_false_p_2(call, internal_x_fatal_error_handler_binding)) &&
      (!s48_false_p_2(call, s48_shared_binding_ref_2(call, 
			    internal_x_fatal_error_handler_binding))))
    s48_call_scheme_2(call, s48_shared_binding_ref_2(
                      call, internal_x_fatal_error_handler_binding),
		    1, scx_enter_display(call,  scx_get_display_bundle(call, d)));
  /* In case the scheme error handler does not exit (or none exists): */
  _XDefaultIOError (d);
  
  /* And if even the default handler does not exit: */
  exit(1);         
  /*NOTREACHED*/
  return 0;
}

void scx_init_error() {
  scx_error_code_binding = s48_get_imported_binding_2("scx-error-code");
  scx_error_codes_binding = s48_get_imported_binding_2("scx-error-codes");
  scx_x_error_binding = s48_get_imported_binding_2("scx-x-error");
  
  S48_EXPORT_FUNCTION(scx_Get_Error_Text);
  S48_EXPORT_FUNCTION(scx_Get_Error_Database_Text);

  internal_x_fatal_error_handler_binding =
    s48_get_imported_binding_2("internal-x-fatal-error-handler");
  internal_x_error_handler_binding =
    s48_get_imported_binding_2("internal-x-error-handler");

  (void)XSetIOErrorHandler(fatal_error_handler_wrapper);
  (void)XSetErrorHandler(error_handler_wrapper);
}
