/******************************************
 *
 * Legal boiler plate goes here
 *
 ******************************************
 *
 *  Portal 3.0 reference library test routines
 * Tests performed:
 *   - Hop count execeding
 *   - MLE unlinking
 *   - MD offset management
 *   - MD overflow
 * In order for these to pass, the following must also work:
 *   - MLE inserting, matching, etc
 *   - MD decoding
 *   - Event queue recording
 */

#include 
#include 
#include 

#include 
#include 

static int do_master( ptl_obj_t *tbl );
static int do_slave( ptl_obj_t *tbl );

int main( int argc, char *argv[] )
{
	ptl_obj_t *obj = ptl_init( ipnal_init, NULL, 8, 8 );
	if( !obj )
		return EXIT_FAILURE;

	switch( obj->mypid ) {
	  case 0 :
		do_master( obj );
		break;
	  case 1 :
		do_slave( obj );
		break;
	  default :
		fprintf( stderr, "%3d: Not needed, exiting\n", obj->mypid );
		break;
	}

	printf( "%3d: Exiting\n", obj->mypid );
	ptl_fini( obj );
	return 0;
}


#define MAX_EVS	16
ptl_ev_t ev_store[MAX_EVS];
char out_buf[1024];

int do_master( ptl_obj_t *tbl )
{
	int		i;
	ptl_eq_t	eq;
	ptl_md_t	md;

	ipnal_debug = 0;

	eq = ptl_eq_create( tbl, ev_store, MAX_EVS );
	if( eq < 0 ) {
		fprintf( stderr, "master: eq create failed\n" );
		return -1;
	}

	md = ptl_md_create( tbl,
		out_buf, 17,		/* Buffer and extent */
		0,			/* Threshold */
		ptl_mdopt_put,		/* Options */
		eq );
	if( md < 0 ) {
		fprintf( stderr, "master: md create failed\n" );
		return -1;
	}

	printf( "master: Waiting\n" );
	sleep(1);


	/*
	 *  The first put should go into md_unlink, which will then
	 * be unlinked.  The second two should go into md_local, which
	 * will truncate the last put due to an overflow
	 */
	for( i=0 ; i<3 ; i++ ) {
		sprintf( out_buf, "%d Master put %d\n", i+1, i+1 );
		if( ptl_md_put( tbl,
			md,			/* Memory descriptor */
			1,			/* Destination pid */
			3,			/* Destination portal */
			0xDEADBEEF,		/* Match bits */
			11			/* Remote offset */
		) < 0 ) {
			fprintf( stderr, "master: md_put %d failed\n", i );
			return -1;
		}
	}

	if( ptl_md_put( tbl, md, 1, 3, 0xBADBEEF, 4 ) < 0 ) {
		fprintf( stderr, "master: md_put 3 failed\n" );
		return -1;
	}


	sleep(10);
	return 0;
}


int do_slave( ptl_obj_t *tbl )
{
	ptl_ev_t	*ev;
	ptl_eq_t	eq;
	ptl_md_t	md_unlink, md_local, md_remote;
	ptl_mle_t	mle_unlink, mle_local, mle_remote;
	static char	unlink_buf[128],
			local_buf[128],
			remote_buf[128];

	ipnal_debug = 0;
	eq = ptl_eq_create( tbl, ev_store, MAX_EVS );
	if( eq < 0 ) {
		fprintf( stderr, "slave: eq create failed\n" );
		return -1;
	}

	md_unlink = ptl_md_create( tbl,
		unlink_buf, 30, -1,	/* Buffer, extent, threshold */
		ptl_mdopt_put |
		ptl_mdopt_remote |
		ptl_mdopt_trunc |
		ptl_mdopt_ack,
		eq );
	if( md_unlink < 0 ) {
		fprintf( stderr, "slave: md_unlink create failed\n" );
		return -1;
	}
		
	md_local = ptl_md_create( tbl,
		local_buf, 30, -1,	/* Buffer, extent, threshold */
		ptl_mdopt_put |
		ptl_mdopt_local |
		ptl_mdopt_trunc,
		eq );
	if( md_local < 0 ) {
		fprintf( stderr, "slave: md_local failed\n" );
		return -1;
	}

	md_remote = ptl_md_create( tbl,
		remote_buf, 30, -1,	/* Buffer, extent, threshold */
		ptl_mdopt_put |
		ptl_mdopt_remote |
		ptl_mdopt_trunc,
		eq );
	if( md_remote < 0 ) {
		fprintf( stderr, "slave: md_remote failed\n" );
		return -1;
	}

	mle_unlink = ptl_mle_create( tbl,
		3,			/* Portal table entry */
		0,			/* Only from master */
		0xDEADBEEF,		/* Match bits */
		0,			/* Ignore bits */
		md_unlink,
		1 );			/* Unlink */
	if( mle_unlink < 0 ) {
		fprintf( stderr, "slave: mle_unlink failed\n" );
		return -1;
	}

	mle_local = ptl_mle_insert_after( tbl,
		mle_unlink,		/* Previous MLE */
		0,			/* PID */
		0xDEADBEEF,		/* Matchbits */
		0,			/* Ignore bits */
		md_local,		/* Same MD */
		0 );			/* Unlink no */
	if( mle_local < 0 ) {
		fprintf( stderr, "slave: mle_local failed\n" );
		return -1;
	}

	mle_remote = ptl_mle_insert_after( tbl,
		mle_local,		/* Previous MLE */
		0,			/* PID */
		0xBADBEEF,		/* Matchbits */
		0,			/* Ignore bits */
		md_remote,		/* Remotely managed MD */
		0 );			/* No unlink */
	if( mle_remote < 0 ) {
		fprintf( stderr, "slave: mle_remote failed\n" );
		return -1;
	}

	printf( "slave: Waiting...\n" );
	sleep(1);

	printf( "slave: First event: deposit into md %d offset 11: ",
			md_unlink );
	if( wait_for( tbl, eq, 0 ) < 0 ) return -1;

	ev = &ev_store[0];
	if(	ev->type != ptl_msg_put
	||	ev->pid != 0
	||	ev->ptl_index != 3
	||	ev->match_bits != 0xDEADBEEF
	||	ev->rlength != 17
	||	ev->mlength != 17
	||	ev->offset != 11
	||	ev->md !=  md_unlink
	) {
		printf( "FAILED\n" );
		dump_ev( ev );
	} else
		printf( "OK\n" );




	printf( "slave: Second event: depsosit into md %d: ", md_local );
	if( wait_for( tbl, eq, 1 ) < 0 ) return -1;
	ev = &ev_store[1];
	if(	ev->type != ptl_msg_put
	||	ev->pid != 0
	||	ev->ptl_index != 3
	||	ev->match_bits != 0xDEADBEEF
	||	ev->rlength != 17
	||	ev->mlength != 17
	||	ev->offset != 0
	||	ev->md !=  md_local
	) {
		printf( "FAILED:\n" );
		dump_ev( ev );
	} else
		printf( "OK\n" );


	printf( "slave: Third event: depsosit into md %d (trunc): ",
		md_local );

	if( wait_for( tbl, eq, 2 ) < 0 ) return -1;
	ev = &ev_store[2];
	if(	ev->type != ptl_msg_put
	||	ev->pid != 0
	||	ev->ptl_index != 3
	||	ev->match_bits != 0xDEADBEEF
	||	ev->rlength != 17
	||	ev->mlength != 13
	||	ev->offset != 17
	||	ev->md !=  md_local
	) {
		printf( "FAILED:\n" );
		dump_ev( ev );
	} else 
		printf( "OK\n" );


	printf( "slave: Forth event: deposit into md %d offset 4: ",
		md_remote );
	if( wait_for( tbl, eq, 3 ) < 0 ) return -1;
	ev = &ev_store[3];
	if(	ev->type != ptl_msg_put
	||	ev->pid != 0
	||	ev->ptl_index != 3
	||	ev->match_bits != 0xBADBEEF
	||	ev->rlength != 17
	||	ev->mlength != 17
	||	ev->offset != 4
	||	ev->md !=  md_remote
	) {
		printf( "FAILED:\n" );
		dump_ev( ev );
	} else
		printf( "OK\n" );
	
	return 0;
}




int dump_ev( ptl_ev_t *ev )
{
	printf( "ev: type=%s from %d into ptl %d with mbs=%lx.  md %d used\n"
		"\tlength %ld / %ld at offset %ld into %p\n",
		ev->type == ptl_msg_ack ? "ack" :
		ev->type == ptl_msg_put ? "put" :
		ev->type == ptl_msg_get ? "get" :
		ev->type == ptl_msg_reply ? "reply" : "???",
		ev->pid,
		ev->ptl_index,
		(unsigned long) ev->match_bits,
		ev->md,
		(long) ev->mlength, (long) ev->rlength, (long) ev->offset,
		ev->addr );
	return 0;
}

int wait_for( ptl_obj_t *tbl, ptl_eq_t eq, int event )
{
	int count = 30;

	fflush( stdout );
	while( ptl_eq_last( tbl, eq ) < event ) {
		if( count-- < 0 ) {
			printf( "TIMED OUT\n" );
			return -1;
		}

		sleep(1);
	}
	return 0;
}