/* ================================================================================
   Two session server shows how to set up to have active patterns on, and
   receive from two ToolTalk sessions at once.  

   Usage is: two_session_server SessionID_1 SessionID_2
         (Where SessionID_1 and SessionID_2 are assumed to be valid session ids.)

   NOTE: If a SessionID is missing the leading "P " or "X " (denoting Process tree 
         session or X session respectively), then process tree is assumed.

   ================================================================================ */

#include <stdio.h>
#include <ctype.h>
#include <sys/param.h>
#include <sys/types.h>
#include <xview/xview.h>
#include <xview/panel.h>
#include <xview/termsw.h>
#include <string.h>

/* include the tooltalk API header file */
#include <desktop/tt_c.h>

/* include Debug support for notification of ToolTalk Errors. */
#include "tt_error_notify.h"


/* ================================================================================ */
/* Simple function to copy a session id.  If the src SessionID is missing the leading 
   "P " or "X " (denoting Process tree session or X session respectively), process 
   tree is assumed. (Note: User of this function is responsible for freeing the 
   storage allocated and assigned to *dest.)                                                                */

void copy_session_id(dest, src)
  char **dest; char *src;
{
 if ((strncmp("P ", src, 2) == 0) || (strncmp("X ", src, 2) == 0))  
    {*dest = malloc(strlen(src) + 1);   
     strcpy(*dest, src);
    }
 else 
    {*dest = malloc(strlen(src) + 3);   
     strcpy(*dest, "P ");
     strcat(*dest, src);
    }

}



/* ================================================================================ */
/* xview related globals and forward declarations                                   */

Xv_opaque base_frame, textout;  /* xview handles */
void create_ui_components();    /* xview - create user interface parts */
Notify_value destroy_func(Notify_client, Destroy_status);
void put_text(char*);           /* for putting chars to the display */
char text_buffer[1024];         /* for transferring chars to the display */

/* ================================================================================ */
/* ToolTalk related globals and forward declarations                                */

char *session1_id;               /* id for session 1 */
char *procid_s1;                 /* proc id for interaction with session 1 */
void receive_tt_message_s1();    /* callback for messages from session 1   */

char *session2_id;               /* id for session 2 */
char *procid_s2;                 /* proc id for interaction with session 2 */
void receive_tt_message_s2();    /* callback for messages from session 2   */



/* ================================================================================ */
/* Main                                                                             */

void main(argc, argv)
	int  argc;
	char **argv;
{
 int ttfd_s1;         /* file descriptor that activates when message session 1 arrives */
 int ttfd_s2;         /* file descriptor that activates when message session 2 arrives */
 Tt_pattern pat_s1;   /* pattern we're interested in from session 1 */
 Tt_pattern pat_s2;   /* pattern we're interested in from session 2 */

 xv_init(XV_INIT_ARGC_PTR_ARGV, &argc, argv, 0);       /* initialize XView */

 if (argc != 3)       /* check for two args - assume to be valid session ids */
   {printf("\nUsage: two_session_server SessionID_1 SessionID_2\n");
    exit (-1);
   }

 copy_session_id(&session1_id, argv[1]);               /* copy session ids */
 copy_session_id(&session2_id, argv[2]);

 create_ui_components();                               /* create interface components  */
 notify_interpose_destroy_func(base_frame, destroy_func); 

 TT_SERR_NOTIFY(tt_default_session_set(session1_id));    /* set session 1 as our default */

 /* Initialize ToolTalk, using current default session (session 1) Session 1 knows 
    us by this procid.  Note that at this point, this is also the default procid.      */
 TT_PERR_NOTIFY(procid_s1 = tt_open());  

 TT_IERR_NOTIFY(ttfd_s1 = tt_fd());  /* set fd that activates when session 1 message arrives */
 
 /* Tell XView to call receive_tt_message_s1 when fd for session 1 becomes active */
 notify_set_input_func(base_frame, (Notify_func)receive_tt_message_s1, ttfd_s1);

 /* Create and register a pattern for a message in session 1 that we're interested in */
 pat_s1 = tt_pattern_create();
 TT_SERR_NOTIFY(tt_pattern_category_set(pat_s1, TT_HANDLE));
 TT_SERR_NOTIFY(tt_pattern_class_add(pat_s1, TT_REQUEST));
 TT_SERR_NOTIFY(tt_pattern_scope_add(pat_s1, TT_SESSION));
 TT_SERR_NOTIFY(tt_pattern_op_add(pat_s1, "UpperCase"));
 TT_SERR_NOTIFY(tt_pattern_arg_add(pat_s1, TT_INOUT, "ALL", NULL));
 TT_SERR_NOTIFY(tt_pattern_register(pat_s1));
 TT_SERR_NOTIFY(tt_session_join(session1_id)); 


 /* At this point we're set up to receive from the session 1 side, now
    let's set up to receive from session 2 */

 TT_SERR_NOTIFY(tt_default_session_set(session2_id));      /* set session 2 as our default */

 /* Initialize ToolTalk, using current default session (session 2) Session 2 knows us 
    by this procid.  Note that at this point, this becomes the default procid.      */
 TT_PERR_NOTIFY(procid_s2 = tt_open());  

 TT_IERR_NOTIFY(ttfd_s2 = tt_fd());   /* set fd that will activates when message arrives from session 2 */

 /* Tell XView to call receive_tt_message_s2 when fd for session 2 becomes active */
 notify_set_input_func(base_frame, (Notify_func)receive_tt_message_s2, ttfd_s2);

 /* Create and register a pattern for a message in session 2 that we're interested in */
 pat_s2 = tt_pattern_create();
 TT_SERR_NOTIFY(tt_pattern_category_set(pat_s2, TT_HANDLE));
 TT_SERR_NOTIFY(tt_pattern_class_add(pat_s2, TT_REQUEST));
 TT_SERR_NOTIFY(tt_pattern_scope_add(pat_s2, TT_SESSION));
 TT_SERR_NOTIFY(tt_pattern_op_add(pat_s2, "UpperCase"));
 TT_SERR_NOTIFY(tt_pattern_arg_add(pat_s2, TT_INOUT, "ALL", NULL));
 /*tt_pattern_session_add(pat_s2, session2_id);*/
 TT_SERR_NOTIFY(tt_pattern_register(pat_s2));
 TT_SERR_NOTIFY(tt_session_join(session2_id));

 /* At this point we're set up to receive from both sessions. */

 xv_main_loop(base_frame);               /* enter XView notifier     */

 /* cleanup Tooltalk for session 1 */
 TT_SERR_NOTIFY(tt_default_procid_set(procid_s1));  /* set default proc id to be for session 1 */
 TT_SERR_NOTIFY(tt_close());                        /* close up operations */

 /* cleanup Tooltalk for session 2 */
 TT_SERR_NOTIFY(tt_default_procid_set(procid_s2));  /* set default proc id to be for session 2 */
 TT_SERR_NOTIFY(tt_close());                        /* close up operations */

 exit(0);
}


/* ================================================================================ */
/* When a ToolTalk message is available from session 1, receive it, and display it. */

void receive_tt_message_s1()
{
 Tt_message msg_in;              /* holds handle to message  */
 int mark;                       /* holds storage mark       */
 char *opname;                   /* points to operation name */
 char *string_base, *string_ptr;  /* used to point to message arguments */

 TT_SERR_NOTIFY(tt_default_procid_set(procid_s1));  /* set default proc id to session 1 */

 TT_PERR_NOTIFY(msg_in = tt_message_receive());      /* get a handle to the message */

 /* It's possible that the file descriptor would become active 
    even though ToolTalk doesn't really have a message for us.
    The returned message handle is NULL in this case. */

 if (msg_in == NULL) return;

  mark = tt_mark(); /* Get storage mark so we can free all data ToolTalk returns to us */

 TT_PERR_NOTIFY(opname = tt_message_op(msg_in));     /* get the operation name */

 if (opname && (0 == strcmp("UpperCase", opname)))   /* check for UpperCase operation */
	{string_base = string_ptr = TT_PERR_NOTIFY(tt_message_arg_val(msg_in, 0));
	 sprintf(text_buffer, "--------------------\nFor session 1, starting UpperCase of: %s\n", string_ptr);
	 put_text(text_buffer);
         while (*string_ptr != '\0') *string_ptr++ = toupper(*string_ptr);
         TT_SERR_NOTIFY(tt_message_arg_val_set(msg_in, 0, string_base));
         sprintf(text_buffer, "Replying to session 1 with %s\n", string_base);
	 put_text(text_buffer);
         TT_SERR_NOTIFY(tt_message_reply(msg_in));
         }
         /* we should never get here since we didn't register a pattern for anything else, but... */
 else {sprintf(text_buffer, "!!! Unknown message %s received in session %s\n", opname, session1_id);
        put_text(text_buffer);
	/* reject requests we don't handle (note: we should never get a notice with status = 
           TT_WRN_START_MESSAGE, since we never get started from a static pattern, but be complete. */
	if (tt_message_class(msg_in) == TT_REQUEST || 
            tt_message_status(msg_in) == TT_WRN_START_MESSAGE) 
          TT_SERR_NOTIFY(tt_message_reject(msg_in));
      }

 TT_PERR_NOTIFY(tt_message_destroy(msg_in));        /* we're all done with the message */

 tt_release(mark);                                /* free any Tooltalk data */

 return;
}
	

/* ================================================================================ */
/* When a ToolTalk message is available from session 2, receive it, and display it. */

void receive_tt_message_s2()
{
 Tt_message msg_in;              /* holds handle to message  */
 int mark;                       /* holds storage mark       */
 char *opname;                   /* points to operation name */
 char *string_base, *string_ptr;  /* used to point to message arguments */

 TT_SERR_NOTIFY(tt_default_procid_set(procid_s2));  /* set default proc id to session 2 */

 TT_PERR_NOTIFY(msg_in = tt_message_receive());      /* get a handle to the message */

 /* It's possible that the file descriptor would become active 
    even though ToolTalk doesn't really have a message for us.
    The returned message handle is NULL in this case. */

 if (msg_in == NULL) return;

  mark = tt_mark(); /* Get storage mark so we can free all data ToolTalk returns to us */

 TT_PERR_NOTIFY(opname = tt_message_op(msg_in));     /* get the operation name */

 if (opname && (0 == strcmp("UpperCase", opname)))   /* check for UpperCase operation */
	{string_base = string_ptr = TT_PERR_NOTIFY(tt_message_arg_val(msg_in, 0));
	 sprintf(text_buffer, "--------------------\nFor session 2, starting UpperCase of: %s\n", string_ptr);
	 put_text(text_buffer);
         while (*string_ptr != '\0') *string_ptr++ = toupper(*string_ptr);
         TT_SERR_NOTIFY(tt_message_arg_val_set(msg_in, 0, string_base));
         sprintf(text_buffer, "Replying to session 2 with %s\n", string_base);
	 put_text(text_buffer);
         TT_SERR_NOTIFY(tt_message_reply(msg_in));
        }
         /* we should never get here since we didn't register a pattern for anything else, but... */
  else {sprintf(text_buffer, "!!! Unknown message %s received in session %s\n", opname, session2_id);
	put_text(text_buffer);
 	/* reject requests we don't handle (note: we should never get a notice with status = 
           TT_WRN_START_MESSAGE, since we never get started from a static pattern, but be complete. */
	if (tt_message_class(msg_in) == TT_REQUEST || 
	    tt_message_status(msg_in) == TT_WRN_START_MESSAGE) 
	  TT_SERR_NOTIFY(tt_message_reject(msg_in));
        }

 TT_PERR_NOTIFY(tt_message_destroy(msg_in));        /* we're all done with the message */

 tt_release(mark);                                /* free any Tooltalk data */

 return;
}
	


/* ================================================================================ */    
/* XView calls for creating the ui elements.        No ToolTalk-specific code here. */

void create_ui_components()
{
	base_frame = xv_create(NULL, FRAME,
		XV_WIDTH, 387,
		XV_HEIGHT, 226,
		XV_LABEL, "Two Session Server",
		XV_SHOW, FALSE,
		FRAME_SHOW_FOOTER, TRUE,
		FRAME_SHOW_RESIZE_CORNER, TRUE,
		NULL);
	textout = xv_create(base_frame, TEXTSW,
		XV_X, 0,
		XV_Y, 0,
		XV_WIDTH, WIN_EXTEND_TO_EDGE,
		XV_HEIGHT, WIN_EXTEND_TO_EDGE,
		OPENWIN_SHOW_BORDERS, TRUE,
		TEXTSW_BROWSING, TRUE,
		TEXTSW_DISABLE_LOAD, TRUE,
		TEXTSW_MEMORY_MAXIMUM, 0,
                TEXTSW_CHECKPOINT_FREQUENCY, 0,
	        NULL);
}


/*  used to put massages to the display */
void put_text(char* buffer)
{textsw_insert(textout, buffer, strlen(buffer));
}

/* when destroying window */
Notify_value
destroy_func(Notify_client client, Destroy_status status)
{
	switch(status)
	{
	case	DESTROY_CHECKING:
		break;
	case	DESTROY_CLEANUP:
		textsw_reset(textout, 0, 0);
		return(notify_next_destroy_func(client, status));
		break;
	case	DESTROY_SAVE_YOURSELF:
		break;
	case	DESTROY_PROCESS_DEATH:
		break;
	default:
		printf("received %X\n", status);
	}
	return (NOTIFY_DONE);
}
