#include <stdio.h>
#include <string.h>
#include <stdlib.h>

#include "charm++.h"
#include "util.h"
#include "jacobi.h"

int M; // readonly variable. All global variables must be readonly. 

#include "jacobi.def.h"

main::main(CkArgMsg *m)
{ 
  M = 6;
  A = CProxy_jacobi::ckNew(M); // 1-D decomposition: array of 5 cells
  CkStartQD(EntryIndex(main,allDone,CkQdMsg), &thishandle);
  CkPrintf("main done\n");
};

void main::allDone(CkQdMsg *m)
{
  CkPrintf("Quiescence detected. stopping\n");
  CkExit();
};

jacobi::jacobi()
{ 
 CkPrintf("[%d]: Element %d created\n", CkMyPe(), thisIndex); 
 int i = 10;
 int j= 100;
 data = initializeCell(thisIndex, 10, 100); // each cell 10x100

 buf1 = new char[200*sizeof(double)];
 buf2 = new char[200*sizeof(double)];

 iterations = 0;
 sendToNeighbors();
};

void callfunc(jacobi *op)
{
   op->borderValues();
}

void jacobi::resumeFromWait()
{
   borderValues();
}

void jacobi::borderValues()
{
 CkPrintf("[%d] A[%d] at step %d: received message from nbr\n", CkMyPe(), thisIndex, iterations);

 copyBorder(data, (double *) buf1, 1);
 copyBorder(data, (double *) buf2, 0);

 if (++iterations < 3)
   {
     nextIteration(data);

     sendToNeighbors();
   }
};

void jacobi::sendToNeighbors()
{
 CkPrintf("[%d] A[%d]: sending to %d and %d\n", CkMyPe(), thisIndex,
	   (thisIndex+1)%M, (M+thisIndex-1)%M );
 CProxy_jacobi B(thisArrayID);

 int size = 200*sizeof(double);

 double *buf = new double[200];
 fillBorder(data, buf, 1);
 isend(buf, 200, CMPI_DOUBLE_PRECISION, (thisIndex-1+M)%M, 1, iterations);
 
 fillBorder(data, buf, 0);
 isend(buf, 200, CMPI_DOUBLE_PRECISION, (thisIndex+1)%M, 0, iterations);

 delete buf;
 
 irecv(buf1, 200, CMPI_DOUBLE_PRECISION, (thisIndex+1)%M, 1, iterations);

 irecv(buf2, 200, CMPI_DOUBLE_PRECISION, (thisIndex-1+M)%M, 0, iterations);

// iwaitAll( (recvCallBack)callfunc, this, iterations);
 iwaitAll(iterations);
};


void jacobi::pup(PUP::er &p)
{
  receiver::pup(p);
  // no need to pack buf1, buf2
  p(iterations);
  if (p.isUnpacking()) data = new CellData;
  p((char *)data, sizeof(CellData));
  if (p.isPacking()) delete data;
}


// for migration, need to write pack interface.
