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

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

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

#include "pingpong.def.h"

main::main(CkArgMsg *m)
{ 
  M = 2;
  A = CProxy_pingpong::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();
};

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

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

 iterations = 0;
 sendToNeighbors();
};

void pingpong::resumeFromWait()
{
 if (iterations < 29)
   {
     sleep(1);
     iterations ++;
     sendToNeighbors();
   }
};

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

 if (thisIndex == 0) {
     if (iterations % 2) {
     CkPrintf("[%d] A[0] sent to 1 in iteration:%d\n", CkMyPe(), iterations);
     isend(buf1, bufsize, CMPI_DOUBLE_PRECISION, 1, 1, iterations);
     resumeFromWait();
     }
     else {
     CkPrintf("[%d] A[0] recv from 1 in iteration:%d\n", CkMyPe(), iterations);
     irecv(buf2, bufsize, CMPI_DOUBLE_PRECISION, 1, 1, iterations);
     iwaitAll(iterations);
     }
 }
 else {
     if (iterations % 2) {
     CkPrintf("[%d] A[1] recv from 0 in iteration:%d\n", CkMyPe(), iterations);
     irecv(buf2, bufsize, CMPI_DOUBLE_PRECISION, 0, 1, iterations);
     iwaitAll(iterations);
     }
     else {
     CkPrintf("[%d] A[1] sent to 0 in iteration:%d\n", CkMyPe(), iterations);
     isend(buf1, bufsize, CMPI_DOUBLE_PRECISION, 0, 1, iterations);
     resumeFromWait();
     }
 }
 
};

void pingpong::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.
