/*
** Filename: FillDb.c
**
** Author: Bettina Kemme
**
** Date: 29.7.99
**
** Description: Fill the replicated database
** at the moment: bookstore database of guido
**
*/

// i/o
#include <stdio.h>

// JM Milan
#include <string.h>

// system imports
#include <sys/time.h>
#include <sys/utsname.h>

// simulation package import
//#include <csim.h>

// ensemble imports
#include "hot_sys.h"
#include "hot_error.h"
#include "hot_msg.h"
#include "hot_ens.h"

// common declarations
#include "declarations.h"
#include "vars.h"

//semaphore
#include "semaphore.h"

//threads
#include "threads.h"

// boolean consts
#define false 0;
#define true 1;

static long INTERVAL=0;       // time between pairs of fifo/total messages
static int TXNNUMBER = 100;   // number of messages sent
static int MESS_DISC=0;       // prefix of messages not considered in stats
int hb_rate = 1000;

static ncasts = 0;        // number of multicasts received

enum kind_of_multicast {TOTAL, FIFO};

hot_gctx_t contexts[2]; //contexts for the total[0] and fifo[1] groups

int complete[2];        // [i]==true when the membership of group i is complete
int my_rank[2];         // member id on each group
char my_name[100];      // member endpoint

typedef long msg_array[3000];
typedef msg_array order_array[2];
order_array order;      // array to measuare the disordering
long indice[2];          // index to the next message to insert
hot_uint32 seqno=0;     // next message id to send

int sender;             // true if sender_thread started
char *query_res;        // transaction result (protected by the above sem


// state associated to group contexts
typedef struct {
  int magic ;
  hot_gctx_t gctx;                  // group context
  hot_view_state_t vs;              // current view
  hot_ens_JoinOps_t jops;           // parameters
  enum kind_of_multicast mcast_kind;// kind of group
} context_state ;


/******** Multicast operations **********/
void 
MulticastCommit(char* writeset, hot_uint32 tid) {
  // TO DO: check writeset type
  hot_err_t err;
  hot_msg_t msg;
  hot_uint32 type=COMMIT;
  int i;

  // create msg
  msg = hot_msg_Create();
 
  // fill the msg in
  // writeset
  err=hot_msg_Write(msg, writeset, strlen(writeset));
  if (err != HOT_OK)
    hot_sys_Panic(hot_err_ErrString(err));
  // tid
  err = hot_msg_WriteUint32(msg, &tid);
  if (err != HOT_OK)
    hot_sys_Panic(hot_err_ErrString(err));
  //type
  err=hot_msg_WriteUint32(msg, &type);
  if (err != HOT_OK)
    hot_sys_Panic(hot_err_ErrString(err));
 
  printf("casting commit message\n"); 
  // multicast the message
  err = hot_ens_Cast(contexts[FIFO], msg, NULL);
 
  // free msg resources  
  err = hot_msg_Release(&msg);
  if (err != HOT_OK)
    hot_sys_Panic(hot_err_ErrString(err));
} // MulticastCommit

void 
SendReply(hot_endpt_t endpoint, char* result) {
  hot_err_t err;
  hot_msg_t msg;
  hot_uint32 type=REPLY;

  // create msg
  msg = hot_msg_Create();
 
  // fill the msg in
  // result
  err=hot_msg_Write(msg, result, strlen(result));
  if (err != HOT_OK)
    hot_sys_Panic(hot_err_ErrString(err));
  //type
  err=hot_msg_WriteUint32(msg, &type);
  if (err != HOT_OK)
    hot_sys_Panic(hot_err_ErrString(err));
  
  // send the message
  err = hot_ens_Send(contexts[TOTAL], &endpoint, msg, NULL);

  // free msg resources  
  err = hot_msg_Release(&msg);
  if (err != HOT_OK)
    hot_sys_Panic(hot_err_ErrString(err));
} // SendReply


/******** Group Communication Callbacks *******/
void exit_cb(
        hot_gctx_t gctx,
        void *env
) {}

void block(
        hot_gctx_t gctx,
	void *env)
{}


void receive_cast(
        hot_gctx_t gctx,
	void *env,
	hot_endpt_t *origin, 
	hot_msg_t msg) {

  hot_err_t err;
  char contents [MAX_TXT];         // message contents
  unsigned pos;                
  T_Mess message;
  int i;
  hot_uint32 u;
  char buf[MAX_TXT];
  // dcadena 
  hot_uint32 thId;

  printf("message received -----------------------------\n");
  // Process msg

  // dcadena. Read the thread id
  err = hot_msg_ReadUint32(msg, &thId);

  // Get message type
  err= hot_msg_ReadUint32(msg, &message.type);
  printf("type = %d\n", message.type);
  if (err != HOT_OK)
     hot_sys_Panic(hot_err_ErrString(err));
  if (message.type == OPT) {
    // noCC
    err= hot_msg_ReadUint32(msg, &message.opt.trans.noCC);
    printf("noCC = %d\n", message.opt.trans.noCC);
    if (err != HOT_OK)
      hot_sys_Panic(hot_err_ErrString(err));
    // transCC
    for (i=0;i<message.opt.trans.noCC;i++) {
      err= hot_msg_ReadUint32(msg, &message.opt.trans.transCC[i]);
      printf("transCC[%d] = %d\n", i, message.opt.trans.transCC[i]);
      if (err != HOT_OK)
        hot_sys_Panic(hot_err_ErrString(err));
    }
    // tid
    err= hot_msg_ReadUint32(msg, &message.opt.trans.tid);
    printf("tid = %d\n", message.opt.trans.tid);
    if (err != HOT_OK)
      hot_sys_Panic(hot_err_ErrString(err));
    strcpy(message.opt.trans.endpoint.name, origin->name);
    printf("endpoint = %s\n", message.opt.trans.endpoint.name);
  } // if OPT
  if (message.type == TO) {
    // tid
    err= hot_msg_ReadUint32(msg, &message.to.tid);
    printf("tid = %d\n", message.to.tid);
    if (err != HOT_OK)
      hot_sys_Panic(hot_err_ErrString(err));
    strcpy(message.to.endpoint.name, origin->name);
    printf("endpoint = %s\n", message.to.endpoint.name);
    MulticastCommit("write set", message.to.tid);
    strcpy(buf, "reply message");
    SendReply(*origin, buf);
  } //if TO
  if (message.type == COMMIT) {
    // tid
    err= hot_msg_ReadUint32(msg, &message.commit.tid);
    printf("tid = %d\n", message.commit.tid);
    if (err != HOT_OK)
      hot_sys_Panic(hot_err_ErrString(err));
    strcpy(message.commit.endpoint.name, origin->name);
    printf("endpoint = %s\n", message.commit.endpoint.name);
 } //if COMMIT
} // receive_cast

void receive_send(
        hot_gctx_t gctx,
	void *env,
	hot_endpt_t *origin,
	hot_msg_t msg) 
{}

void accepted_view(
        hot_gctx_t gctx,
	void *env,
	hot_view_state_t *view_state) 
{
  context_state *s = (context_state*) env ;
  int size, i ;
  long disorder;

  // Release old view
  if (s->vs.members != NULL)
    free(s->vs.members) ;
  s->vs = *view_state; 
  size = sizeof(s->vs.members[0])*s->vs.nmembers ;
  s->vs.members = (hot_endpt_t*) malloc(size) ;
  memcpy(s->vs.members,view_state->members,size) ;

  
  //  if (s->vs.rank == 0)
  printf("HOT_TEST:view (nmembers=%d, rank=%d)\n",s->vs.nmembers,s->vs.rank);


  printf("\tview_id = (%d,%s)\n", view_state->view_id.ltime,
	 view_state->view_id.coord.name);
  //  printf("\tversion = \"%s\"\n", view_state->version);
  printf("\tgroup_name = \"%s\"\n", view_state->group_name);
  printf("\tprotocol = \"%s\"\n", view_state->protocol);
  printf("\tmy_rank = %d\n", 	view_state->rank);
  //  printf("\tgroup daemon is %s\n", view_state->groupd ? "in use" : "not in use");
  //  printf("\tparameters = \"%s\"\n", view_state->params);
  //  printf("\txfer_view = %d\n", view_state->xfer_view);
  //  printf("\tprimary = %d\n", view_state->primary);
  
  for (i = 0; i < view_state->nmembers; i++)
    printf("%s\n", view_state->members[i].name);
  

  if (s->vs.nmembers == MEMBERS)
  {
    // the membership of this group is complete
    complete[s-> mcast_kind]= true;
    // get the rank in the group
    my_rank[s->mcast_kind]= s-> vs.rank;
    if (s->mcast_kind == TOTAL)
      MY_ID = my_rank[TOTAL];
  }

  if (complete[0] && complete[1] && (s->vs.nmembers < MEMBERS))
  {
     printf("View shrinking, exiting\n");
     hot_ens_Leave(gctx);
  }
} // accepted_view


void sender_thread(context_state *s) {

} // sender_thread


void heartbeat(
        hot_gctx_t gctx,
	void *env,
	unsigned time) 
{
  context_state *s = (context_state*) env ;

  // When the memberships on both groups are complete
  //   start a sender thread in the first half of the members
  if (s->vs.nmembers == MEMBERS)
  {
    if  (complete[0] && complete[1] && !sender) {
       printf("Sender\n");
       //hot_thread_Create((void*)sender_thread, s, 0);
       createThread((void*)sender_thread, s, NULL);
       sender= true;
    }
  }
} // hearbeat

/******** Join ********************************/
void join(enum kind_of_multicast prop) {
  hot_err_t err ;
  context_state *s ;
  char *outboard ;
  static struct utsname mname;
  s = (context_state *) malloc(sizeof(*s)) ;
  memset(s,0,sizeof(*s)) ;
  
  s->jops.heartbeat_rate = hb_rate;
  s->jops.argv = NULL; //argv;
  strcpy(s->jops.transports, "DEERING");
  if (prop==TOTAL)
    strcpy(s->jops.group_name, "HOT1");
  else
    strcpy(s->jops.group_name, "HOT2");
  if (prop == TOTAL)
     strcpy(s->jops.properties, "Gmp:Sync:Total:Heal:Frag:Flow:Slander:Suspect");
  else
     strcpy(s->jops.properties, "Gmp:Sync:Heal:Frag:Flow");
  s->jops.use_properties = 1;
  strcpy(s->jops.params, "suspect_max_idle=3:int;suspect_sweep=1.000:time");
  s->jops.groupd = 0;
  
  s->jops.conf.receive_cast = receive_cast;
  s->jops.conf.receive_send = receive_send;
  s->jops.conf.accepted_view = accepted_view;
  s->jops.conf.heartbeat = heartbeat;
  s->jops.conf.exit = exit_cb;
  s->jops.conf.block = block;
  s->jops.debug = 0 ;
  s->jops.client = 0;

  // clients are name with C followed by the node number
  // only one client per node is allowed
  uname(&mname);
  strcpy(my_name, "R");
  strcat(my_name, strtok(mname.nodename, "."));
  strcpy(s-> jops.endpt.name, my_name);

  outboard = (char*) getenv("ENS_OUTBOARD_TYPE") ;
  if (!outboard)
    outboard = "FORK" ;
  strcpy(s->jops.outboard, outboard);

  s->jops.env = s;

  s->mcast_kind = prop;  

  // Join the group
  err = hot_ens_Join(&s->jops, &s->gctx);
  if (err != HOT_OK)
    hot_sys_Panic(hot_err_ErrString(err));

  // Record the group context
  if (prop == TOTAL)
    contexts[0] = s->gctx;
  else
    contexts[1] = s->gctx;
}




void sim()
{
}


int 
main(int argc, char *argv[]) {
  semaphore block_main_sem; // semaphore to block the main program

  
  // At the beginning none of the groups has completed its membership
  complete[0]= false;
  complete[1]= false;

  // Join both groups for fifo and total order
  join(FIFO);
  join(TOTAL) ;

  // Check argument number
  if (argc != 3) {
    printf(
	   "usage:\n %s Number_of_replicas Number_of_CC_per_replica\n", 
	   argv[0]);
    exit(1);
  }

  // get the value of parameters and check correctness
  MEMBERS = atoi(argv[1]);
  NUM_LOCAL_CC = atoi(argv[2]);
  NUM_TOTAL_CC = NUM_LOCAL_CC*MEMBERS;
  printf("Run a set of %d replicas\n", MEMBERS);

  sim();

  // The main program blocks waiting for the rest
  //   of the group members to finish
  //hot_sema_Create(0,&block_main_sem) ;
  createSemaphore(0, &block_main_sem);
  //hot_sema_Dec(block_main_sem) ;
  decSemaphore(&block_main_sem);

  return 0 ;
}
