/* make emcas happy -*-C-*-  make emacs happy */

#include <ctype.h>
#include <Xm/AtomMgr.h>

#define MAX_STRING_SEGMENT 4096
#define MAX_FORMAT_LEN 50

static XmStringCharSet defaultCharSet;

/*
#define defaultCharSet XmSTRING_DEFAULT_CHARSET
*/

#define	Done(type, value) \
	{							\
	    if (toVal->addr != NULL) {				\
		if (toVal->size < sizeof(type)) {		\
		    toVal->size = sizeof(type);			\
		    return False;				\
		}						\
		*(type*)(toVal->addr) = (value);		\
	    }							\
	    else {						\
		static type static_val;				\
		static_val = (value);				\
		toVal->addr = (XtPointer)&static_val;		\
	    }							\
	    toVal->size = sizeof(type);				\
	    return True;					\
	}


/*
 * Initialize the default character Set variable!
 */

void
wafeMotifInit()
    {
    XmString          dummy;
    XmStringContext   strContext;
    char              *text;
    XmStringCharSet   tmpCharSet;
    XmStringDirection dummyDir;
    Boolean           dummySep;
    dummy = XmStringCreateSimple("dummy");

    if (!XmStringInitContext(&strContext, dummy))
	wafeFatal("motif","Couldn't create string context",NULL);

    XmStringGetNextSegment(strContext, &text, &tmpCharSet,
			   &dummyDir, &dummySep);

    defaultCharSet = XtNewString(tmpCharSet);

    XtFree(text);
    XmStringFree(dummy);
    XmStringFreeContext(strContext);

/*
    fprintf(stderr, "Setting defaultCharSet to %s default <%s> (XmSTRING_DEFAULT_CHARSET)\n",
	    defaultCharSet, XmSTRING_DEFAULT_CHARSET);
*/
    /* wafeRegisterXpmTypeConverter(XmCPixmap, True);*/
    wafeRegisterXpmTypeConverter("XmBackgroundPixmap", True);
    }



/*
 * Utility function: Creates a new compound string segment, appends it
 * to an existing XmString. Does NOT create a segement if the string is empty!
 */

static XmString
compStringAppend(oldCompStr, text, charSet, dir, sep)
XmString           oldCompStr;
char              *text;
XmStringCharSet    charSet;
XmStringDirection  dir;
Boolean            sep;
    {
    XmString  addSeg, newString;

    if (*text == '\0' && sep)
	return(oldCompStr);

/*
    fprintf(stderr, "Generated segment with <%s>\n", text);
 */

    if (!oldCompStr)
	return(XmStringSegmentCreate(text, charSet, dir, sep));
    else
	{
	addSeg = XmStringSegmentCreate(text, charSet, dir, sep);
	newString = XmStringConcat(oldCompStr, addSeg);
	XmStringFree(addSeg);
	XmStringFree(oldCompStr);
	return(newString);
	}
    }

XmString
wafeCvtStringToXmString(inString)
char  *inString;
    {
    char               segBuf[MAX_STRING_SEGMENT];
    char               formatBuf[MAX_FORMAT_LEN];
    XmStringCharSet    charSet = XmSTRING_DEFAULT_CHARSET;
    XmStringDirection  dir = XmSTRING_DIRECTION_L_TO_R;
    XmString           result = NULL;
    char              *segPtr = segBuf;
    char              *ptr = inString;
    char              *top;

/*
    fprintf(stderr, "Converting string <%s>\n", inString);
*/
    top = ptr + strlen(inString);
    *segPtr = '\0';
    while (ptr < top)
	{
	switch (*ptr)
	    {
	    case '\n':
	    /* NewLine: Finish current segment */

	    *segPtr = '\0';
            result = compStringAppend(result, segBuf, charSet, dir, True);
	    segPtr = segBuf;
	    ptr++;
	    break;
	
	    case '^':
            /* Enter command mode */
	    ptr++;

	    if (ptr == top)
		{
		fprintf(stderr,
		    "Wafe(XmString Conversion): Cannot handle trailing '^'\n");
		return NULL;
		}

	    if (*ptr == '^')
		{
		/* Two ^ => leave command mode, insert one ^ */
		*segPtr++ = '^';
		ptr++;
		}
	    else
		if (!isalpha(*ptr))
		    {
                    /* ^ + whatever not alphabetic => ignore ^ */
		    *segPtr++ = *ptr++;
		    }
		else
		    {
		    char   *comPtr = formatBuf;
		
		    /* Terminate current segment */
		    *segPtr = '\0';
		    result = compStringAppend(result, segBuf,
					      charSet, dir, False);
		    segPtr = segBuf;
				
		    /* Extract command */
		    while ((isalnum(*ptr)
			    || (*ptr == '-')
			    || (*ptr == '_'))
			   && (ptr <= top))
			*comPtr++ = *ptr++;

		    *comPtr = '\0';
/*
		    fprintf(stderr, "Parsed command <%s>\n", formatBuf);
 */
		    if (!strcmp(formatBuf, "lr"))
			dir = XmSTRING_DIRECTION_L_TO_R;		
		    else
			if (!strcmp(formatBuf, "rl"))
			    dir = XmSTRING_DIRECTION_R_TO_L;
			else
			    charSet = XtNewString(formatBuf);

		    if (*ptr == ' ')
			ptr++;
		    }
	    break;
	    	
	    default:
	    *segPtr++ = *ptr++;
	    break;

	    } /* switch */	
	} /* while */

    *segPtr = '\0';
    result = compStringAppend(result, segBuf, charSet, dir, False);
    return result;
    }


XmStringTable
wafeCvtStringToXmStringTable(inString)
char * inString;
    {
    int argc;
    char ** argv;
    XmString *strTable, *t;
    int i;

    Tcl_SplitList(wafeInterpreter,inString, &argc,&argv);
    t = strTable = (XmString *)XtMalloc((argc+1)*sizeof(XmString));

    for ( i = 0; i < argc; i++, t++ )
	if (!(*t = wafeCvtStringToXmString(argv[i])))
	    {
	    wafeConvError("wafeCvtStringToXmStringTable","n",argv[i],"XmString");
	    return NULL;
	    }
    *t = NULL;

    return (XmStringTable) strTable;
    }


/* Queries a XmString Value and translates it to a valid input string */

char *
wafeCvtXmString2String(buffer,compStr)
char          * buffer;
XmString      compStr;
    {
    XmStringContext    strContext;
    char               *text;
    XmStringCharSet    charSet,
                       lastCharSet = defaultCharSet;
    XmStringDirection  dir,
                       lastDir = XmSTRING_DIRECTION_L_TO_R;
    Boolean            sep;
    Boolean            fontChange;
    char               *returnString, *ptr;

    DBUG_ENTER("wafeCvtXmString2String");

    if (!XmStringInitContext(&strContext, compStr))
	{
	fprintf(stderr, "Wafe(motif): Couldn't create string Context\n");
	return(NULL);
	}

    if (!buffer)
	{
	ptr = returnString = XtMalloc(XmStringLength(compStr)+100); /* hack  for now */
/*	fprintf(stderr, "buffer new allocated %p, size=%d\n",
		ptr,XmStringLength(compStr));
 */
	}
    else
	{
	fprintf(stderr, "using provided buffer %p\n", buffer);
	ptr = returnString = buffer;	
	}


    while (XmStringGetNextSegment(strContext, &text, &charSet, &dir, &sep))
	{
	char *textPtr = text;

	fontChange = strcmp(charSet, lastCharSet);

	/* This means: If there are no changes in charSet or direction,
	 * just make a newline but don't do it, if this is the first segment
	 */
	if (   (!fontChange)
	    && (dir == lastDir)
	    && (returnString != ptr))
	    *ptr++ = '\n';

	if (fontChange
#ifdef XmFONTLIST_DEFAULT_TAG
	    /* Motif 1.2 seems to want it that way ... */
	    && strcmp(charSet,XmFONTLIST_DEFAULT_TAG)
#endif
	    )
	    {
            *ptr++ = '^';
	    strcpy(ptr, charSet);
	    ptr+= strlen(charSet);
	    *ptr++ = ' ';
	    lastCharSet = charSet;
	    }

	if (dir != lastDir)
	    {
	    if (dir == XmSTRING_DIRECTION_L_TO_R)
		strcpy(ptr, "^lr ");
	    else
		strcpy(ptr, "^rl ");		
	    ptr+=4;
	    lastDir = dir;
	    }	

	while (*textPtr != '\0')
	    {
	    if (*textPtr == '^')
		{
		strcpy(ptr, "^^");
		ptr +=2;
		textPtr++;
		}
	    else
		*ptr++ = *textPtr++;
	    }
	}
    *ptr = '\0';
    XmStringFreeContext(strContext);
/*
    fprintf(stderr,"xmString2String returns <%s> size=%d\n",
	    returnString,strlen(returnString));
*/
    DBUG_RETURN(returnString);
    }

void
wafeEscapedXmString2String(buffer,compStr)
char          * buffer;
XmString      compStr;
    {
    char *p = wafeCvtXmString2String(NULL,compStr);
    wafeEscapedStrcpy(buffer,p);
    XtFree(p);
    }



char *
wafeCvtXmStringTable2String(count,strTable)
int           count;
XmStringTable strTable;
    {
    char    **temp, **cPtr;
    XmString  *ptr = strTable;
    char      *result;
    int        i;

    DBUG_ENTER("wafeCvtXmStringTable2String");

    temp = (char **)XtMalloc(count*sizeof(char *));
    for (ptr=strTable, cPtr=temp, i=count; i>0; ptr++, cPtr++, i--)
	{
	*cPtr = wafeCvtXmString2String(NULL,*ptr);
	}

    result = Tcl_Merge(count, temp);

    for (cPtr = temp, i=count; i>0; cPtr++, i--)
	XtFree(*cPtr);

    XtFree((char *)temp);
    DBUG_RETURN (result);
    }

/*
 * Two converter function wrappers for the *2String functions, which are
 * called by getValues
 */

Boolean
CvtXmStringToString(dpy, args, num_args, fromVal, toVal, converterData)
Display   *dpy;
XrmValue  *args;
Cardinal  *num_args;
XrmValue  *fromVal;
XrmValue  *toVal;
XtPointer *converterData;
    {
    static char *result;
    DBUG_ENTER("CvtXmStringToString");
    result = wafeCvtXmString2String(NULL,(XmString)(fromVal->addr));
    toVal->addr = result;
    toVal->size = 1; /* if nobody else uses this converter it shouldn't matter anyway */
    DBUG_RETURN(True);
    }


Boolean
CvtXmStringTableToString(dpy, args, num_args, fromVal, toVal, converter_data)
Display   *dpy;
XrmValue  *args;
Cardinal  *num_args;
XrmValue  *fromVal;
XrmValue  *toVal;
XtPointer *converter_data;
    {
    static char *result;
    DBUG_ENTER("CvtXmStringTableToString");
    result = wafeCvtXmStringTable2String(0,(XmStringTable)(fromVal->addr));

    toVal->addr = result;
    toVal->size = 1; /* it should not matter either */
    DBUG_RETURN(True);
    }


void
wafeFreeStringTable(table)
XmStringTable  table;
    {
    XmStringTable t = table;

    while (*t != NULL)
	XmStringFree(*t++);
    XtFree((char *) table);
    }	



Boolean
CvtStringToXmStrings(dpy, args, num_args, fromVal, toVal, converter_data)
Display   *dpy;
XrmValue  *args;
Cardinal  *num_args;
XrmValue  *fromVal;
XrmValue  *toVal;
XtPointer *converter_data;
    {
    int               argc, count;
    char            **argv;
    XmString         *ptr;
    static XmString  *result;

    DBUG_ENTER("CvtStringToXmStrings");

    if (Tcl_SplitList(wafeInterpreter,(char*)fromVal->addr, &argc, &argv)
	!= TCL_OK)
	{
	fprintf(stderr, "Wafe(XmStringTableConversion): %s\n",
		wafeInterpreter->result);
	return False;
	}


    result = (XmString *)XtMalloc(argc*sizeof(XmString));
    for(count = 0, ptr = result; count < argc; count++, ptr++)
	if ((*ptr = wafeCvtStringToXmString(argv[count],NULL,0)) == NULL)
	    {
	    XtDisplayStringConversionWarning(dpy, (char *)fromVal->addr,
					     "XmString");
	    XtFree((char *)result);
	    XtFree((char *)argv);
	    return False;
	    }
    XtFree((char *)argv);

    if (toVal->addr)
	{
	fprintf(stderr,"somebody else called CvtStringToXmStrings! size=%d\n",
		toVal->size);
	
	if (toVal->size < sizeof(XmString *))
	    {
	    toVal->size = sizeof(XmString *);
	    XtFree((char *)result);
	    DBUG_RETURN(False);
	    }

	toVal->size = sizeof(XmString *);
	*(XmString **)(toVal->addr) = result;
	DBUG_RETURN(True);
	}

	
    toVal->size = sizeof(XmString **);
    toVal->addr = (caddr_t)&result;

    wafeMMreplace(wafeCurrentAttribList, wafeCurrentAttrib,
	      (char *)result, wafeFreeStringTable);

    DBUG_RETURN(True);
    }


Boolean
CvtStringToXmString(dpy, args, num_args, fromVal, toVal,
                                converter_data)
Display   *dpy;
XrmValue  *args;
Cardinal  *num_args;
XrmValue  *fromVal;
XrmValue  *toVal;
XtPointer *converter_data;
    {
    static XmString  result;
    DBUG_ENTER("CvtStringToXmString");

/*    fprintf(stderr, "CvtStringToXmString: <%s> %p\n",
	    fromVal->addr, toVal->addr);
 */

    if ((result = wafeCvtStringToXmString((char *)fromVal->addr, NULL, 0)) == NULL)
	{
	XtDisplayStringConversionWarning(dpy, (char *)fromVal->addr,
					 "XmString");
	DBUG_RETURN(False);
	}

    if (toVal->addr)
	{
	if (toVal->size < sizeof(XmString))
	    {
	    toVal->size = sizeof(XmString);
	    XmStringFree(result);
	    DBUG_RETURN(False);
	    }

	toVal->size = sizeof(XmString);
	*(XmString *)(toVal->addr) = result;
	DBUG_RETURN(True);
	}

    toVal->size = sizeof(XmString *);
    toVal->addr = (XtPointer)result;

    wafeMMreplace(wafeCurrentAttribList, wafeCurrentAttrib,
	      (char *)result, (freeProc)XmStringFree);
    DBUG_RETURN(True);
    }


Boolean
CvtStringToLong(dpy, args, num_args, fromVal, toVal,
                                converter_data)
Display   *dpy;
XrmValue  *args;
Cardinal  *num_args;
XrmValue  *fromVal;
XrmValue  *toVal;
XtPointer *converter_data;
    {
    long result;

    if (sscanf((char *)fromVal->addr, "%ld", &result) == 1)
	{
	Done(long, result);
	}
    else
	return False;
    }

