/***********************************************************/
/* Copyright 1996 Daniel Dardailler.  
Permission to use, copy, modify, distribute, and sell this software
for any purpose is hereby granted without fee, 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 Daniel Dardailler not be used in advertising or
publicity pertaining to distribution of the software without specific,
written prior permission.  Daniel Dardailler makes no representations
about the suitability of this software for any purpose.  It is
provided "as is" without express or implied warranty.
************************************************************/
/***** Targets/Index stuff */
/* incomplete support: search for unsupported */

#include "DndP.h"

static Atom atom_motif_window, atom_target_list ;

static void
InitAtoms(Display * dpy) 
{
    if (atom_motif_window) return ; /* already Initialized */

    /* Init atoms used in the com */
    atom_motif_window = XInternAtom(dpy, "_MOTIF_DRAG_WINDOW", False);
    atom_target_list = XInternAtom(dpy, "_MOTIF_DRAG_TARGETS", False);
}


static Window 
MotifWindow(Display *display )
{
    Atom            type;
    int             format;
    unsigned long   size;
    unsigned long   bytes_after;
    Window         *property = NULL;
    Window	    motif_window ;

    /* this version does no caching, so it's slow: round trip each time */

    if ((XGetWindowProperty (display, DefaultRootWindow(display),
                             atom_motif_window,
                             0L, 100000L, False, AnyPropertyType,
                             &type, &format, &size, &bytes_after, 
			     (unsigned char **) &property) == Success) &&
         (type != None)) {
	motif_window = *property;
    } else {
	XSetWindowAttributes sAttributes;

	/* really, this should be done on a separate connection,
	   with XSetCloseDownMode (RetainPermanent), so that
	   others don't have to recreate it; hopefully, some real 
	   Motif application will be around to do it */

	sAttributes.override_redirect = True;
	sAttributes.event_mask = PropertyChangeMask;
	motif_window = XCreateWindow (display,
				     DefaultRootWindow (display),
				     -170, -560, 1, 1, 0, 0,
				     InputOnly, CopyFromParent,
				     (CWOverrideRedirect |CWEventMask),
				     &sAttributes);
	XMapWindow (display, motif_window);
    }

    if (property) {
	XFree ((char *)property);
    }

    return (motif_window);
}


static DndTargetsTable TargetsTable(Display *display)
{
    Atom            type;
    int             format;
    unsigned long   size;
    unsigned long   bytes_after;
    Window motif_window = MotifWindow(display) ;
    DndTargets * target_prop;
    DndTargetsTable targets_table ;
    int i,j ;
    char * target_data ;

    /* this version does no caching, so it's slow: round trip each time */
    /* ideally, register for property notify on this target_list
       atom and update when necessary only */

    if ((XGetWindowProperty (display, motif_window,	
			     atom_target_list, 0L, 100000L,	
			     False, atom_target_list,	
			     &type, &format,	&size, &bytes_after,
			     (unsigned char **) &target_prop) != Success) ||
           type == None) {
	printf("Get Targets cannot get property on motif_window\n");
	return NULL ;
    }

    if (target_prop->protocol_version != DND_PROTOCOL_VERSION) {
	printf("Get Targets warning: D&D protocol mismatch\n");
    }

    if (target_prop->byte_order != _DndByteOrder()) {
	/* need to swap num_target_lists and size */
	SWAP2BYTES(target_prop->num_target_lists);
	SWAP4BYTES(target_prop->data_size);
    }
    
    /* now parse DndTarget prop data in a TargetsTable */

    targets_table = (DndTargetsTable)malloc(sizeof(DndTargetsTableRec));
    targets_table->num_entries = target_prop->num_target_lists ;
    targets_table->entries = (DndTargetsTableEntry)
	malloc(sizeof(DndTargetsTableEntryRec) * target_prop->num_target_lists);

    target_data = (char*)target_prop + sizeof(*target_prop) ;

    for (i=0 ; i < targets_table->num_entries; i++) {
	CARD16 num_targets ;
	CARD32 atom ;

	memcpy(&num_targets, target_data, 2);
	target_data += 2;

	/* potential swap needed here */
	if (target_prop->byte_order != _DndByteOrder())
	    SWAP2BYTES(num_targets);

	targets_table->entries[i].num_targets = num_targets ;
	targets_table->entries[i].targets = (Atom *)
	    malloc(sizeof(Atom) * targets_table->entries[i].num_targets);


	for (j = 0; j < num_targets; j++) {
	    memcpy(&atom, target_data, 4 );
	    target_data += 4;

	    /* another potential swap needed here */
	    if (target_prop->byte_order != _DndByteOrder())
		SWAP4BYTES(atom);

	    targets_table->entries[i].targets[j] = (Atom) atom ;
	}
    }

    if (target_prop) {
        XFree((char *)target_prop);
    } 

    return targets_table ;
}

static int 
AtomCompare(void *atom1, void *atom2 )
{
    return (*((Atom *) atom1) - *((Atom *) atom2));
}

extern int
_DndTargetsToIndex(Display * display,
		   Atom * targets, int num_targets)
{
    int		i, j;
    Atom		*sorted_targets;
    DndTargetsTable	targets_table;
    int index = -1 ;

    InitAtoms(display) ;

    if (!(targets_table = TargetsTable (display))) {
	printf("_DndTargetsToIndex: cannot find the target table data\n");
        return -1 ;
    }

    /* sort the given target list */
    sorted_targets = (Atom *) malloc(sizeof(Atom) * num_targets);
    memcpy (sorted_targets, targets, sizeof(Atom) * num_targets);
    qsort ((void *)sorted_targets, (size_t)num_targets, (size_t)sizeof(Atom),
           AtomCompare);

    /* look for a match */

    for (i = 0; i < targets_table->num_entries; i++) {
	if (num_targets == targets_table->entries[i].num_targets) {
            for (j = 0; j < num_targets; j++) {
	        if (sorted_targets[j] != 
		    targets_table->entries[i].targets[j]) {
	            break;
		}
	    }
	    if (j == num_targets) {
	        index = i ;
		break ;
	    }
	}
    }
    
    XFree ((char *)sorted_targets);
    /* free the target table and its guts */
    for (i=0 ; i < targets_table->num_entries; i++) 
	XFree((char*)targets_table->entries[i].targets);
    XFree((char*)targets_table);

    if (index == -1 )
	printf("DndTargetsToIndex: non existing target list: unsupported\n");
        /* to support: need to grab the server, add our target list
	   to the property, return index = num_entries++, and ungrab */
    return index ;
}

int
_DndIndexToTargets(Display * display,
		   int index,
		   Atom ** targets)
{
    DndTargetsTable	targets_table;
    int i ;

    /* again, slow: no caching here, alloc/free each time */

    InitAtoms(display) ;

    printf("index %d\n", index);

    if (!(targets_table = TargetsTable (display)) ||
	(index >= targets_table->num_entries)) {
        return -1;
    }

    /* transfer the correct target list index */
    *targets = (Atom*)malloc(sizeof(Atom)*targets_table->
			    entries[index].num_targets);
    memcpy((char*)*targets,
	   (char*)targets_table->entries[index].targets,
	   sizeof(Atom)*targets_table->entries[index].num_targets);

    /* free the target table and its guts */
    for (i=0 ; i < targets_table->num_entries; i++) 
	XFree((char*)targets_table->entries[i].targets);
    XFree((char*)targets_table);

    return targets_table->entries[index].num_targets;
}
