#include <limits.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <time.h>
#include "rtr.h"

/*
Copyright Digital Equipment Corporation 1994-1995. All rights reserved.

Restricted Rights: Use, duplication, or disclosure by the U.S. Government
is subject to restrictions as set forth in subparagraph (c) (1) (ii) of
DFARS 252.227-7013, or in FAR 52.227-19, or in FAR 52.227-14 Alt. III, as
applicable.

This software is proprietary to and embodies the confidential technology
of Digital Equipment Corporation. Possession, use, or copying of this
software and media is authorized only pursuant to a valid written license
from Digital or an authorized sublicensor.
*/

/*
    File: rtrreq.c

On OSF/1
	
    compile and link using:

	cc -o rtrreq rtrreq.c -lrtr

On WINNT

    build using:

       nmake -f rtrreq.mak

Simple test on one node:

	- start rtr
	    > rtr start rtr
    	- create a facility
	    > rtr create facility /all_roles=<your node name>
	    > rtr create facility /all_roles=<your node name>
	- run the server
	    > rtrsrv
	- run this client
	    > rtrreq
*/

#define APITSTBUFSIZ 10000	

/* This program assumes that printf "%d" int format tokens can also be used
 * for time_t and 32 bit datatypes.
 */

/******************************************************************************/
static void print_rtr_status_data (
    const void		*rcvbuf
    )
{
    const rtr_status_data_t	*pstd ;

    pstd = rcvbuf ;
    printf ( "  status: %d (%s)\n",
		pstd->status,
		rtr_error_text ( pstd->status )
	    ) ;
    printf ( "  reason: %d\n",
		pstd->reason
	    ) ;
}

/******************************************************************************/
static void print_rtr_event_text ( rtr_evtnum_t	evtnum )
{
    switch ( evtnum )
    {
	case RTR_EVTNUM_FACREADY:
	    printf ( "facility ready" ) ;
	    break ;

	case RTR_EVTNUM_FACDEAD:
	    printf ( "facility not ready\n" ) ;
	    break ;

	case RTR_EVTNUM_FERTRGAIN:
	    printf ( "FE link to router\n" ) ;
	    break ;

	case RTR_EVTNUM_FERTRLOSS:
	    printf ( "FE lost link to router\n" ) ;
	    break ;

	case RTR_EVTNUM_RTRBEGAIN:
	    printf ( "router link to BE\n" ) ;
	    break ;

	case RTR_EVTNUM_RTRBELOSS:
	    printf ( "router lost link to BE\n" ) ;
	    break ;

	case RTR_EVTNUM_KEYRANGEGAIN:
	    printf ( "new key range available\n" ) ;
	    break ;

	case RTR_EVTNUM_KEYRANGELOSS:
	    printf ( "a key range not available\n" ) ;
	    break ;

	case RTR_EVTNUM_BERTRGAIN:
	    printf ( "BE link to a router\n" ) ;
	    break ;

	case RTR_EVTNUM_BERTRLOSS:
	    printf ( "BE lost link to a router\n" ) ;
	    break ;

	case RTR_EVTNUM_RTRFEGAIN:
	    printf ( "Router link to an FE\n" ) ;
	    break ;

	case RTR_EVTNUM_RTRFELOSS:
	    printf ( "Router lost link to an FE\n" ) ;
	    break ;

	case RTR_EVTNUM_SRPRIMARY:
	    printf ( "Server is primary mode\n" ) ;
	    break ;

	case RTR_EVTNUM_SRSTANDBY:
	    printf ( "Server is standby mode\n" ) ;
	    break ;

	case RTR_EVTNUM_SRSECONDARY:
	    printf ( "Server is secondary mode\n" ) ;
	    break ;

	case RTR_EVTNUM_SRSHADOWLOST:
	    printf ( "Server lost shadow\n" ) ;
	    break ;

	case RTR_EVTNUM_SRSHADOWGAIN:
	    printf ( "Server gained shadow\n" ) ;
	    break ;

	case RTR_EVTNUM_SRRECOVERCMPL:
	    printf ( "Server recovered\n" ) ;
	    break ;

	default:
	    printf ( "Unexpected RTR event %d\n", evtnum ) ;
    }
}

/******************************************************************************/
static void application_wakeup_routine (void)
{
    static int	nr_wakeup_calls = 0 ;

    nr_wakeup_calls++ ;
    printf ( "Wakeup called (%d)\n", nr_wakeup_calls ) ;
}

/******************************************************************************/
static void print_msgsb (
    const rtr_msgsb_t	*msgsb,
    const void		*rcvbuf
    )
{
    switch ( msgsb->msgtype )
    {
	case rtr_mt_msg1:
	    printf ( "rtr_mt_msg1 - first message of a transaction\n" ) ;
	    break ;
	case rtr_mt_msgn:
	    printf ( "rtr_mt_msgn - nth message (i.e. not the first) of a tx\n" ) ;
	    break ;
	case rtr_mt_msg1_uncertain:
	    printf ( "rtr_mt_msg1_uncertain - first message - might have been received\n" ) ;
	    break ;
	case rtr_mt_reply:
	    printf ( "rtr_mt_reply - message sent by a server to a client\n" ) ;
	    break ;
	case rtr_mt_prepare:
	    printf ( "rtr_mt_prepare - expects server to accept or reject\n" ) ;
	    break ;
	case rtr_mt_rtr_event:
	    printf ( "rtr_mt_rtr_event - received message is an RTR event\n" ) ;
	    print_rtr_event_text ( msgsb->evtnum ) ;
	    break ;
	case rtr_mt_user_event:
	    printf ( "rtr_mt_user_event - received message is a user event\n" ) ;
	    break ;
	case rtr_mt_accepted:
	    printf ( "rtr_mt_accepted - tx has been accepted by all participants\n" ) ;
	    print_rtr_status_data ( rcvbuf ) ;
	    break ;
	case rtr_mt_rejected:
	    printf ( "rtr_mt_rejected - specified tx was rejected by a participant\n" ) ;
	    print_rtr_status_data ( rcvbuf ) ;
	    break ;
	case rtr_mt_opened:
	    printf ( "rtr_mt_opened - channel opened\n" ) ;
	    print_rtr_status_data ( rcvbuf ) ;
	    break ;
	case rtr_mt_closed:
	    printf ( "rtr_mt_closed - channel closed\n" ) ;
	    print_rtr_status_data ( rcvbuf ) ;
	    break ;
	case rtr_mt_request_info:
	    printf ( "rtr_mt_request_info - message from rtr_request_info\n" ) ;
	    break ;
	case rtr_mt_set_info:
	    printf ( "rtr_mt_set_info - message from rtr_set_info\n" ) ;
	    break ;
	case rtr_mt_rettosend:
	    printf ( "rtr_mt_rettosend - message has been returned to sender\n" ) ;
	    break ;
	default:
	    printf ( "Unexpected msgtype %d\n", msgsb->msgtype ) ;
    }

    printf ( "usrhdl: %lu\n", (unsigned long)msgsb->usrhdl ) ;
    printf ( "msglen: %d\n", msgsb->msglen ) ;
    printf ( "tid:    %x,%x,%x,%x,%x,%x,%x\n",
    			msgsb->tid.tid32[0],
    			msgsb->tid.tid32[1],
    			msgsb->tid.tid32[2],
    			msgsb->tid.tid32[3],
    			msgsb->tid.tid32[4],
    			msgsb->tid.tid32[5],
    			msgsb->tid.tid32[6]
    	) ;
    printf ( "evtnum: %d\n", msgsb->evtnum ) ;
}

/******************************************************************************/
static void exit_if_error (
    const char		*msgtxt,
    rtr_status_t	sts
    )
{
    if ( sts > 0 )
    {
    	return ;
    }
    printf ( "%s unexpected error(%d)\n", msgtxt, sts);
    printf ( "%s\n", rtr_error_text (sts));
    rtr_set_wakeup ( NULL ) ;
    exit (EXIT_FAILURE);
}
/******************************************************************************/
static void continue_if_error (
    const char		*msgtxt,
    rtr_status_t	sts
    )
{
    if ( sts > 0 )
    {
	printf ( "%s didn't get expected error(%d)\n", msgtxt, sts);
	printf ( "%s\n", rtr_error_text (sts));
        rtr_set_wakeup ( NULL ) ;
	exit (EXIT_FAILURE);
    }
    else
    {
	printf ( "%s got expected error(%d)\n", msgtxt, sts);
	printf ( "%s\n", rtr_error_text (sts));
	return ;
    }
}

/******************************************************************************/
static void get_message_from_rtr (
    rtr_channel_t	*pchannel,
    void		*rcvbuf,
    rtr_msgsb_t		*msgsb
    )
{
    rtr_status_t sts ;

printf ( "------------------------------ rtr_receive_message %lu\n",  (unsigned long)time(0) ) ;
    sts = rtr_receive_message (
	    /* pchannel	*/ pchannel,
	    /* flags	*/ RTR_NO_FLAGS,
	    /* prcvchan	*/ RTR_ANYCHAN,
	    /* pmsg	*/ rcvbuf,
	    /* maxlen	*/ APITSTBUFSIZ,
	    /* timoutms	*/ RTR_NO_TIMOUTMS,
	    /* pmsgsb	*/ msgsb
	    ) ;
    exit_if_error ( "rtr_receive_message", sts ) ;
    printf ( "Got message on channel %d %lu\n", *pchannel, (unsigned long)time(0) ) ;
    print_msgsb ( msgsb, rcvbuf ) ;
}

/******************************************************************************/
int main ( int argc, char *argv[] )
{
    char          	szBuffer[128];
    /* ensure sufficient alignment using:
     *   union   or
     *   char * rcvbuf = malloc(APITSTBUFSIZ);
     */
    union {
	char		buf [ APITSTBUFSIZ ] ;
	rtr_status_data_t aligned_status_data;
    } rcvbuffer,
      *rcvbuf = &rcvbuffer;
    rtr_channel_t	reqchn ;
    rtr_channel_t	inch ;
    rtr_msgsb_t		msgsb ;
    rtr_status_t	sts;

    rtr_evtnum_t  pevtnum[] = {
                                  RTR_EVTNUM_USERDEF,
                                  RTR_EVTNUM_USERBASE,
                                  RTR_EVTNUM_UP_TO,
                                  RTR_EVTNUM_USERMAX,
                                  RTR_EVTNUM_RTRDEF,
                                  RTR_EVTNUM_RTRBASE,
                                  RTR_EVTNUM_UP_TO,
                                  RTR_EVTNUM_RTRMAX,
                                  RTR_EVTNUM_ENDLIST
                              };

printf ( "RTR V3.1D (150) Test Client\n" ) ;

        rtr_set_wakeup ( application_wakeup_routine ) ;
	/* remember to cancel rtr_set_wakeup before exit */

printf ( "------------------------------ client rtr_open_channel \n" ) ;

	sts = rtr_open_channel (
		/* pchannel	*/ &reqchn,
		/* flags	*/ RTR_F_OPE_CLIENT,
		/* facnam	*/ "RTR$DEFAULT_FACILITY",
		/* chanam	*/ "TEST_CHUNNEL",
		/* pevtnum	*/ pevtnum,
		/* access	*/ "ACCSTR",
		/* numseg	*/ 0,
		/* pkeyseg	*/ RTR_NO_PKEYSEG
    		) ;    		
	exit_if_error ( "rtr_open_channel", sts ) ;

	get_message_from_rtr (
	    /* rcvchn	*/ &inch,
	    /* rcvbuf	*/ rcvbuf,
	    /* msgsb	*/ &msgsb
	    ) ;	

printf ( "------------------------------ rtr_send_to_server \n" ) ;
	strcpy ( szBuffer, "message from client to server" );

	sts = rtr_send_to_server (
    		/* channel	*/ reqchn,
    		/* flags	*/ RTR_NO_FLAGS,
    		/* pmsg		*/ szBuffer,
    		/* msglen	*/ strlen (szBuffer) + 1,
    		/* msgfmt	*/ RTR_NO_MSGFMT
    		) ;
	exit_if_error ( "rtr_send_to_server", sts ) ;


printf ( "------------------------------ rtr_send_to_server again\n" ) ;
	strcpy ( szBuffer, "a second message" );

	sts = rtr_send_to_server (
    		/* channel	*/ reqchn,
    		/* flags	*/ RTR_NO_FLAGS,
    		/* pmsg		*/ szBuffer,
    		/* msglen	*/ strlen (szBuffer) + 1,
    		/* msgfmt	*/ RTR_NO_MSGFMT
    		) ;
	exit_if_error ( "rtr_send_to_server", sts ) ;


printf ( "------------------------------ rtr_accept_tx \n" ) ;
	sts = rtr_accept_tx (
		/* channel	*/ reqchn,
		/* flags	*/ RTR_NO_FLAGS,
		/* reason	*/ RTR_NO_REASON
		) ;
	exit_if_error ( "rtr_accept_tx", sts ) ;

printf ( "------------------------------ timeout waiting on wrong channel \n" ) ;
	{
	    rtr_channel_t	rcvchans[]={ UINT_MAX-1, RTR_CHAN_ENDLIST } ;
	    sts = rtr_receive_message (
		    /* pchannel	*/ &reqchn,
		    /* flags	*/ RTR_NO_FLAGS,
		    /* prcvchan	*/ rcvchans,
		    /* pmsg	*/ rcvbuf,
		    /* maxlen	*/ APITSTBUFSIZ,
		    /* timoutms	*/ 10000,
		    /* pmsgsb	*/ &msgsb
		    ) ;
	}
	continue_if_error ( "rtr_receive_message", sts ) ;

printf ( "------------------------------ client gets informed \n" ) ;
	get_message_from_rtr (
	    /* rcvchn	*/ &inch,
	    /* rcvbuf	*/ rcvbuf,
	    /* msgsb	*/ &msgsb
	    ) ;	

printf ( "------------------------------ rtr_broadcast_event \n" ) ;
	sts = rtr_broadcast_event (
    		/* channel	*/ reqchn,
    		/* flags	*/ RTR_NO_FLAGS,
    		/* pmsg		*/ szBuffer,
    		/* msglen	*/ strlen (szBuffer) + 1,
    		/* evtnum	*/ 42,
    		/* rcpnam	*/ "RECEIVER",
    		/* msgfmt	*/ RTR_NO_MSGFMT
    		) ;

printf ( "------------------------------ incorrect rtr_reject_tx \n" ) ;
	sts = rtr_reject_tx (
		/* channel	*/ reqchn,
		/* flags	*/ RTR_NO_FLAGS,
		/* reason	*/ RTR_NO_REASON
		) ;
	continue_if_error ( "rtr_reject_tx", sts ) ;

printf ( "------------------------------ client rtr_close_channel \n" ) ;
	sts = rtr_close_channel ( reqchn, RTR_NO_FLAGS );
	exit_if_error ( "rtr_close_channel", sts ) ;

    rtr_set_wakeup ( NULL ) ;
    exit( EXIT_SUCCESS );
}
