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

#include "charm++.h"

#include "receiver.h"


receiver::receiver(ArrayElementCreateMessage *m) : ArrayElement(m)
{
  msgTbl = CmmNew();
  reqTbl = CmmNew();

  callback = NULL;
  counter = -1;

  finishConstruction();
}

receiver::receiver(ArrayElementMigrateMessage *m) : ArrayElement(m)
{
  msgTbl = CmmNew();
  reqTbl = CmmNew();

  callback = NULL;
  counter = -1;

  finishMigration();
}

receiver::~receiver()
{
  CmmFree(msgTbl);
  CmmFree(reqTbl);
}

#define MIN(a,b) (a)<(b)?(a):(b)

void receiver::sendTo(int tag, char *pointer, int size, int from, int refno)
{
  int tags[3], ret_tags[3];

//CkPrintf("Sendto (msgTbl): tag:%d, from:%d, refno:%d size:%d. \n", tag, from, refno,size);
  tags[0] = tag; tags[1] = from; tags[2] = refno;
  tblEntry *req = (tblEntry *)CmmGet(reqTbl, 3, tags, ret_tags);

  if (req) {
    //  irecv called before; copy buffer
    memcpy(req->buf, pointer, MIN(size, req->size)); 
    delete req;

    recvAlready();
  }
  else {
    // msg come before irecv called
    tags[0] = tag; tags[1] = from; tags[2] = refno;
    req = new tblEntry;
    req->buf = new char[size];
    memcpy(req->buf, pointer, size); 
    req->size = size;
    CmmPut(msgTbl, 3, tags, req);
  }

}

void receiver::generic(receiverMsg *msg)
{
  sendTo(msg->tag, msg->buf, msg->size, msg->sendFrom, msg->refno);
  delete msg;
}

static int typesize(int type, int count)
{
  switch(type) {
    case CMPI_DOUBLE_PRECISION : return count*sizeof(double);
    case CMPI_INTEGER : return count*sizeof(int);
    case CMPI_REAL : return count*sizeof(float);
    case CMPI_COMPLEX: return 2*count*sizeof(double);
    case CMPI_LOGICAL: return 2*count*sizeof(int);
    case CMPI_CHARACTER:
    case CMPI_BYTE:
    case CMPI_PACKED:
    default:
      return 2*count;
  }
}

void receiver::send(void *buf, int size, int dest, int tag, int refno)
{
// int size = typesize(datatype, count);
 receiverMsg * d = new (&size, 0) receiverMsg;
 d->tag = tag;
 d->sendFrom = thisIndex;
 d->refno = refno;
 memcpy(d->buf, buf, size);
 CProxy_receiver B(thisAID);
 B[dest].generic(d);
}

void receiver::irecv(void *buf, int size, int source, int tag, int refno)
{
  int tags[3], ret_tags[3];
//  int size = typesize(datatype, count);

  tags[0] = tag; tags[1] = source; tags[2] = refno;
  tblEntry *req = (tblEntry *)CmmGet(msgTbl, 3, tags, ret_tags);

  if (req) {
    // send called before; copy buffer into
    memcpy(buf, req->buf, MIN(size, req->size));
    delete [] req->buf;
    delete req;
  }
  else {
//CkPrintf("irecv (reqtbl): tag:%d, senderTag:%d, refno:%d. \n", tag, senderTag, refno);
   // recv called before send
    tags[0] = tag; tags[1] = source; tags[2] = refno;
    req = new tblEntry;
    req->buf = (char *)buf;
    req->size = size;
    CmmPut(reqTbl, 3, tags, req);
  }
}

void receiver::iwaitAll(recvCallBack f, void *data, int ref)
{
  if (callback != NULL) 
  {
    CkPrintf("iwaitAll wrong!\n");
    CkExit();
  }
  callback = f;
  this->cb_data = data;
  counter = ref;

  recvAlready();
}

void receiver::recvAlready()
{
   if (callback == NULL) return;

   int tags[3], ret_tags[3];
   tags[0] = CmmWildCard; tags[1] = CmmWildCard; tags[2] = counter;
   tblEntry *req1 = (tblEntry *)CmmProbe(reqTbl, 3, tags, ret_tags);
   tblEntry *req2 = (tblEntry *)CmmProbe(msgTbl, 3, tags, ret_tags);
   if (req1 == NULL && req2 == NULL && callback != NULL) 
   {
      CProxy_receiver B(thisAID);
      B[thisIndex].ready2go();
//CkPrintf("[%d] ready to go on %d\n", thisIndex, counter);
   }
}

void receiver::ready2go()
{
    if (callback == NULL) CkPrintf("Fatal error in Call back on [%d]. \n", thisIndex);

    recvCallBack tmpfn = callback;
    callback = NULL;
    tmpfn(cb_data);
}

#include "receiver.def.h"


