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

#include "charm++.h"
#include <converse.h>
#include "commlib.h"

#include "pgm.h"
#include "pgm.def.h"

#define BROADCAST_SIZE 2

CkGroupID node_thisgroup;

main::main(CkArgMsg *m)
{
  if (BROADCAST_SIZE > CkNumPes()) 
     CmiAbort("Num of Pes must greater than 1.\n");
  node_thisgroup = CProxy_Node::ckNew();
  CProxy_Node(node_thisgroup).start();
};


Node::Node()
{
  msgCount = 0;
  Count = 0;
}

void Node::start(void)
{

  ComlibInit();
  if (CkMyPe() == 0)
  {
  comID id=ComlibInstance(GRID, CmiNumPes());
  CommStartMsg *m = new CommStartMsg;
  m->id = id;
  CProxy_Node(node_thisgroup).GetCommID(m);
  CkStartQD(CProxy_Node::ckIdx_allWork((CkQdMsg*)0),&thishandle);
  }
}

void Node::GetCommID(CommStartMsg *msg)
{
  commID = msg->id;
//  displayComID();
}

void Node::allWork(CkQdMsg* m)
{
  CProxy_Node(node_thisgroup).broadcast();
}

void Node::broadcast(void)
{
  //broadcast
  CkPrintf("[%d] WORK\n", CkMyPe());
//  displayComID();
//  if (CkMyPe() == 1)
  {
  int i;
  int c = BROADCAST_SIZE; // CkNumPes();
  int *pelist = new int[c];
  for (i=0; i<c; i++) pelist[i] = i;
  int ep = CProxy_Node::ckIdx_recvMsg((broadcastMsg *)0);
  broadcastMsg *msg = new broadcastMsg;
  msg->dummy = new int [3];
  msg->n = 3;
  for (i=0; i<msg->n; i++) msg->dummy[i] = i;
//CkPrintf("[%d] CComlibEachToManyMulticast called. \n", CkMyPe());
  CComlibEachToManyMulticast(commID, ep, msg, thisgroup, c ,pelist);
//  CProxy_Node(node_thisgroup).recvMsg(msg);
  }
}

void Node::recvMsg(broadcastMsg *m)
{
CkPrintf("[%d] recvMsg: count=%d %d %d %d.\n", CkMyPe(), msgCount, m->dummy[0], m->dummy[1], m->dummy[2]);
//  displayComID();
  msgCount ++;
  if (msgCount == BROADCAST_SIZE)
  {
  notifyMsg *msg = new notifyMsg;
  CProxy_Node(node_thisgroup).notifyOk(msg, 0);
  }
  delete m;
}

void Node::notifyOk(notifyMsg *m)
{
  delete m;
  Count++;
  CkPrintf("[%d] notify:%d\n", CkMyPe(), Count);
  if (Count == BROADCAST_SIZE) {
    CkPrintf("Program done.\n");
    CkExit();
  }
}

void Node::displayComID()
{
  CkPrintf("comm:srcpe:%d ImplType:%d, ImplIndex:%d, SwitchVal:%d, NumMembers:%d grp:%d\n", commID.srcpe, commID.ImplType, commID.ImplIndex, commID.SwitchVal, commID.NumMembers, commID.grp);
}


void * broadcastMsg::pack(broadcastMsg *msg)
{
  int packmsg_size = msg->n * sizeof(int) + sizeof(broadcastMsg);
  char *packmsg_buf = (char *)CkAllocBuffer(msg,packmsg_size);
  *((broadcastMsg*)packmsg_buf) = *msg;
  int *_data = (int*)(packmsg_buf+sizeof(broadcastMsg));
  memcpy(_data, msg->dummy, msg->n * sizeof(int));
  delete msg;
  return packmsg_buf;
}

broadcastMsg * broadcastMsg::unpack(void *ptr)
{
  void *_ptr = CkAllocBuffer(ptr, sizeof(broadcastMsg));
  broadcastMsg* m = new (_ptr) broadcastMsg;
  char *buffer = (char*)ptr;
  *m = *((broadcastMsg*)buffer);
  int *_data = (int*)(buffer+sizeof(broadcastMsg));
  m->dummy = (int *)malloc(m->n*sizeof(int));
  memcpy(m->dummy, _data, m->n*sizeof(int));
  CkFreeMsg(ptr);
  return m;
}
