/* -*- Mode: C; c-basic-offset:4 ; indent-tabs-mode:nil ; -*- */
/*
 *
 *  (C) 2009 by Argonne National Laboratory.
 *      See COPYRIGHT in top-level directory.
 */
#include "mpi.h"
#include <stdio.h>
#include "mpitest.h"

/*
static char MTestDescrip[] = "Test creating and inserting attributes in \
different orders to ensure that the list management code handles all cases.";
*/

int checkAttrs( MPI_Comm, int, int [], int [] );
int delete_fn( MPI_Comm, int, void *, void *);

#define NKEYS 5
static int key[NKEYS];      /* Keys in creation order */
static int keyorder[NKEYS]; /* Index (into key) of keys in order added to comm 
			    (key[keyorder[0]] is first set) */
static int nkeys = 0;
static int ncall = 0;
static int errs  = 0;
/* 
 * Test that attributes on comm self are deleted in LIFO order 
 */

int main( int argc, char *argv[] )
{
    int      attrval[10];
    int      wrank, i;
    MPI_Comm comm;

    MPI_Init( &argc, &argv );

    MPI_Comm_rank( MPI_COMM_WORLD, &wrank );

    comm = MPI_COMM_SELF;
    
    /* Create key values */
    for (nkeys=0; nkeys<NKEYS; nkeys++) {
	MPI_Comm_create_keyval( MPI_NULL_COPY_FN, delete_fn,
				&key[nkeys], (void *)0 );
	attrval[nkeys] = 1024 * nkeys;
    }
    
    /* Insert attribute in several orders.  Test after put with get,
       then delete, then confirm delete with get. */
    
    MPI_Comm_set_attr( comm, key[3], &attrval[3] ); keyorder[0] = 3;
    MPI_Comm_set_attr( comm, key[2], &attrval[2] ); keyorder[1] = 2;
    MPI_Comm_set_attr( comm, key[0], &attrval[0] ); keyorder[2] = 0;
    MPI_Comm_set_attr( comm, key[1], &attrval[1] ); keyorder[3] = 1;
    MPI_Comm_set_attr( comm, key[4], &attrval[4] ); keyorder[4] = 4;
    
    errs += checkAttrs( comm, NKEYS, key, attrval );
    
    for (i=0; i<NKEYS; i++) {
	/* Save the key value so that we can compare it in the 
	   delete function */
	int keyval = key[i];
	MPI_Comm_free_keyval( &keyval );
    }
	
    MPI_Finalize();
    
    if (wrank == 0) {
	if (ncall != nkeys) {
	    printf( "Deleted %d keys but should have deleted %d\n", 
		    ncall, nkeys );
	    errs++;
	}
	if (errs == 0) printf( " No Errors\n" );
	else printf( " Found %d errors\n", errs );
    }
    return 0;
  
}

int checkAttrs( MPI_Comm comm, int n, int lkey[], int attrval[] )
{
    int lerrs = 0;
    int i, flag, *val_p;

    for (i=0; i<n; i++) {
	MPI_Comm_get_attr( comm, lkey[i], &val_p, &flag );
	if (!flag) {
	    lerrs++;
	    fprintf( stderr, "Attribute for key %d not set\n", i );
	}
	else if (val_p != &attrval[i]) {
	    lerrs++;
	    fprintf( stderr, "Atribute value for key %d not correct\n",
		     i );
	}
    }

    return lerrs;
}

/* We *should* be deleting key[keyorder[nkeys-ncall]] */
int delete_fn( MPI_Comm comm, int keyval, void *attribute_val, 
	       void *extra_state)
{
    if (ncall >= nkeys) {
	printf( "delete function called too many times!\n" );
	errs++;
    }

    /* As of MPI 2.2, the order of deletion of attributes on 
       MPI_COMM_SELF is defined */
    if (MPI_VERSION > 2 || (MPI_VERSION == 2 && MPI_SUBVERSION >= 2)) {
	if (keyval != key[keyorder[nkeys-1-ncall]]) {
	    printf( "Expected key # %d but found key with value %d\n", 
		    keyorder[nkeys-1-ncall], keyval );
	    errs++;
	}
    }
    ncall++;
    return MPI_SUCCESS;
}

/*
int checkNoAttrs( MPI_Comm comm, int n, int lkey[] )
{
    int lerrs = 0;
    int i, flag, *val_p;

    for (i=0; i<n; i++) {
	MPI_Comm_get_attr( comm, lkey[i], &val_p, &flag );
	if (flag) {
	    lerrs++;
	    fprintf( stderr, "Attribute for key %d set but should be deleted\n", i );
	}
    }

    return lerrs;
}
*/
