/*********************** io_lattice_generic.c *************************/
/* This reads and writes a (binary) lattice
 *
 * NOTE: THIS HAS TO BE ENCAPSULATED BY A FILE 
 * io_lattice.c 
 * WHICH DEFINES
 *
 * typedef struct { } allfields;
 *
 * void copy_fields(int site, allfields *s)    copy from all latfields to s.(whatever)
 * void set_fields(allfields *s, int site)     copy s.(stuff) to lattice fields
 *
 * #include "../generic/io_lattice_generic.c"
 */ 

/* read and write a binary lattice */

void save_binary_lattice(FILE *f);
void restore_lattice_slow(FILE * f);
void restore_lattice_fast(FILE * f);

/* size of max comm buffer, in bytes - used in fast routines */
#define N_MAX_COMMSIZE 50000

/* stored field */

typedef struct {
  allfields a;
  int x[NDIM];
} stored_field;


void restore_binary(FILE * f) 
{
  int j;

  if(this_node==0) {
    int ok,i;
    int dims[NDIM];
    
    ok = (fread(&j,sizeof(int),1,f) == 1);
    if (ok && j != NDIM) {
      printf("* Lattice dimension error: in file %d, expecting %d\n",j,NDIM);
      halt(" ####");
    }

    ok = ok && (fread(dims,sizeof(int),NDIM,f) == NDIM);
    j = 0;
    foralldir(i) if (dims[i] != lattice.size[i]) j = 1;
    if (ok && j) {
      printf("* Lattice size error: in file ");
      for(i=0; i<NDIM-1; i++) printf("%d x",dims[i]);
      printf("%d,  should be ",dims[NDIM-1]);
      for(i=0; i<NDIM-1; i++) printf("%d x",lattice.size[i]);
      printf("%d\n ",lattice.size[NDIM-1]);
      halt(" #####");
    }

    ok = ok && (fread(&j,sizeof(int),1,f) == 1);
    if (ok && j != sizeof(allfields)) {
      printf("* Wrong size of fields: in file %d, expecting %d\n",
	     j, (int)sizeof(allfields));
      halt(" #####");
    }

    ok = ok && (fread(&j,sizeof(int),1,f) == 1);
    if (j == number_of_nodes && number_of_nodes > 1) {
      /* # of nodes is the same, layout is likely the same - use
       * fast io mode
       */
      printf("  Loading config with fast I/O\n");
      j = 1;
    } else {
      printf("  Loading config with slow I/O\n");
      j = 0;   /* or not */
    }
    if (!ok) halt("config I/O error");
  }
  
  broadcast_int(&j);
  
  if (j) restore_lattice_fast(f);
  else restore_lattice_slow(f);
}


void save_binary(FILE *f) 
{

  /* node 0 does all the writing */
  if(this_node==0){
    int i,j;

    i = NDIM;
    j = sizeof(allfields);
    if (fwrite(&i,sizeof(int),1,f) != 1 ||
	fwrite(lattice.size,sizeof(int),NDIM,f) != NDIM ||
	fwrite(&j,sizeof(int),1,f) != 1 ||
	fwrite(&number_of_nodes,sizeof(int),1,f) != 1)
      halt("Error in writing lattice header");
  }

  save_binary_lattice(f);
}

/*************************************************************
 * Slow restore routine - 1 message/site
 */

void restore_lattice_slow(FILE * f) 
{
  stored_field nf;

  g_sync();

  if (this_node == 0) {
    int *sent;
    int l,i,newnode;

    /* Node 0 reads, and sends site to correct node */

    sent = (int *)memalloc(number_of_nodes,sizeof(int));
    for (l=0; l<number_of_nodes; l++) sent[l] = 0;

    for (l=0; l<lattice.volume; l++) {
      if (fread(&nf,sizeof(stored_field),1,f) != 1)
	halt("Read error in restore_lattice_slow");

      newnode=node_number(nf.x);
      if (newnode != 0) {
	send_field(&nf,sizeof(stored_field),newnode);
      } else {
	i = node_index(nf.x,&node);
	set_fields( &nf.a, i );
      }
      sent[newnode]++;
    }
    
    for (l=0; l<number_of_nodes; l++) 
      if (sent[l] != node.sites) halt("Site # sync error in restore_lattice_slow");
    free(sent);

  } else {
    /* now other node than 0 */
    int s,i;

    for (s=0; s<node.sites; s++) {
      receive_field(&nf,sizeof(stored_field));
      if (node_number(nf.x) != this_node)
	halt(" Receive node # error in restore_lattice_slow");
      i = node_index(nf.x,&node);
      set_fields( &nf.a, i );
    }
  }
  g_sync();
}
    


/*************************************************************
 * Fast restore routine - 1 or few msgs/node
 */


void restore_lattice_fast(FILE *f) 
{
  int i,j,n,s;
  stored_field nf, *buf;
  int n_messages,buf_size,last_size,idx;

  g_sync();

  if (node.sites * sizeof(stored_field) > N_MAX_COMMSIZE) 
    buf_size = N_MAX_COMMSIZE/sizeof(stored_field); 
  else buf_size = node.sites;

  n_messages = node.sites / buf_size;
  last_size = node.sites % buf_size;
  if (last_size > 0) n_messages++; else last_size = buf_size;

  if (this_node == 0) {

    /* first read own stuff */
    forallsites(i) {
      if (fread(&nf,sizeof(stored_field),1,f) != 1)
	halt("Read error in restore_binary_fast");
      if (this_node != node_number(nf.x)) 
	halt ("Node number error in restore_binary_fast");
      idx = node_index(nf.x,&node);
      set_fields( &nf.a, idx);
    }

    if (number_of_nodes > 1) {
      buf = (stored_field *)memalloc(buf_size,sizeof(stored_field));

      for (n=1; n<number_of_nodes; n++) {
	/* read the field(s) */	
	for (i=1; i<=n_messages; i++) {
	  if (i<n_messages) s = buf_size; else s = last_size;
	  if (fread(buf,sizeof(stored_field),s,f) != s)
	    halt("Read error in restore_binary_fast");

	  /* and send it */
	  send_field(buf, s*sizeof(stored_field),n);
	}
      }

      free( buf );
    }

  } else {

    /* Now other node than 0 */
    buf = (stored_field *)memalloc(buf_size,sizeof(stored_field));
    
    /* receive the fields */
    j = 0;
    for (n=1; n<=n_messages; n++) {
      if (n<n_messages) s = buf_size; else s = last_size;
      /* and receive it */
      receive_field(buf, s*sizeof(stored_field));
      
      for (i=0; i<s; i++) {
	if (!is_on_node(buf[i].x)) halt("Node index error in restore_binary_fast");
	idx = node_index(buf[i].x, &node);
	set_fields( &buf[i].a, idx );
	j++;
      }
    }
    free(buf);
    if (j != node.sites) halt("Node size error in restore_binary_fast");
  }
}


/*************************************************************
 * Fast save routine - 1 or few msgs/node
 */


void save_binary_lattice(FILE *f) 
{
  int i,j,n,s;
  stored_field nf, *buf;
  int n_messages,buf_size,last_size;

  g_sync();

  if (node.sites * sizeof(stored_field) > N_MAX_COMMSIZE) 
    buf_size = N_MAX_COMMSIZE / sizeof(stored_field); 
  else buf_size = node.sites;

  n_messages = node.sites / buf_size;
  last_size = node.sites % buf_size;
  if (last_size > 0) n_messages++; else last_size = buf_size;

  if (this_node == 0) {

    /* first write own stuff */
    forallsites(i) {
      copy_fields(i, &nf.a);
      foralldir(s) nf.x[s] = coordinate(i,s);
      if (fwrite(&nf,sizeof(stored_field),1,f) != 1)
	halt("Write error in save_binary");
    }

    if (number_of_nodes > 1) {
      buf = (stored_field *)memalloc(buf_size,sizeof(stored_field));

      for (n=1; n<number_of_nodes; n++) {
	/* tell node that can start sending */
	send_field(&n,sizeof(int),n);
	/* and receive the field(s) */
	for (i=1; i<=n_messages; i++) {
	  if (i<n_messages) s = buf_size; else s = last_size;
	  receive_field(buf, s*sizeof(stored_field));
	  
	  if (fwrite(buf,sizeof(stored_field),s,f) != s)
	    halt("Write error in save_binary 2");
	}
      }
      free( buf );
    }

  } else {
    /* Now other node than 0 */
    /* Wait for acknowledgement */
    receive_field(&n,sizeof(int));

    if (n != this_node) halt("Node # error in save_binary");
    buf = (stored_field *)memalloc(buf_size,sizeof(stored_field));
    
    /* copy the fields to buf */
    j = 0;
    forallsites(i) {
      copy_fields( i, &buf[j].a );
      foralldir(s) buf[j].x[s] = coordinate(i,s);
      if (++j == buf_size) {
	/* and send the full buffer to node 0 */
	send_field(buf, j*sizeof(stored_field), 0);
	j = 0;
      }
    }
    if (j) {
      if (j != last_size) halt("Last size error in save_binary");
      send_field(buf, j*sizeof(stored_field), 0);
    }
    free( buf );
  }

  g_sync();
}

