#include "xt.h"
#include <X11/Xmu.h>

#include <ctype.h>

static SYMDESCR Orientation_Syms[] = {
    { "horizontal",         XtorientHorizontal },
    { "vertical",           XtorientVertical },
    { 0, 0 }
};

static SYMDESCR Justify_Syms[] = {
    { "left",              XtJustifyLeft },
    { "center",            XtJustifyCenter },
    { "right",             XtJustifyRight },
    { 0, 0 }
};

#define XtRFloat             "Float"
#define XtRWidget            "Widget"

#define T_Unknown            -1
#define T_String_Or_Symbol   -2
#define T_Callbacklist       -3
#define T_Float              -4
#define T_Backing_Store      -5
#define T_Orientation        -6
#define T_Justify            -7
#define T_Translations       -8

static Resource_To_Scheme_Type (t) register char *t; {
    if (streq (XtRBackingStore, t))
	return T_Backing_Store;
    else if (streq (XtRBoolean, t))
	return T_Boolean;
    else if (streq (XtRCallback, t))
	return T_Callbacklist;
    else if (streq (XtRCursor, t))
	return T_Cursor;
    else if (streq (XtRDimension, t))
	return T_Fixnum;
    else if (streq (XtRDisplay, t))
	return T_Display;
    else if (streq (XtRFloat, t))
	return T_Float;
    else if (streq (XtRFont, t))
	return T_Font;
    else if (streq (XtRFontStruct, t))
	return T_Font;
    else if (streq (XtRInt, t))
	return T_Fixnum;
    else if (streq (XtRJustify, t))
	return T_Justify;
    else if (streq (XtROrientation, t))
	return T_Orientation;
    else if (streq (XtRPixel, t))
	return T_Pixel;
    else if (streq (XtRPixmap, t))
	return T_Pixmap;
    else if (streq (XtRPosition, t))
	return T_Fixnum;
    else if (streq (XtRShort, t))
	return T_Fixnum;
    else if (streq (XtRString, t))
	return T_String_Or_Symbol;
    else if (streq (XtRTranslationTable, t))
	return T_Translations;
    else if (streq (XtRUnsignedChar, t))
	return T_Character;
    else if (streq (XtRWidget, t))
	return T_Widget;
    else if (streq (XtRWindow, t))
	return T_Window;
    return T_Unknown;
}

Get_All_Resources (w, c, rp, np, cp) Widget w; WidgetClass c;
	XtResource **rp; int *np, *cp; {
    XtResource *r, *sr, *cr;
    int nr, snr = 0, cnr = 0;

    XtGetResourceList (c, &r, &nr);
    if (w == 0) /* Not allowed with get-values and set-values! */
	Get_Sub_Resource_List (c, &sr, &snr);
    if (w && XtParent (w))
	XtGetConstraintResourceList (XtClass (XtParent (w)), &cr, &cnr);
    *np = nr + snr + cnr;
    *cp = cnr;
    *rp = (XtResource *)XtMalloc (*np * sizeof (XtResource));
    bcopy ((char *)r, (char *)*rp, nr * sizeof (XtResource));
    XtFree ((char *)r);
    if (snr)
	bcopy ((char *)sr, (char *)(*rp + nr), snr * sizeof (XtResource));
    if (cnr) {
	bcopy ((char *)cr, (char *)(*rp + nr+snr), cnr * sizeof (XtResource));
	XtFree ((char *)cr);
    }
}

Convert_Args (ac, av, to, widget, class) Object *av; ArgList to;
	Widget widget; WidgetClass class; {
    register char *name, *res;
    register i, j, k;
    Object arg, val;
    XtResource *r;
    int nr, nc;
    int st, dt;
    char key[128];
    PFX converter;

    if (ac & 1)
	Primitive_Error ("missing argument value");
    Get_All_Resources (widget, class, &r, &nr, &nc);
    /* Note:
     * `r' is not freed in case of error.
     */
    for (i = k = 0; k < ac; i++, k++) {
	arg = av[k];
	Make_C_String (arg, name);
	Make_Resource_Name (name);
	for (j = 0; j < nr && !streq (name, r[j].resource_name); j++)
	    ;
	if (j == nr)
	    Primitive_Error ("no such resource: ~s", arg);
	res = r[j].resource_name;
	val = av[++k];
	st = TYPE(val);
	dt = Resource_To_Scheme_Type (r[j].resource_type);

	if (widget && j >= nr-nc)
	    class = XtClass (XtParent (widget));
	sprintf (key, "%s-%s", Class_Name (class), name);
	converter = Find_Converter_To_C (key);

	if (converter) {
	    XtArgVal ret = converter (val);
	    XtSetArg (to[i], res, ret);
	} else if (dt == T_String_Or_Symbol) {
	    char *s;

	    Make_C_String (val, s);
	    XtSetArg (to[i], res, XtNewString (s));  /* Never freed! */
	} else if (dt == T_Callbacklist) {
	    int n;
	    XtCallbackList callbacks;

	    Check_Callback_List (val);
	    n = Internal_Length (val);
	    callbacks = (XtCallbackRec *)  /* Never freed! */
		    XtMalloc ((n+1) * sizeof (XtCallbackRec));
	    callbacks[n].callback = 0;
	    callbacks[n].closure = 0;
	    Fill_Callbacks (val, callbacks, n,
		Find_Callback_Converter (class, name, arg));
	    XtSetArg (to[i], res, callbacks);
	} else if (dt == T_Float) {
	    float f = (float)Get_Double (val);
	    to[i].name = res;
	    bcopy ((char *)&f, (char *)&to[i].value, sizeof f);
	} else if (dt == T_Backing_Store) {
	    XtSetArg (to[i], res, Symbols_To_Bits (val, 0,
		Backing_Store_Syms));
	} else if (dt == T_Orientation) {
	    XtSetArg (to[i], res, Symbols_To_Bits (val, 0, Orientation_Syms));
	} else if (dt == T_Justify) {
	    XtSetArg (to[i], res, Symbols_To_Bits (val, 0, Justify_Syms));
	} else if (dt == T_Translations) {
	    XtSetArg (to[i], res, Get_Translations (val));
	} else {
	    if (st != dt) {
		char msg[128];
		if (widget && (st == T_String || st == T_Symbol)) {
		    char *s;
		    XrmValue src, dst;

		    Make_C_String (val, s);
		    src.size = strlen (s);
		    src.addr = (caddr_t)s;
		    XtConvert (widget, (String)XtRString, &src,
			r[j].resource_type, &dst);
		    if (dst.addr) {
			XtSetArg (to[i], res, *(XtArgVal *)dst.addr);
			goto done;
		    }
		}
		sprintf (msg, "%s: can't convert %s ~s to %s", name,
		    Types[st].name, r[j].resource_type);
		Primitive_Error (msg, val);
	    }
	    if (dt == T_Boolean) {
		XtSetArg (to[i], res, EQ(val, True));
	    } else if (dt == T_Cursor) {
		XtSetArg (to[i], res, CURSOR(val)->cursor);
	    } else if (dt == T_Fixnum) {
		XtSetArg (to[i], res, FIXNUM(val));
	    } else if (dt == T_Display) {
		XtSetArg (to[i], res, DISPLAY(val)->dpy);
	    } else if (dt == T_Font) {
		Open_Font_Maybe (val);
		if (streq (r[j].resource_type, XtRFontStruct))
		    XtSetArg (to[i], res, FONT(val)->info);
		else
		    XtSetArg (to[i], res, FONT(val)->id);
	    } else if (dt == T_Pixel) {
		XtSetArg (to[i], res, PIXEL(val)->pix);
	    } else if (dt == T_Pixmap) {
		XtSetArg (to[i], res, PIXMAP(val)->pm);
	    } else if (dt == T_Character) {
		XtSetArg (to[i], res, CHAR(val));
	    } else if (dt == T_Widget) {
		XtSetArg (to[i], res, WIDGET(val)->widget);
	    } else if (dt == T_Window) {
		XtSetArg (to[i], res, WINDOW(val)->win);
	    } else Panic ("bad conversion type");
	} 
done: ;
    }
    XtFree ((char *)r);
}

Object Get_Values (w, ac, av) Widget w; Object *av; {
    register char *name;
    register i, j;
    Object arg;
    XtResource *r;
    int nr, nc;
    int t;
    ArgList argl;
    Object ret, tail;
    Display *dpy;
    char key[128];
    PFO converter;
    Widget w2;
    GC_Node2;

    argl = (Arg *)alloca (ac * sizeof (Arg));
    Get_All_Resources (w, XtClass (w), &r, &nr, &nc);
    /* Note:
     * `r' is not freed in case of error.
     */
    for (i = 0; i < ac; i++) {
	arg = av[i];
	Check_Type (arg, T_Symbol);
	Make_C_String (arg, name);
	Make_Resource_Name (name);
	for (j = 0; j < nr && !streq (name, r[j].resource_name); j++)
	    ;
	if (j == nr)
	    Primitive_Error ("no such resource: ~s", arg);
	argl[i].name = name;
	argl[i].value = (XtArgVal)alloca (r[j].resource_size);
    }
    XtGetValues (w, argl, (Cardinal)ac);
    ret = tail = P_Make_List (Make_Fixnum (ac), Null);
    GC_Link2 (ret, tail);
    /*
     * Display is needed for resources like cursor and pixmap.
     * XtDisplay(w) is not necessarily the right one!
     */
    dpy = XtDisplay (w);
    for (i = 0; i < ac; i++, tail = Cdr (tail)) {
	Object o;
	XtArgVal val = argl[i].value;
	for (j = 0; j < nr && !streq (argl[i].name, r[j].resource_name); j++)
	    ;
	t = Resource_To_Scheme_Type (r[j].resource_type);

	w2 = (j >= nr-nc) ? XtParent (w) : w;
	sprintf (key, "%s-%s", Class_Name (XtClass (w2)), argl[i].name);
	converter = Find_Converter_To_Scheme (key);

	if (converter) {
	    o = converter (*(XtArgVal **)val);
	} else if (t == T_String_Or_Symbol) {
	    char *s = *(char **)val;

	    if (s == 0) s = "";
	    o = Make_String (s, strlen (s));
	} else if (t == T_Callbacklist) {
	    register i, n;
	    Object ret, tail;
	    XtCallbackList callbacks = *(XtCallbackList *)val;
	    GC_Node;

	    for (n = 0; callbacks[n].callback; n++)
		;
	    ret = tail = P_Make_List (Make_Fixnum (n), Null);
	    GC_Link2 (ret, tail);
	    for (i = 0; i < n; i++, tail = Cdr (tail))
		Car (tail) = Get_Callbackfun (callbacks[i].closure);
	    GC_Unlink;
	    o = ret;
	} else if (t == T_Float) {
	    o = Make_Reduced_Flonum ((double)*(float *)val);
	} else if (t == T_Backing_Store) {
	    o = Bits_To_Symbols ((unsigned long)*(int *)val, 0,
		Backing_Store_Syms);
	    if (Nullp (o))
		Primitive_Error ("invalid backing-store (Xt bug)");
	} else if (t == T_Orientation) {
	    o = Bits_To_Symbols ((unsigned long)*(int *)val, 0,
		Orientation_Syms);
	} else if (t == T_Justify) {
	    o = Bits_To_Symbols ((unsigned long)*(int *)val, 0, Justify_Syms);
	} else if (t == T_Boolean) {
	    o = (Boolean)*(Boolean *)val ? True : False;
	} else if (t == T_Cursor) {
	    o = Make_Cursor (dpy, *(Cursor *)val);
	} else if (t == T_Fixnum) {
	    /*
	     * Assumption: Dimension and Position are short!
	     */
	    if (streq (r[j].resource_type, XtRInt))
		o = Make_Integer (*(int *)val);
	    else
		o = Make_Integer (*(short *)val);
	} else if (t == T_Display) {
	    o = Make_Display (0, dpy);
	} else if (t == T_Font) {
	    if (streq (r[j].resource_type, XtRFontStruct)) {
		o = Make_Font (dpy, False, (Font)0, *(XFontStruct **)val);
	    } else {
		XFontStruct *info;
		Disable_Interrupts;
		info = XQueryFont (dpy, *(Font *)val);
		Enable_Interrupts;
		o = Make_Font (dpy, False, *(Font *)val, info);
	    }
	} else if (t == T_Pixel) {
	    o = Make_Pixel (*(unsigned long *)val);
	} else if (t == T_Pixmap) {
	    o = Make_Pixmap (dpy, *(Pixmap *)val);
	} else if (t == T_Character) {
	    o = Make_Char (*(unsigned char *)val);
	} else if (t == T_Widget) {
	    o = Make_Widget (*(Widget *)val);
	} else if (t == T_Window) {
	    o = Make_Window (0, dpy, *(Window *)val);
	} else {
	    char s[128];
	    sprintf (s, "%s: no converter for %s", argl[i].name,
		r[j].resource_type);
	    Primitive_Error (s);
	}
	Car (tail) = o;
    }
    XtFree ((char *)r);
    GC_Unlink;
    return ret;
}

/* Convert `mapped-when-managed' to `mappedWhenManaged'.
 */
Make_Resource_Name (s) register char *s; {
    register char *p;

    for (p = s; *s; ) {
	if (*s == '-') {
	    if (*++s) {
		if (islower (*s))
		    *s = toupper (*s);
		*p++ = *s++;
	    }
	} else *p++ = *s++;
    }
    *p = '\0';
}

Object Get_Resources (c, fun, freeit) WidgetClass c; void (*fun)(); {
    XtResource *r;
    register XtResource *p;
    int nr;
    Object ret, tail, tail2, x;
    GC_Node3;
    
    fun (c, &r, &nr);
    /* Note:
     * `r' is not freed in case of error.
     */
    ret = tail = tail2 = P_Make_List (Make_Fixnum (nr), Null);
    GC_Link3 (ret, tail, tail2);
    for (p = r; p < r+nr; p++, tail = Cdr (tail)) {
	x = tail2 = P_Make_List (Make_Fixnum (3), Null);
	Car (tail) = tail2 = x;
	x = Intern (p->resource_name);
	Car (tail2) = x; tail2 = Cdr (tail2);
	x = Intern (p->resource_class);
	Car (tail2) = x; tail2 = Cdr (tail2);
	x = Intern (p->resource_type);
	Car (tail2) = x;
    }
    GC_Unlink;
    if (freeit) XtFree ((char *)r);
    return ret;
}

/* --------------------------------------------------------------------
 *
 * Delete this when XtGetConstraintResourceList() is provided by
 * the Xt intrinsics.
 *
 * This code has been written by Paul Asente <asente@wsl.dec.com>.
 *
 * Copyright 1985, 1986, 1987, 1988 by the Massachusetts Institute
 * of Technology
 * 
 * Permission to use, copy, modify, and distribute this
 * software and its documentation for any purpose and without
 * fee is hereby granted, provided that the above copyright
 * notice appear in all copies and that both that copyright
 * notice and this permission notice appear in supporting
 * documentation, and that the name of M.I.T. not be used in
 * advertising or publicity pertaining to distribution of the
 * software without specific, written prior permission.
 * M.I.T. makes no representations about the suitability of
 * this software for any purpose.  It is provided "as is"
 * without express or implied warranty.
 *
 */

#include <X11/Intrinsic.h>
#include <X11/IntrinsicP.h>
#include <X11/CoreP.h>

void XtGetConstraintResourceList(widget_class, resources, num_resources)
	WidgetClass widget_class;
	XtResourceList *resources;
	Cardinal *num_resources;
{
	if (_XtClassIsSubclass(widget_class, constraintWidgetClass)) {
	    ConstraintWidgetClass cwc = (ConstraintWidgetClass) widget_class;

	    GetResourceList(widget_class, resources, num_resources,
		    cwc->constraint_class.num_resources,
		    cwc->constraint_class.resources);
	} else {
	    *resources = NULL;
	    *num_resources = 0;
	}
}

static GetResourceList(widget_class, resources, num_resources, count, r_source)
	WidgetClass widget_class;
	XtResourceList *resources;
	Cardinal *num_resources;
	Cardinal count;
	XtResourceList r_source;
{
	int size = count * sizeof(XtResource);
	register int i, dest = 0;
	register XtResourceList dlist;
	register XtResourceList *source;

	*resources = (XtResourceList) XtMalloc((unsigned) size);

	if (!widget_class->core_class.class_inited) {
	    /* Easy case */

	    bcopy((char *) r_source, (char *) *resources, size);
	    *num_resources = count;
	    return;
	}

	/* Nope, it's the hard case */

	dlist = *resources;
	source = (XtResourceList *) r_source;
	for (i = 0; i < count; i++) {
	    if (source[i] != NULL) {
		dlist[dest].resource_name = (String)
			XrmQuarkToString((XrmQuark) source[i]->resource_name);
		dlist[dest].resource_class = (String) 
			XrmQuarkToString((XrmQuark) source[i]->resource_class);
		dlist[dest].resource_type = (String)
			XrmQuarkToString((XrmQuark) source[i]->resource_type);
		dlist[dest].resource_size = source[i]->resource_size;
		dlist[dest].resource_offset = -(source[i]->resource_offset + 1);
		dlist[dest].default_type = (String)
			XrmQuarkToString((XrmQuark) source[i]->default_type);
		dlist[dest].default_addr = source[i]->default_addr;
		dest++;
	    }
	}
	*num_resources = dest;
}
