/** @file testio.c
    @short Test program for eventio data format.
    
    @author  Konrad Bernloehr
    @date    1994, 1997, 2000
    @date    CVS $Date: 2002-07-25 17:57:05 $
    @version CVS $Revision: 1.1 $
*/
    
/*  Copyright (C) 1994, 1997, 2000 Konrad Bernloehr. All rights reserved.   */

#include "initial.h"
#include "warning.h"
#include "io_basic.h"

struct test_struct
{
   long lvar[2];
   int ilvar[2];
   int isvar[2];
   short svar[3];
   double fvar[2];
   double dvar[2];
   int8_t i8var[2];
   uint8_t u8var[2];
   int16_t i16var[2];
   uint16_t u16var[2];
   int32_t i32var[2];
   uint32_t u32var[2];
#ifdef HAVE_64BIT_INT
   int64_t i64var[2];
   uint64_t u64var[2];
#endif
};

typedef struct test_struct TEST_DATA;

static int care_long, care_int, care_short;

int datacmp ARGLIST((TEST_DATA *data1, TEST_DATA *data2));
int write_test1 ARGLIST((TEST_DATA *data, IO_BUFFER *iobuf));
int read_test1 ARGLIST((TEST_DATA *data, IO_BUFFER *iobuf));
int write_test2 ARGLIST((TEST_DATA *data, IO_BUFFER *iobuf));
int read_test2 ARGLIST((TEST_DATA *data, IO_BUFFER *iiobuf));

/* ------------------------ datacmp ---------------------- */
/**
 *  @short Compare elements of test data structures.
 *  Compare elements of test data structures with the accuracy
 *  relevant to the I/O package.
 *
 *  @param data1 first data structure
 *  @param data2 second data structure
 *
 *  @return 0 (something did not match), 1 (O.K.)
 *
 */
 
int datacmp(data1,data2)
   TEST_DATA *data1;
   TEST_DATA *data2;
{
   int i, ok;
   
   ok = 1;
   
   for (i=0; i<2; i++)
      if ( (int32_t)data1->lvar[i] != (int32_t)data2->lvar[i] )
      {
         fprintf(stderr,"Long variable %d does not match: %08lx <--> %08lx\n",
            i+1,data1->lvar[i],data2->lvar[i]);
         ok = 0;
      }
      else if ( data1->lvar[i] != data2->lvar[i] )
         care_long = 1;
      
   for (i=0; i<2; i++)
      if ( (int32_t)data1->ilvar[i] != (int32_t)data2->ilvar[i] )
      {
         fprintf(stderr,"Int 'l' variable %d does not match: %08x <--> %08x\n",
            i+1,data1->ilvar[i],data2->ilvar[i]);
         ok = 0;
      }
      
   for (i=0; i<2; i++)
      if ( (int16_t)data1->isvar[i] != (int16_t)data2->isvar[i] )
      {
         fprintf(stderr,"Int 's' variable %d does not match: %04x <--> %04x\n",
            i+1,data1->isvar[i],data2->isvar[i]);
         ok = 0;
      }
      else if ( data1->isvar[i] != data2->isvar[i] )
         care_short = 1;
      
   for (i=0; i<3; i++)
      if ( data1->svar[i] != data2->svar[i] )
      {
         fprintf(stderr,"Short variable %d does not match: %04x <--> %04x\n",
            i+1,data1->svar[i],data2->svar[i]);
         ok = 0;
      }
      
   for (i=0; i<2; i++)
   {
      float f1, f2;
      f1 = (float) data1->fvar[i];
      f2 = (float) data2->fvar[i];
      if ( f1 != f2 )
      {
         fprintf(stderr,"Float variable %d does not match: %08x <--> %08x\n",
            i+1,*((int *)(&f1)),*((int *)(&f2)));
         ok = 0;
      }
   }
   
   for (i=0; i<2; i++)
      if ( data1->dvar[i] != data2->dvar[i] )
      {
	 double f1, f2;
	 f1 = data1->dvar[i];
	 f2 = data2->dvar[i];
         fprintf(stderr,
	    "Double variable %d does not match: %08x%08x <--> %08x%08x\n",
            i+1,*((int *)(&f1)),*((int *)((char *)(&f1)+4)),
	    *((int *)(&f2)),*((int *)((char *)(&f2)+4)));
         ok = 0;
      }
      
   for (i=0; i<2; i++)
      if ( data1->i8var[i] != data2->i8var[i] )
      {
         fprintf(stderr,"Int8 variable %d does not match: %08x <--> %08x\n",
            i+1,data1->i8var[i],data2->i8var[i]);
         ok = 0;
      }
      
   for (i=0; i<2; i++)
      if ( data1->u8var[i] != data2->u8var[i] )
      {
         fprintf(stderr,"UInt8 variable %d does not match: %08x <--> %08x\n",
            i+1,data1->u8var[i],data2->u8var[i]);
         ok = 0;
      }
      
   for (i=0; i<2; i++)
      if ( data1->i16var[i] != data2->i16var[i] )
      {
         fprintf(stderr,"Int16 variable %d does not match: %08x <--> %08x\n",
            i+1,data1->i16var[i],data2->i16var[i]);
         ok = 0;
      }
      
   for (i=0; i<2; i++)
      if ( data1->u16var[i] != data2->u16var[i] )
      {
         fprintf(stderr,"UInt16 variable %d does not match: %08x <--> %08x\n",
            i+1,data1->u16var[i],data2->u16var[i]);
         ok = 0;
      }
      
   for (i=0; i<2; i++)
      if ( data1->i32var[i] != data2->i32var[i] )
      {
         fprintf(stderr,"Int32 variable %d does not match: %08x <--> %08x\n",
            i+1,data1->i32var[i],data2->i32var[i]);
         ok = 0;
      }
      
   for (i=0; i<2; i++)
      if ( data1->u32var[i] != data2->u32var[i] )
      {
         fprintf(stderr,"UInt32 variable %d does not match: %08x <--> %08x\n",
            i+1,data1->u32var[i],data2->u32var[i]);
         ok = 0;
      }

#ifdef HAVE_64BIT_INT
   for (i=0; i<2; i++)
      if ( data1->i64var[i] != data2->i64var[i] )
      {
#ifdef SIXTY_FOUR_BITS
         fprintf(stderr,"Int64 variable %d does not match: %016lx <--> %016lx\n",
            i+1,data1->i64var[i],data2->i64var[i]);
#else
         fprintf(stderr,"Int64 variable %d does not match: %016llx <--> %016llx\n",
            i+1,data1->i64var[i],data2->i64var[i]);
#endif
         ok = 0;
      }
      
   for (i=0; i<2; i++)
      if ( data1->u64var[i] != data2->u64var[i] )
      {
#ifdef SIXTY_FOUR_BITS
         fprintf(stderr,"UInt64 variable %d does not match: %016lx <--> %016lx\n",
            i+1,data1->u64var[i],data2->u64var[i]);
#else
         fprintf(stderr,"UInt64 variable %d does not match: %016llx <--> %016llx\n",
            i+1,data1->u64var[i],data2->u64var[i]);
#endif
         ok = 0;
      }
#endif
      
   return ok;
}

/* --------------------- write_test1 ---------------------- */
/**
 *  @short Write test data with single-element functions
 *
 *  @param    data   Pointer to test data structure
 *  @param    iobuf  Pointer to I/O buffer
 *
 *  @return  0 (O.K.), <0 (error as for put_item_end())
 *
 */

int write_test1(data,iobuf)
   TEST_DATA *data;
   IO_BUFFER *iobuf;
{
   IO_ITEM_HEADER item_header;
   
   item_header.type = 99;             /* test data */
   item_header.version = 0;          /* Version 0 (test) */
   item_header.ident = 123;
   
   put_item_begin(iobuf,&item_header);

   put_long(data->lvar[0],iobuf);
   put_long(data->lvar[1],iobuf);
   put_long((long)data->ilvar[0],iobuf);
   put_long((long)data->ilvar[1],iobuf);
   put_short(data->isvar[0],iobuf);
   put_short(data->isvar[1],iobuf);
   put_short(data->svar[0],iobuf);
   put_short(data->svar[1],iobuf);
   put_short(data->svar[2],iobuf);
   put_real(data->fvar[0],iobuf);
   put_real(data->fvar[1],iobuf);
   put_double(data->dvar[0],iobuf);
   put_double(data->dvar[1],iobuf);
   put_byte(data->i8var[0],iobuf);
   put_byte(data->i8var[1],iobuf);
   put_byte(data->u8var[0],iobuf);
   put_byte(data->u8var[1],iobuf);
   put_short(data->i16var[0],iobuf);
   put_short(data->i16var[1],iobuf);
   put_short(data->u16var[0],iobuf);
   put_short(data->u16var[1],iobuf);
   put_int32(data->i32var[0],iobuf);
   put_int32(data->i32var[1],iobuf);
   put_uint32(data->u32var[0],iobuf);
   put_uint32(data->u32var[1],iobuf);
#ifdef HAVE_64BIT_INT
   put_vector_of_int64(&data->i64var[0],1,iobuf);
   put_vector_of_int64(&data->i64var[1],1,iobuf);
   put_vector_of_uint64(&data->u64var[0],1,iobuf);
   put_vector_of_uint64(&data->u64var[1],1,iobuf);
#endif   
   return(put_item_end(iobuf,&item_header));
}

/* ---------------------- read_test1 ---------------------- */
/**
 *  @short Read test data with single-element functions
 *
 *  @param   data   Pointer to test data structure
 *  @param   iobuf  Pointer to I/O buffer
 *
 *  @return  0 (ok), <0 (error as for get_item_end())
 *
 */

int read_test1(data,iobuf)
   TEST_DATA *data;
   IO_BUFFER *iobuf;
{
   IO_ITEM_HEADER item_header;
   
   item_header.type = 99;             /* test data */
   if ( get_item_begin(iobuf,&item_header) < 0 )
   {
      Warning("Missing or invalid test data block.");
      return -4;
   }

   data->lvar[0]  = get_long(iobuf);
   data->lvar[1]  = get_long(iobuf);
   data->ilvar[0] = get_long(iobuf);
   data->ilvar[1] = get_long(iobuf);
   data->isvar[0] = get_short(iobuf);
   data->isvar[1] = get_short(iobuf);
   data->svar[0]  = get_short(iobuf);
   data->svar[1]  = get_short(iobuf);
   data->svar[2]  = get_short(iobuf);
   data->fvar[0]  = get_real(iobuf);
   data->fvar[1]  = get_real(iobuf);
   data->dvar[0]  = get_double(iobuf);
   data->dvar[1]  = get_double(iobuf);
   data->i8var[0] = get_byte(iobuf);
   data->i8var[1] = get_byte(iobuf);
   data->u8var[0] = get_byte(iobuf);
   data->u8var[1] = get_byte(iobuf);
   data->i16var[0] = get_short(iobuf);
   data->i16var[1] = get_short(iobuf);
   data->u16var[0] = get_short(iobuf);
   data->u16var[1] = get_short(iobuf);
   data->i32var[0] = get_int32(iobuf);
   data->i32var[1] = get_int32(iobuf);
   data->u32var[0] = get_uint32(iobuf);
   data->u32var[1] = get_uint32(iobuf);
#ifdef HAVE_64BIT_INT
   get_vector_of_int64(&data->i64var[0],1,iobuf);
   get_vector_of_int64(&data->i64var[1],1,iobuf);
   get_vector_of_uint64(&data->u64var[0],1,iobuf);
   get_vector_of_uint64(&data->u64var[1],1,iobuf);
#endif   

   return(get_item_end(iobuf,&item_header));
}

/* --------------------- write_test2 ---------------------- */
/**
 *  @short Write test data with vector functions as far as possible
 *
 *  @param    data    Pointer to test data structure
 *  @param    iobuf   Pointer to I/O buffer
 *
 *  @return  0 (ok), <0 (error as for put_item_end())
 *
 */

int write_test2(data,iobuf)
   TEST_DATA *data;
   IO_BUFFER *iobuf;
{
   IO_ITEM_HEADER item_header;
   
   item_header.type = 99;             /* test data */
   item_header.version = 0;          /* Version 0 (test) */
   item_header.ident = 123;
   
   put_item_begin(iobuf,&item_header);

   put_vector_of_long(data->lvar,2,iobuf);
   put_long((long)data->ilvar[0],iobuf);
   put_long((long)data->ilvar[1],iobuf);
   put_vector_of_int(data->isvar,2,iobuf);
   put_vector_of_short(data->svar,3,iobuf);
   put_vector_of_real(data->fvar,2,iobuf);
   put_vector_of_double(data->dvar,2,iobuf);
   put_vector_of_byte((uint8_t *)data->i8var,2,iobuf);
   put_vector_of_byte(data->u8var,2,iobuf);
   put_vector_of_short(data->i16var,2,iobuf);
   put_vector_of_short((int16_t *)data->u16var,2,iobuf);
   put_vector_of_int32(data->i32var,2,iobuf);
   put_vector_of_uint32(data->u32var,2,iobuf);
#ifdef HAVE_64BIT_INT
   put_vector_of_int64(data->i64var,2,iobuf);
   put_vector_of_uint64(data->u64var,2,iobuf);
#endif   
   
   return(put_item_end(iobuf,&item_header));
}

/* ---------------------- read_test2 ---------------------- */
/**
 *  @short Read test data with vector functions as far as possible
 *
 *  @param   data   Pointer to test data structure
 *  @param   iobuf  Pointer to I/O buffer
 *
 *  @return  0 (ok), <0 (error as for get_item_end())
 *
 */

int read_test2(data,iobuf)
   TEST_DATA *data;
   IO_BUFFER *iobuf;
{
   IO_ITEM_HEADER item_header;
   
   item_header.type = 99;             /* test data */
   if ( get_item_begin(iobuf,&item_header) < 0 )
   {
      Warning("Missing or invalid test data block.");
      return -4;
   }

   get_vector_of_long(data->lvar,2,iobuf);
   data->ilvar[0] = get_long(iobuf);
   data->ilvar[1] = get_long(iobuf);
   get_vector_of_int(data->isvar,2,iobuf);
   get_vector_of_short(data->svar,3,iobuf);
   get_vector_of_real(data->fvar,2,iobuf);
   get_vector_of_double(data->dvar,2,iobuf);
   get_vector_of_byte((uint8_t *)data->i8var,2,iobuf);
   get_vector_of_byte(data->u8var,2,iobuf);
   get_vector_of_short(data->i16var,2,iobuf);
   get_vector_of_short((int16_t *)data->u16var,2,iobuf);
   get_vector_of_int32(data->i32var,2,iobuf);
   get_vector_of_uint32(data->u32var,2,iobuf);
#ifdef HAVE_64BIT_INT
   get_vector_of_int64(data->i64var,2,iobuf);
   get_vector_of_uint64(data->u64var,2,iobuf);
#endif   

   return(get_item_end(iobuf,&item_header));
}

/* ---------------------- perror ------------------------- */
/**
 *  @short Replacement for function missing on OS-9
*/

#ifdef OS_OS9
int perror(text)
    char *text;
{
    fprintf(stderr,"%s: Error\n",text);
    return 0;
}
#endif

/* ------------------------ main ------------------------- */
/**
 *  @short Main function for I/O test program.
 *  First writes a test data structure with the vector
 *  functions, then the same data structure with the
 *  single-element functions. The output file is then
 *  closed and reopened for reading. The first structure
 *  is then read with the single-element functions and
 *  the second with the vector functions (i.e. the other
 *  way as done for writing).
 *  The data from the file is compared with the original
 *  data, taking the relevant accuracy into account.
 *  Note that if an 'int' variable is written via 'put_short()'
 *  and then read again via 'get_short()' not only the
 *  upper two bytes (on a 32-bit machine) are lost but
 *  also the sign bit is propagated from bit 15 to the
 *  upper 16 bits. Similarly, if a 'long' variable is written
 *  via 'put_long()' and later read via 'get_long()' on a
 *  64-bit-machine, not only the upper 4 bytes are lost but
 *  also the sign in bit 31 is propagated to the upper 32 bits.
 */

#ifdef ANSI_C
int main (int argc, char **argv)
#else
int main (argc, argv)
   int argc;
   char **argv;
#endif
{
   IO_BUFFER *iobuf;
   IO_ITEM_HEADER item_header;
   FILE *output = NULL, *input;
   TEST_DATA tdata, cdata1, cdata2;
   int ok;
   
   tdata.lvar[0]   = 0x01020304L;  tdata.lvar[1]  = 0xf1f2f3f4L;
   tdata.ilvar[0]  = 0x01020304;   tdata.ilvar[1] = 0xf1f2f3f4;
   tdata.isvar[0]  = 0x0102;       tdata.isvar[1] = 0xf1f2;
   tdata.svar[0]   = 0x0102; 
   tdata.svar[1]   = (short) 0xf1f2; 
   tdata.svar[2]   = 0x0a0b;
   tdata.fvar[0]   = 2.38793926059e-38;
   tdata.fvar[1]   = -2.40608939547e+30;
   tdata.dvar[0]   = 2.38793926059674673672e-140;
   tdata.dvar[1]   = -2.40608939547354636548e+180;
   tdata.i8var[0]  = 0x1e;         tdata.i8var[1]  = 0xe1;
   tdata.u8var[0]  = 0x1e;         tdata.u8var[1]  = 0xe1;
   tdata.i16var[0] = 0x1e2e;       tdata.i16var[1] = 0xe2e1;
   tdata.u16var[0] = 0x1e2e;       tdata.u16var[1] = 0xe2e1;
   tdata.i32var[0] = 0x1e2e3e4e;   tdata.i32var[1] = 0xe4e3e2e1;
   tdata.u32var[0] = 0x1e2e3e4e;   tdata.u32var[1] = 0xe4e3e2e1;
#ifdef HAVE_64BIT_INT
   tdata.i64var[0] = 0x1a2a3a4a5a6a7a8a;
   tdata.i64var[1] = 0xa8a7a6a5a4a3a2a1;
   tdata.u64var[0] = 0x1b2b3b4b5b6b7b8b;
   tdata.u64var[1] = 0xb8b7b6b5b4b3b2b1;
#endif

   if ( (iobuf = allocate_io_buffer((size_t)1000)) == (IO_BUFFER *) NULL )
      exit(1);
   if ( argc > 1 )
   {
      if ( (output = fopen(argv[1],WRITE_BINARY)) == (FILE *) NULL )
      {
         perror(argv[1]);
         exit(1);
      }
      iobuf->output_file = output;
   }
   else
   {
      fprintf(stderr,"Syntax: %s filename\n",argv[0]);
      exit(1);
   }

   fprintf(stderr,"\nWrite test data to file '%s'.\n",argv[1]);
   fprintf(stderr,"Default byte order, using mainly vector functions.\n");
   write_test2(&tdata,iobuf);
   fprintf(stderr,"Default byte order, using single-element functions.\n");
   write_test1(&tdata,iobuf);
   iobuf->byte_order = 1;
   fprintf(stderr,"Reversed byte order, using single-element functions.\n");
   write_test1(&tdata,iobuf);
   iobuf->byte_order = 0;
   fprintf(stderr,"Normal byte order, using single-element functions.\n");
   write_test1(&tdata,iobuf);
   fprintf(stderr,"Write tests done.\n\n");
   
   fclose(output);
   iobuf->output_file = output = NULL;
   if ( (input = fopen(argv[1],READ_BINARY)) == (FILE *) NULL )
   {
      perror(argv[1]);
      exit(1);
   }
   iobuf->input_file = input;
   ok = 1;

   fprintf(stderr,"Read test data from file '%s'.\n",argv[1]);   

   fprintf(stderr,"Default byte order, using single-element functions.\n");
   if ( find_io_block(iobuf,&item_header) < 0 )
   {
      Error("*** Finding I/O block 1 failed");
      exit(1);
   }
   if ( read_io_block(iobuf,&item_header) < 0 )
   {
      Error("*** Reading I/O block 1 failed");
      exit(1);
   }
   if ( read_test1(&cdata1,iobuf) < 0 )
   {
      Error("*** Read test 1 failed");
      exit(1);
   }
   if ( datacmp(&tdata,&cdata1) != 1 )
   {
      Error("*** Data from read test 1 does not match.");
      ok = 0;
   }
   
   fprintf(stderr,"Default byte order, using mainly vector functions.\n");
   if ( find_io_block(iobuf,&item_header) < 0 )
   {
      Error("*** Finding I/O block 1 failed");
      exit(1);
   }
   if ( read_io_block(iobuf,&item_header) < 0 )
   {
      Error("*** Reading I/O block 1 failed");
      exit(1);
   }
   if ( read_test2(&cdata2,iobuf) < 0 )
   {
      Error("*** Read test 2 failed");
      exit(1);
   }
   if ( datacmp(&tdata,&cdata2) != 1 )
   {
      Error("*** Data from read test 2 does not match");
      ok = 0;
   }
   fprintf(stderr,"Reversed byte order, using single-element functions.\n");
   if ( find_io_block(iobuf,&item_header) < 0 )
   {
      Error("*** Finding I/O block 3 failed");
      exit(1);
   }
   if ( read_io_block(iobuf,&item_header) < 0 )
   {
      Error("*** Reading I/O block 3 failed");
      exit(1);
   }
   if ( read_test1(&cdata1,iobuf) < 0 )
   {
      Error("*** Read test 3 failed");
      exit(1);
   }
   if ( datacmp(&tdata,&cdata1) != 1 )
   {
      Error("*** Data from read test 3 does not match.");
      ok = 0;
   }
   
   fprintf(stderr,"Normal byte order, using single-element functions.\n");
   if ( find_io_block(iobuf,&item_header) < 0 )
   {
      Error("*** Finding I/O block 4 failed");
      exit(1);
   }
   if ( read_io_block(iobuf,&item_header) < 0 )
   {
      Error("*** Reading I/O block 4 failed");
      exit(1);
   }
   if ( read_test1(&cdata2,iobuf) < 0 )
   {
      Error("*** Read test 4 failed");
      exit(1);
   }
   if ( datacmp(&tdata,&cdata2) != 1 )
   {
      Error("*** Data from read test 4 does not match");
      ok = 0;
   }
   
   Information("Read tests done\n");
   
   if ( ok )
      Information("Everthing is ok. Congratulations!\n");
      
   if ( care_long )
   {
      Information("Note: on this machine you should care about the sign propagation");
      Information("of 'LONG' (INT32) data elements to long integer variables.\n");
   }
   
   if ( care_int )
   {
      Information("Note: on this machine you should care about the sign propagation");
      Information("of 'LONG' (INT32) data elements to 32 bit integer variables.\n");
   }
   
   if ( care_short )
   {
      Information("Note: on this machine you should care about the sign propagation");
      Information("of 'SHORT' data elements to integer or long integer variables.\n");
   }
   
#ifdef HAVE_64BIT_INT
   Information("On this machine you can read and write 64-bit integers but you should");
   Information("be aware that this feature is not available on all systems otherwise");
   Information("supported by eventio.");
#ifdef SIXTY_FOUR_BITS
   Information("These 64-bit integers are native types.\n");
#else
   Information("These 64-bit integers are implemented through the C compiler.\n");
#endif
#else
   Information("On this system no 64-bit integers are supported.\n");
#endif

   return 0;
}

