/* $XConsortium: send.c /main/5 1995/07/15 20:38:50 drk $ */
/*
 * Motif
 *
 * Copyright (c) 1987-2012, The Open Group. All rights reserved.
 *
 * These libraries and programs are free software; you can
 * redistribute them and/or modify them under the terms of the GNU
 * Lesser General Public License as published by the Free Software
 * Foundation; either version 2 of the License, or (at your option)
 * any later version.
 *
 * These libraries and programs are distributed in the hope that
 * they will be useful, but WITHOUT ANY WARRANTY; without even the
 * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
 * PURPOSE. See the GNU Lesser General Public License for more
 * details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with these librararies and programs; if not, write
 * to the Free Software Foundation, Inc., 51 Franklin Street, Fifth
 * Floor, Boston, MA 02110-1301 USA
 * 
 */
/*
 * HISTORY
 */

#include <Xm/Transfer.h>
#include "wsm_proto.h"
#include "utm_send.h"

typedef struct _RequestInfo {
    WSMReplyCallbackFunc reply_callback; /* The reply callback func */
    XtPointer reply_data;	/* The user data for the callback func */
    WSMRequestType request_type; /* The kind of request for cross check. */
    Atom send_atom;		/* The atom that we sent this message to. */
} RequestInfo;

static void UTMReplyReceived(
Widget, XtPointer, XtPointer
);

static void ReplyReceived(
Widget, XtPointer, Atom *, Atom *, XtPointer, unsigned long *, int *
);

extern Time GetTimestamp (Display *dpy);

/*	Function Name: WSMSendMessage
 *	Description: Sends a message to WSM or WM on the screen
 *                   and display specified.
 *	Arguments: w - any widget on this display and screen that
 *                     also has destination callback.
 *                     Note - This widget MUST have the destination
 *                            callback set to UTMDestinationProc that
 *                            is defined in utm_send.c.
 *                 send_to - either WSMWorkspaceManager or WSMWindowManager
 *                 request - the request to send.
 *                 reply_callback - The routine to call when the
 *                                  reply comes in.
 *                 reply_data - Client data passed to the reply_callback.
 *	Returns: True if an attempt is made to retrieve the selection,
 *               False if an obvious error occured.
 * 
 * NOTE: Reply callback will be called with reply == NULL if unable
 *       to convert request.
 */

Boolean
WSMSendMessage(Widget w, WSMClientType send_to, WSMRequest *request, 
	       WSMReplyCallbackFunc reply_callback, XtPointer reply_data)
{
    int screen_num = XScreenNumberOfScreen(XtScreen(w));
    Display *dpy = XtDisplay(w);
    Atom send_atom = _WSMGetSelectionAtom(dpy, screen_num, send_to);
    MessageData msg_data;
    unsigned long msg_len;
    RequestInfo *req_info;
    WSMErrorCode error;
    Time time = GetTimestamp(dpy);

    if (send_atom == None) {
	fprintf(stderr, "%s: Could not get selection atom to send message\n",
		"Internal Error");
	return(False);
    }

    /*
     * Package human-readable request data into protocol package.
     */
    msg_data = _WSMPackRequest(dpy, screen_num, request, &msg_len, &error);
    if (msg_data == NULL) {
	(*reply_callback)(w, reply_data, NULL, error);
	return(False);
    }

    req_info = (RequestInfo *) XtMalloc(sizeof(RequestInfo));
    req_info->reply_callback = reply_callback;	/* The reply callback func */
    req_info->reply_data = reply_data;	/* user data for the callback func */
    req_info->request_type = request->any.type;/*request kind for cross check*/
    req_info->send_atom = send_atom;	/* Atom we sent this message to. */

    if (!XtIsRealized(w)) {
	fprintf(stderr, "%s WSMSendMessage must be realized, and is not.\n",
		"Programmer Error: Widget passed to");
	return(False);
    }

    UTMSendMessage(w,
		   send_atom,
		   _WSMReqTypeToTarget(dpy, request->any.type),
		   (XtPointer) msg_data, msg_len, WSM_PROTO_FMT,
		   UTMReplyReceived, req_info,
		   time);
    return(True);
}


/************************************************************
 *
 *  Internal Routines.
 *
 ************************************************************/


/*	Function Name: UTMReplyReceived
 *	Description: Called after the selection owner's convert proc
 *                   has finished.
 *	Arguments: w - The widget who initiated the request.
 *                 clientData - 
 *                 callData - 
 *	Returns: none
 */
static void
UTMReplyReceived(Widget w, XtPointer clientData, XtPointer callData)
{
  XmSelectionCallbackStruct *scs = (XmSelectionCallbackStruct *) callData;
  RequestInfo *req_info = (RequestInfo *) clientData;
  Boolean errorFound = False;
  Atom type = _WSMReqTypeToTarget(XtDisplay(w), req_info->request_type);

  /* Let's check some other values just to make sure things are ok. */
  if (scs->reason != XmCR_OK) {
    fprintf(stderr, "ERROR: Bad reason value received in UTMReplyReceived.\n");
    errorFound = True;
  }
  if (scs->type == XT_CONVERT_FAIL) {
    fprintf(stderr, "ERROR: Convert failure detected in UTMReplyReceived.\n");
    errorFound = True;
  }
  if (scs->flags != XmSELECTION_DEFAULT) {
    fprintf(stderr, "ERROR: Bad flags value received in UTMReplyReceived.\n");
    errorFound = True;
  }
  if (errorFound)
    return;

  ReplyReceived(w,
		req_info, /* the request info pointer to fill in. */
		&scs->selection,
		&type,  /* type of request that was made. */
		scs->value, /* data returned from conversion. */
		&scs->length,
		&scs->format);
}


/*	Function Name: ReplyReceived
 *	Description: Called when a reply is received from a request
 *                   initiated by a WSMSendMessage.
 *	Arguments: w - The widget who initiated the request.
 *                 req_info_ptr - pointer to the request info.
 *                 selection - The selection that has been converted.
 *                 req_type_atom - The type of request that was made.
 *                 length - the amount of message data.
 *                 format - the format of the reply.
 *	Returns: none
 */
static void
ReplyReceived(Widget w, XtPointer req_info_ptr,
	      Atom *selection, Atom *req_type_atom,
	      XtPointer value, unsigned long *length, int *format)
{
    RequestInfo *req_info = (RequestInfo *) req_info_ptr;
    WSMReply reply;
    Display *dpy = XtDisplay(w);
    int screen_num;
    WSMErrorCode fail_code = WSM_SUCCESS;

    /*
     * First a few checks to make sure everything is as we expect.
     */

    if (*selection != req_info->send_atom) {
	fprintf(stderr, "%s, request %d - reply %d\n",
	       "Selection of the reply is not the same as the request",
	       (int) req_info->send_atom, (int) *selection);

	    fail_code = WSM_ERROR_INTERNAL;
    }
    if (*req_type_atom != _WSMReqTypeToTarget(dpy, req_info->request_type)) {
	if (*req_type_atom == None) {
	    if (XGetSelectionOwner(dpy, *selection) == None)
	      {
		fail_code = WSM_ERROR_NO_SEL_OWNER;
		fprintf(stderr, "No owner for selection #%d\n", (int)*selection);
	      }
	    else
		fail_code = WSM_ERROR_CONVERSION_FAILED;
	}
	else if (*req_type_atom == XT_CONVERT_FAIL)
	    fail_code = WSM_ERROR_TIMEOUT;
	else {
	    fprintf(stderr, "%s, request %s - reply %d:%s\n",
		"Target of the reply is not the same as the request",
		_WSMReqTypeToName(req_info->request_type),
		(int) *req_type_atom,
		_WSMReqTypeToName(_WSMTargetToReqType(dpy, *req_type_atom)));

	    fail_code = WSM_ERROR_INTERNAL;
	}
    }
    if (*format != WSM_PROTO_FMT) {
	fprintf(stderr, "%s, request %d - reply %d\n",
	       "Format of the reply is not the same as the request",
	       (int) WSM_PROTO_FMT, (int) *format);

	fail_code = WSM_ERROR_INTERNAL;
    }

    if (fail_code != WSM_SUCCESS) {
	/*
	 * Failure, call callback with NULL reply.
	 */

	(*req_info->reply_callback)(w, req_info->reply_data, NULL, fail_code);
    }
    else {
	screen_num = XScreenNumberOfScreen(XtScreen(w));
	
	_WSMUnpackReply(dpy, screen_num, value, *length,
			  req_info->request_type, &reply);
	
	(*req_info->reply_callback)(w, req_info->reply_data, &reply,fail_code);
	
	FreeReply(&reply);
    }

    XtFree((XtPointer) req_info_ptr);
}
