/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
 * Copyright by The HDF Group.                                               *
 * Copyright by the Board of Trustees of the University of Illinois.         *
 * All rights reserved.                                                      *
 *                                                                           *
 * This file is part of HDF5.  The full HDF5 copyright notice, including     *
 * terms governing use, modification, and redistribution, is contained in    *
 * the files COPYING and Copyright.html.  COPYING can be found at the root   *
 * of the source code distribution tree; Copyright.html can be found at the  *
 * root level of an installed copy of the electronic HDF5 document set and   *
 * is linked from the top-level documents page.  It can also be found at     *
 * http://hdfgroup.org/HDF5/doc/Copyright.html.  If you do not have          *
 * access to either file, you may request a copy from help@hdfgroup.org.     *
 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */

#include "h5diff.h"
#include "ph5diff.h"
#include <stdlib.h>
#include <assert.h>
#include "h5diff_common.h"

static void ph5diff_worker(int );

/*-------------------------------------------------------------------------
 * Function: main
 *
 * Purpose: h5diff/ph5diff main program
 *
 * Return: An  exit status of 0 means no differences were found, 1 means some
 *   differences were found.
 *
 * Programmer: Pedro Vicente, pvn@ncsa.uiuc.edu
 *
 * Date: May 9, 2003
 *
 * Comments:
 *
 * Modifications: July 2004
 *  Introduced the four modes:
 *   Normal mode: print the number of differences found and where they occured
 *   Report mode: print the above plus the differences
 *   Verbose mode: print the above plus a list of objects and warnings
 *   Quiet mode: do not print output
 *
 * November 2004: Leon Arber (larber@uiuc.edu)
 *        Additions that allow h5diff to be run in parallel
 *
 * This function drives the diff process and will do a serial or parallel diff depending
 * on the value of the global variable g_Parallel (default is 0), set to 1 when the program
 * is run as "ph5diff"
 *-------------------------------------------------------------------------
 */


int main(int argc, const char *argv[])
{
    int        nID = 0;
    const char *fname1 = NULL;
    const char *fname2 = NULL;
    const char *objname1  = NULL;
    const char *objname2  = NULL;
    hsize_t    nfound=0;
    diff_opt_t options;

    outBuffOffset = 0;
    g_Parallel = 1;

    MPI_Init(&argc, (char***) &argv);

    MPI_Comm_rank(MPI_COMM_WORLD, &nID);
    MPI_Comm_size(MPI_COMM_WORLD, &g_nTasks);

    if(g_nTasks == 1)
    {
    printf("Only 1 task available...doing serial diff\n");

    g_Parallel = 0;

    parse_input(argc, argv, &fname1, &fname2, &objname1, &objname2, &options);

    nfound = h5diff(fname1,fname2,objname1,objname2,&options);

    print_info(&options);

    MPI_Finalize();

    return 0;
    }

    /* Have the manager process the command-line */
    if(nID == 0)
    {
    parse_input(argc, argv, &fname1, &fname2, &objname1, &objname2, &options);

    nfound = h5diff(fname1,fname2,objname1,objname2,&options);

    MPI_Barrier(MPI_COMM_WORLD);

    print_info(&options);
    print_manager_output();

    MPI_Finalize();

    return 0;
    }
    /* All other tasks become workers and wait for assignments. */
    else
    ph5diff_worker(nID);
}

/*-------------------------------------------------------------------------
 * Function: ph5diff_worker
 *
 * Purpose: worker process of ph5diff
 *
 * Return: none
 *
 * Programmer: Leon Arber
 * Date: January 2005
 *
 * Comments:
 *
 * Modifications:
 *
 *-------------------------------------------------------------------------
 */
static void
ph5diff_worker(int nID)
{
    struct diff_args args;
    hid_t file1_id, file2_id;
    char    filenames[2][1024];
    char    out_data[PRINT_DATA_MAX_SIZE] = {0};
    hsize_t    nfound=0;
    struct diffs_found  diffs;
    int i;
    MPI_Status Status;

    MPI_Comm_rank(MPI_COMM_WORLD, &nID);
    outBuffOffset = 0;

    MPI_Recv(filenames, 1024*2, MPI_CHAR, 0, MPI_ANY_TAG, MPI_COMM_WORLD, &Status);
    if(Status.MPI_TAG == MPI_TAG_PARALLEL)
    {
    /* disable error reporting */
    H5E_BEGIN_TRY
    {
        /* Open the files */
        if ((file1_id = H5Fopen (filenames[0], H5F_ACC_RDONLY, H5P_DEFAULT)) < 0)
        {
        printf ("h5diff Task [%d]: <%s>: unable to open file\n", nID, filenames[0]);
        MPI_Abort(MPI_COMM_WORLD, 0);
        }
        if ((file2_id = H5Fopen (filenames[1], H5F_ACC_RDONLY, H5P_DEFAULT)) < 0)
        {
        printf ("h5diff Task [%d]: <%s>: unable to open file\n", nID, filenames[1]);
        MPI_Abort(MPI_COMM_WORLD, 0);
        }
        /* enable error reporting */
    }
    H5E_END_TRY;


    while(1)
    {
        MPI_Probe(0, MPI_ANY_TAG, MPI_COMM_WORLD, &Status);

        if(Status.MPI_TAG == MPI_TAG_ARGS)
        {
        /*Recv parameters for diff from manager task */
        MPI_Recv(&args, sizeof(struct diff_args), MPI_BYTE, 0, MPI_TAG_ARGS, MPI_COMM_WORLD, &Status);
        /*Do the diff */
        nfound = diff(file1_id, args.name, file2_id, args.name, &(args.options), args.type);
        diffs.nfound = nfound;
        diffs.not_cmp = args.options.not_cmp;

        /*If print buffer has something in it, request print token.*/
        if(outBuffOffset>0)
        {
            MPI_Send(NULL, 0, MPI_BYTE, 0, MPI_TAG_TOK_REQUEST, MPI_COMM_WORLD);
            /*Wait for print token. */
            MPI_Recv(NULL, 0, MPI_BYTE, 0, MPI_TAG_PRINT_TOK, MPI_COMM_WORLD, &Status);

            /*When get token, send all of our output to the manager task and then return the token */
            for(i=0; i<outBuffOffset; i+=PRINT_DATA_MAX_SIZE)
            MPI_Send(outBuff+i, PRINT_DATA_MAX_SIZE, MPI_BYTE, 0, MPI_TAG_PRINT_DATA, MPI_COMM_WORLD);


            /* An overflow file exists, so we send it's output to the manager too and then delete it */
            if(overflow_file)
            {
                int tmp;
            memset(out_data, 0, PRINT_DATA_MAX_SIZE);
            i=0;

            rewind(overflow_file);
            while((tmp = getc(overflow_file)) >= 0)
            {
                *(out_data + i++) = (char)tmp;
                if(i==PRINT_DATA_MAX_SIZE)
                {
                MPI_Send(out_data, PRINT_DATA_MAX_SIZE, MPI_BYTE, 0, MPI_TAG_PRINT_DATA, MPI_COMM_WORLD);
                i=0;
                memset(out_data, 0, PRINT_DATA_MAX_SIZE);
                }
            }

            if(i>0)
                MPI_Send(out_data, PRINT_DATA_MAX_SIZE, MPI_BYTE, 0, MPI_TAG_PRINT_DATA, MPI_COMM_WORLD);

            fclose(overflow_file);
            overflow_file = NULL;
            }

            fflush(stdout);
            memset(outBuff, 0, OUTBUFF_SIZE);
            outBuffOffset = 0;

            MPI_Send(&diffs, sizeof(diffs), MPI_BYTE, 0, MPI_TAG_TOK_RETURN, MPI_COMM_WORLD);
        }
        else
            MPI_Send(&diffs, sizeof(diffs), MPI_BYTE, 0, MPI_TAG_DONE, MPI_COMM_WORLD);
        }
        else if(Status.MPI_TAG == MPI_TAG_END)
        {
        MPI_Recv(NULL, 0, MPI_BYTE, 0, MPI_TAG_END, MPI_COMM_WORLD, &Status);
    /*  printf("exiting..., task: %d\n", nID);
        fflush(stdout);*/
        break;
        }
        else
        {
        printf("ph5diff_worker: ERROR: invalid tag (%d) received\n", Status.MPI_TAG);
        MPI_Abort(MPI_COMM_WORLD, 0);
        }

    }
    }

    MPI_Barrier(MPI_COMM_WORLD);
    MPI_Finalize();
}

