/****************************************************************
 * This routine copies 'latfield'-distributed data from all nodes
 * to node zero.  It copies a box determined by coordinates
 * xmin and xmax.  The target field t must be already allocated
 */

#include "comdefs.h"
#include "generic.h"

void copy_lat_data_to_node( void *dat, int dsize, 
			    int xmin[NDIM], int xmax[NDIM], void *t, int target_node )
{
  int i,d,n,ok,idx;
  int x[NDIM],xsiz[NDIM],nmin[NDIM],nmax[NDIM],vmin[NDIM],vmax[NDIM],siz;
  extern node_struct *allnodes;  /* defined in layout.c */
  node_struct *np;
  char *src, *trg;


  foralldir(d) {
    if (xmin[d] < 0 || xmax[d] > lattice.size[d] || xmin[d] > xmax[d])
      halt("Block size error in copy_lat_data");
  }

  foralldir(d) xsiz[d] = xmax[d] - xmin[d] + 1;

  /* Go through the nodes */
  for (n=0; n<number_of_nodes; n++) {
    if (n == this_node || this_node == target_node) {
      np = &allnodes[n];
      /* check if this node is included */
      ok = siz = 1;
      foralldir(d) {
	nmin[d] = np->xmin[d];  
	nmax[d] = np->xmin[d] + np->nodesize[d] - 1;
	vmin[d] = greater(nmin[d],xmin[d]);
	vmax[d] = smaller(nmax[d],xmax[d]);
	ok = ok && (vmax[d] - vmin[d] >= 0);
	siz = siz * (vmax[d] - vmin[d] + 1);
      }

      /* printf("NODE %d, size %d, OK %d\n",n,siz,ok);
       */
      if (ok) {
	/* Now is included */
	char * cp = NULL;
	if (n != target_node) cp = (char *)memalloc(siz,dsize);
	  
	if (this_node == target_node && n != target_node) {
	  /* node target sends ack to n, and receives */
	  send_field( &siz, sizeof(int), n );
	  receive_field( cp, siz*dsize );
	}
	 
	/* copy data */
	foralldir(d) x[d] = vmin[d];
	x[0]--;  /* need to subtract 1 from 1st to make the addition */
	for (i=0; i<siz; i++) {
	  /* add to coordinate */
	  d=0; while (++x[d] > vmax[d]) { x[d] = vmin[d]; d++; }

	  /* copy from cp if we're node 0 receiving, else dat */
	  if (this_node == target_node && n != target_node) src = cp + i*dsize;
	  else src = ((char *)dat) + node_index(x,np)*dsize;
  
	  /* copy to t in node target, else to cp */
	  if (this_node != target_node) trg = cp + i*dsize;
	  else {
	    idx = 0;
	    for(d=NDIM-1; d>=0; d--) idx = x[d]-xmin[d] + idx*xsiz[d];
	    trg = ((char *)t) + idx*dsize;
	  }

	  memcpy( trg, src, dsize );
	}

	if (this_node != target_node) {
	  /* receive ack, and send the stuff */
	  receive_field( &i, sizeof(int) );
	  if (i != siz) halt(" copy_lat_data siz error");
	  send_field( cp, siz*dsize, target_node );
	}

	if (n != target_node) free( cp );
      } 
    } /* if this_node == target || n */
  } /* loop over nodes */

  g_sync();
}


void copy_lat_slice(void *dat, int dsize, int dir, int slice, void *t)
{
  int d,x1[NDIM], x2[NDIM];
  
  foralldir(d) {
    if (d != dir) {
      x1[d] = 0;
      x2[d] = lattice.size[d];
    } else x1[d] = x2[d] = slice;
  }
  
  copy_lat_data_to_node( dat, dsize, x1, x2, *t, 0 );
}

