/*
 * Copyright (C) 2008 The Trustees of Columbia University in the City of New York. 
 * All rights reserved.
 *
 * See the file LICENSE included in this distribution for details.
 */

#include "msgheader.h"

#ifdef _WENGO
#include "p2pp/common/trace.h"
#include "p2pp/net/socketi.h"
#include "p2pp/sys/gettimeofday.h"
#endif

#ifndef _WENGO
#include "trace.h"
#include "socketi.h"
#include "gettimeofday.h"
#endif

////////////////////////////////////////////////////////////////
//                      COMMON HEADER
////////////////////////////////////////////////////////////////

common_hdr::common_hdr() {
  version = 1;
  msgtype = eREQUEST;
  ack = 0;
  porc = 0;
  rori = 0;
  respcode = 0;
  reqoritype = 0;
  ttl = 255;
}//common_hdr
  

int common_hdr::parse(uchar* buf, int size, int& cur) {
  int tlen;
  
  //version
  version = buf[0] >> 6;
  
  //request type
  msgtype = (buf[0] << 2 ) >> 6;

  //ACK
  ack     = (buf[0] << 4) >> 7;

  //peer or client
  porc = (buf[0] << 5 ) >> 7;

  //recursive or iterative
  rori = (buf[0] << 6) >> 7;

  TRACE(2, "P2PP(Header(Version=%d; Msg-type=%s; ACK=%d; Peerorclient=%d; Recursive/Iterative=%d; ", version, pmsgtype[msgtype], ack, porc, rori);

  //response-code
  respcode = 0;
  if(eRESPONSE == msgtype || eRESPONSEACK == msgtype) {
    respcode = (buf[0] << 7);
    respcode = respcode << 1;
    respcode |= buf[1];

    TRACE(2, "Response-code=%d; ", respcode);
  }
  
  //request type
  reqoritype = buf[2];
  TRACE(2, "Request-type=%s; ", preqoritype[reqoritype]);
  
  //ttl
  ttl = buf[3];
  TRACE(2, "TTL=%d; ", ttl);

  //magic cookie
  magic_cookie = ntohl(*((uint32*)(buf+4)));
  TRACE(2, "Magic cookie=0x%x; ", magic_cookie);

  //transaction Id
  transid = ntohl(*((uint32*)(buf+8)));
  TRACE(2, "Transaction-Id=0x%x; ", transid);
  
  //length
  length = ntohl(*((uint32*)(buf+12)));
  TRACE(2, "Length=%u; ", length);

  cur=16;

  tlen=4;
  if(reqoritype != eBOOTSTRAP && reqoritype != eGETDIAGNOSTICS) {
    tlen=idlen;
  }

  //HACK -- needs correction
  if( (1==porc && reqoritype != eBOOTSTRAP) || (0==porc && reqoritype != eBOOTSTRAP) ) {
    tlen = idlen;
  }

  memcpy(srcid, (buf+cur), tlen);
  slen=tlen;
  cur+=tlen;
  
  TRACE(2, "SrcId=0x", "");
  for(int i=0; i<tlen; i++) {
    TRACE(2, "%02x", srcid[i]);
  }//end for
  TRACE(2, "; ", "");
  

  if(msgtype == eRESPONSE || eRESPONSEACK == msgtype) {    

    if(reqoritype == eGETDIAGNOSTICS) {
      tlen = idlen;
    }
    memcpy(respid, (buf+cur), tlen);
    rlen=tlen;
    cur+=tlen;

    TRACE(2, "DstId=0x", "");
    for(int i=0; i<tlen; i++) {
      TRACE(2, "%02x", respid[i]);
    }//end for
    TRACE(2, " ", "");
  }  
  
  TRACE(2, ") \n", "");
  return cur;
  
}//common_hdr::parse



/*
magic cookie
transaction-id
length

0 failed
1 success
*/

int common_hdr::encode(uchar* buf, int size, int& cur) {

  uint16 respcodeT=0;
  
  buf[0] = version << 6;
  buf[0] |= msgtype << 4;
  buf[0] |= ack << 3;
  buf[0] |= porc << 2;
  buf[0] |= rori << 1;
  
  //response Code
  if(eRESPONSE == msgtype || eRESPONSEACK == msgtype) {
    respcodeT=respcode;
  }
  buf[0] |= ((respcodeT << 7) >> 15);
  buf[1] = (respcodeT);
  
  //request type
  buf[2] = reqoritype;
  
  //TTL
  buf[3] = ttl;

  //magic cookie
  magic_cookie=0x596ABF0D;
  //1500167949

  for(int j=4, i=24;i>=0 && j<=7; i-=8, j++) {
    buf[j] = magic_cookie >> i;
  }
  
  //Transaction-ID
  for(int j=8, i=24;i>=0 && j<=11; i-=8, j++) {
    buf[j] = transid >> i;
  }

  //Length  
  for(int j=12, i=24;i>=0 && j<=15; i-=8, j++) {
    buf[j] = length >> i;
  }

  cur=16;

  //copy srcId
  memcpy( (buf+cur), srcid, slen);
  cur+=slen;

  //If response
  if(eRESPONSE == msgtype || eRESPONSEACK == msgtype) {
    memcpy( (buf+cur), respid, rlen);
    cur+=rlen;
  }

  return 1;
  
}//common_hdr::encode  


////////////////////////////////////////////////////////////////
//                        OBJECT HEADER
////////////////////////////////////////////////////////////////


int obj_hdr::encode(uchar* buf, int size, int& cur) {

  //object type
  buf[cur] = objtype;

  //A and B
  buf[cur+1]  = A << 7;
  buf[cur+1] |= B << 6;
  
  //Length
  buf[cur+2] = length >> 8;
  buf[cur+3] = length;
  
  cur = cur+4;    
  
  return cur;
}//obj_hdr::encode


int obj_hdr::parse(uchar* buf, int size, int& cur) {

  //parse object type
  objtype = buf[cur];

  //parse A and B
  A = buf[cur+1] >> 7;
  B = (buf[cur+1] << 1) >> 7;
   
  //parse length
  length = buf[cur+2] << 8;
  length |= buf[cur+3];
  
  cur = cur+4;    

  return cur;
  
}//obj_hdr::parse



////////////////////////////////////////////////////////////////
//                PEER-INFO AND RELATED OBJECTS               
////////////////////////////////////////////////////////////////


resourcelist::resourcelist() {
  num=0;
}//resourcelist


int resourcelist::parse(uchar* buf, int length, int& cur) {

  int32 i=1;
  TRACE(2, "ResourceList=", "");

  //number of resources
  num = buf[cur];
  
  //for loop
  for(i=1; i<num; i++) {
    lrestype.push_back( buf[cur+i] );
  }

  //update current pointer
  cur+=i;

  return 1;
}//resourcelist::parse


int resourcelist::encode(uchar* buf, int length, int& cur) {

  list<uchar>::iterator itr;
  int i=1;

  //encode the object header
  stobj.length=lrestype.size()+1;
  stobj.encode(buf, length, cur);

  //num
  buf[cur] = num;

  for(itr=lrestype.begin(); itr!=lrestype.end(); itr++) {
    buf[cur+i] = *itr;
    i++;
  }
  
  //update the current pointer
  cur=cur+i;

  return 1;
}//resourcelist::encode


uptime::uptime() {
  utime=0;
}//uptime
  
int uptime::parse(uchar* buf, int length, int& cur) {
  utime=ntohl(*((uint32*)(buf+cur)));

  TRACE(2, "Uptime=0x%x; ", utime);

  //update current pointer
  cur+=4;
  
  return 1;
    
}//uptime::parse
  
  
int uptime::encode(uchar* buf, int length, int& cur) {
  //encode the object header
  stobj.length=4;    
  stobj.encode(buf, length, cur);
    
  //the time
  buf[cur] = utime >> 24;
  buf[cur+1] = utime >> 16;
  buf[cur+2] = utime >> 8;
  buf[cur+3] = utime;
  
  //update the current pointer
  cur+=4;
  
  return 1;
}//uptime::encode  



rtt::rtt() {
  utime=0;
}//rtt
  
int rtt::parse(uchar* buf, int length, int& cur) {
  utime=ntohl(*((uint32*)(buf+cur)));
    
  TRACE(2, "RTT(utime=0x%x ms; )", utime);

  //update current pointer
  cur+=4;
  
  return 1;
    
}//rtt::parse
  
  
int rtt::encode(uchar* buf, int length, int& cur) {
  //encode the object header
  stobj.length=4;    
  stobj.encode(buf, length, cur);
    
  //the time
  buf[cur] = utime >> 24;
  buf[cur+1] = utime >> 16;
  buf[cur+2] = utime >> 8;
  buf[cur+3] = utime;
  
  //update the current pointer
  cur+=4;
  
  return 1;
  
}//rtt::encode  



bw::bw() {
  sbw=0;
  rbw=0;
  ismach=0;
}//bw
  
int bw::parse(uchar* buf, int length, int& cur) {
  //bandwidth is for program or machine
  ismach=buf[cur];
  cur++;

  sbw=ntohl(*((uint32*)(buf+cur)));
  rbw=ntohl(*((uint32*)(buf+cur+4)));

  TRACE(2, "BW(ismach %d SBW=%u B/s; RBW=%u B/s); ", ismach, sbw, rbw);

  //update current pointer
  cur+=8;
  
  return 1;
    
}//bw::parse
  
  
int bw::encode(uchar* buf, int length, int& cur) {
  //encode the object header
  stobj.length=4;    
  stobj.encode(buf, length, cur);
    
  //ismach
  buf[cur]=ismach;
  cur++;

  //the send bw
  buf[cur] = sbw >> 24;
  buf[cur+1] = sbw >> 16;
  buf[cur+2] = sbw >> 8;
  buf[cur+3] = sbw;

  cur+=4;

  //the receive bw
  buf[cur] = rbw >> 24;
  buf[cur+1] = rbw >> 16;
  buf[cur+2] = rbw >> 8;
  buf[cur+3] = rbw;

  //update the current pointer
  cur+=4;
  
  return 1;
}//bw::encode

cpuutil::cpuutil() {
  cpuusage=0;
  ismach=0;
}//cpuutil
  
int cpuutil::parse(uchar* buf, int length, int& cur) {
  //cpuutil is for program or machine. 0 is for program, 1 for machine
  ismach=buf[cur];
  cur++;

  cpuusage=ntohs(*((uint16*)(buf+cur)));

  TRACE(2, "CPUUSAGE(ismach %d, usage=%u %); ", ismach, cpuusage);

  //update current pointer
  cur+=2;
  
  return 1;
    
}//cpuutil::parse
  
  
int cpuutil::encode(uchar* buf, int length, int& cur) {
  //encode the object header
  stobj.length=2;    
  stobj.encode(buf, length, cur);

  //ismach
  buf[cur]=ismach;
  cur++;

  //the cpu usage
  buf[cur] = cpuusage >> 8;
  buf[cur+1] = cpuusage;

  cur+=2;

  return 1;
}//cpuutil::encode


memory::memory() {
  mem=0;
  ismach=0;
}//memory
  
int memory::parse(uchar* buf, int length, int& cur) {
  //memory is for program or machine. 0 is for program, 1 for machine
  ismach=buf[cur];
  cur++;

  mem=ntohl(*((uint32*)(buf+cur)));

  TRACE(2, "MEMORY(ismach: %d, mem=%u bytes); ", ismach, mem);

  //update current pointer
  cur+=4;
  
  return 1;
    
}//memory::parse
  
  
int memory::encode(uchar* buf, int length, int& cur) {
  //encode the object header
  stobj.length=4;    
  stobj.encode(buf, length, cur);

  //ismach
  buf[cur]=ismach;
  cur++;
    
  //the memory
  buf[cur] = mem >> 24;
  buf[cur+1] = mem >> 16;
  buf[cur+2] = mem >> 8;
  buf[cur+3] = mem;

  //update the current pointer
  cur+=4;
  
  return 1;
}//memory::encode



unhashedid::unhashedid() {
  isPresent=0;
}
  
int unhashedid::parse(uchar* buf, int length, int& cur) {
  int i=0;

  //TODO: update length
  if(stobj.length > HASHLEN) {
    return 0;
  }
  
  TRACE(2,"Unhashed-Id=","");

  //copy the unhashedid into buf
  memcpy(unhashed, (buf+cur), stobj.length);

  for(i=0; i<stobj.length;i++) {
    TRACE(2, "%c",unhashed[i]);
  }
  //TRACE(2, "; ", "");

  //terminate with a null
  unhashed[i]='\0';

  //update buf
  cur += stobj.length;

  return 1;
  
}//unhashedid::parse
  
  
int unhashedid::encode(uchar* buf, int length, int& cur) {

  //encode the object header
  stobj.encode(buf, length, cur);
  
  //copy the unhashed Id
  memcpy( (buf+cur), unhashed, stobj.length);
  
  //Update length
  cur += stobj.length;
  
  return 1;
  
}//unhashed::encode  




addressinfo::addressinfo() {
  
}

int addressinfo::parse(uchar* buf, int length, int& cur) {
  struct in_addr in;
  int hosttype=0;

  //number of host-type, IP address, port tuples
  num = buf[cur];
  cur++;

  TRACE(2, "Address-Info(Num=%d; ", num);
  
  for(int i=0; i<num; i++) {
    
    
    R[i]          = buf[cur]>>7;         //include relay address and port
    ipver[i]      = (buf[cur]<<4) >> 4;  //ipversion
    foundation[i] = buf[cur+1];          //foundation
    compid[i]     = buf[cur+2];          //component id
    priority[i]   = ntohl(*((uint32*)(buf+cur+3)));  //priority
    tt[i]         = (buf[cur+7]>>4);     //transport type
    ht[i]         = 0x0F & buf[cur+7]; //(buf[cur+7]<<4) >>4;  //host type
    hosttype = (int)ht[i];
    port[i] = ntohs( *((uint16*)(buf+cur+8)) ); //port

    in.s_addr = *( (uint32*)(buf+cur+10)); //address
    strncpy(addr[i], inet_ntoa(in), 16);
    
    
    if(0==R[i]) {
      //printf("SAL HOST TYPE: %d %s IP: %s\n", hosttype, (char*)paddrtype[hosttype], addr[i] );

      TRACE(2, "[R=%d; Foundation=%d; Component-ID=%d; Priority=%u; HT=%s; TT=%d; Port: %d; Peer-address=%s;]  ", R[i], foundation[i], compid[i], priority[i], (char*)paddrtype[ht[i]], tt[i], port[i], addr[i]);

      cur+=14;
    } else {
      TRACE(2, "[R=%d; Foundation=%d; Component-ID=%d; Priority=%u; HT=%s; TT=%d; Port=%d; Peer-address=%s;  R-port=%d; R-address=%s;] ", R[i], foundation[i], compid[i], priority[i], (char*)paddrtype[ht[i]], tt[i], port[i], addr[i], rport[i], raddr[i]);

      cur+=20;
    }

  }//end for
  TRACE(2, ")", "");

  return 1;
  
}//addressinfo::parse
  

  
int addressinfo::encode(uchar* buf, int length, int& cur) {
  int temp1, temp2, j=0;
  struct in_addr inp;

  temp1=cur;
  stobj.encode(buf, length, cur);
  temp2=cur;
  
  buf[cur] = num;
  cur++;
  
  for(int i=0; i<num; i++) {
    
    buf[cur]    = R[i]<<7;           //relay address and port
    buf[cur]   |= ipver[i];          //ipversion
    buf[cur+1]  = foundation[i];     //foundation
    buf[cur+2]  = compid[i];         //component id

    //priority
    buf[cur+3]  = priority[i]>>24;
    buf[cur+4]  = (priority[i]<< 8) >> 24;
    buf[cur+5]  = (priority[i]<<16) >> 24;
    buf[cur+6]  = (priority[i]<<24) >> 24;

    buf[cur+7]  =  tt[i]<<4;         //transport type
    buf[cur+7]  |=  ht[i];         //host type

    buf[cur+8]  = port[i]>>8;        //port
    buf[cur+9] = (port[i]<<8)>>8;

    //address
    inet_aton(addr[i], &inp);
    
    buf[cur+10] = inp.s_addr;
    buf[cur+11] = inp.s_addr >> 8;
    buf[cur+12] = inp.s_addr >> 16;
    buf[cur+13] = inp.s_addr >> 24;

    cur+=14;

    //encode relay address and port
    if(1==R[i]) {
      //port
      buf[cur+14] = rport[i]>>8;
      buf[cur+15] = (rport[i]<<8)>>8;

      //address
      inet_aton(raddr[i], &inp);
    
      buf[cur+16] = inp.s_addr;
      buf[cur+17] = inp.s_addr >> 8;
      buf[cur+18] = inp.s_addr >> 16;
      buf[cur+19] = inp.s_addr >> 24;

      j+=6;

    }//end if
    
  }//end for    
  
  
  //update lengt
  stobj.length = cur - temp2;
  stobj.encode(buf, length, temp1);
  
  return 1;
  
}//addressinfo::encode



peerid::peerid() {

}

int peerid::parse(uchar* buf, int length, int& cur) {

  //TODO: update length
  if(stobj.length > HASHLEN) {
    return 0;
  }  

  TRACE(2, "Peer-Id=0x", "");

  memcpy(id, (const void*)(buf+cur), stobj.length);

  for(int i=0; i<stobj.length; i++) {
    TRACE(2, "%02x", id[i]);
  }
  //TRACE(2, "; ", "");

  //update buf
  cur += stobj.length;

  return 1;
  
}//parse


int peerid::encode(uchar* buf, int length, int& cur) {

  //encode the object header
  stobj.encode(buf, length, cur);

  //copy the peer id
  memcpy( (buf+cur), id, stobj.length);
  
  //Update length
  cur += stobj.length;

  return 1;
  
}//peerid::encode


peerinfo::peerinfo() {  
  //idlen=10;
}//peerinfo::peerinfo  


void peerinfo::Initialize() {
  isPresent=0;
  stpeerid.isPresent=0;
  stunhashed.isPresent=0;
  stuptime.isPresent=0;
  staddr.isPresent=0;
  staddr.num=0;
  strtt.isPresent=0;
  strtt.utime=0;

  //memset(stunhashed.unhashed, 0, HASHLEN);
}//Initialize


int peerinfo::encode(uchar* buf, int length, int& cur) {
  int temp1, temp2;
  
  temp1 = cur;
  stobj.encode(buf, length, cur);
  temp2 = cur;
  
  //Peer-Id
  if(stpeerid.isPresent) {
    //printf("encoding cur=%d, peerid len: %d\n", cur, stpeerid.stobj.length);
    stpeerid.stobj.objtype = ePEERID;
    stpeerid.encode(buf, length, cur);
  }
  
  
  //Unhashed-Id
  if(stunhashed.isPresent) {
    //printf("encoding unhashedid\n");
    stunhashed.stobj.objtype = eUNHASHEDID;
    stunhashed.encode(buf, length, cur);
  }
  
  
  //Uptime
  if(stuptime.isPresent) {
    //printf("encoding uptime\n");
    stuptime.stobj.objtype = eUPTIME;
    stuptime.encode(buf, length, cur);
  }
  
  //Address-Info
  if(staddr.isPresent) {
    //printf("encoding addressinfo\n");
    staddr.stobj.objtype = eADDRESSINFO;
    staddr.encode(buf, length, cur);
  }

  
  //RTT
  if(strtt.isPresent) {
    //printf("encoding addressinfo\n");
    strtt.stobj.objtype = eRTT;
    strtt.encode(buf, length, cur);
  }
  
  
  stobj.length = cur - temp2;
  stobj.encode(buf, length, temp1);

  //printf("encode object length is: %d\n", stobj.length);
  
  return 1;
}//peerinfo::encode

  

int peerinfo::parse(uchar* buf, int length, int& cur) {

  int curlen=0;
  struct obj_hdr stobj_hdr;
  
  //The total length of this object from current buffer position
  //excluding header
  curlen = cur+stobj.length;

  TRACE(2, "PEER-INFO( LEN=%d; ", stobj.length);

  while(curlen > cur) {
    
    //parse object header
    stobj_hdr.parse(buf, length, cur);
    
    switch(stobj_hdr.objtype) {
      
    case ePEERID:
      stpeerid.isPresent = 1;
      stpeerid.stobj = stobj_hdr;
      stpeerid.parse(buf, length, cur);
      break;
      
    case eUNHASHEDID:
      stunhashed.isPresent = 1;
      stunhashed.stobj = stobj_hdr;
      stunhashed.parse(buf, length, cur);
      break;          
      
    case eUPTIME:
      stuptime.isPresent = 1;
      stuptime.stobj = stobj_hdr;
      stuptime.parse(buf, length, cur);
      break;
      
    case eADDRESSINFO:
      staddr.isPresent = 1;
      staddr.stobj = stobj_hdr;
      staddr.parse(buf, length, cur);
      break;

    case eRTT:
      strtt.isPresent = 1;
      strtt.stobj = stobj_hdr;
      strtt.parse(buf, length, cur);
      break;
      
    default:        
      break;
    }//end switch    


    TRACE(2, "; ", "");

  }//end while

  TRACE(2, ")", "");

  return 1;
  
}//peerinfo::parse


////////////////////////////////////////////////////////////////
//                     REQUEST-OPTIONS
////////////////////////////////////////////////////////////////

requestoptions::requestoptions() {
  
}//requestoptions


int requestoptions::parse(uchar* buf, int length, int& cur) {
  
  TRACE(2, "REQUEST-OPTIONS( ", "");  
  
  p=buf[cur]>>7;     //primary parallel lookups
  r=buf[cur]>>6;     //request routing table
  n=buf[cur]>>5;     //request neighbor table
  e=buf[cur]>>4;     //in-seperate-request
  a=buf[cur]>>3;     //partial reply
  s=buf[cur]>>2;     //immediate neighbors of JP
  l=buf[cur]>>1;     //route-log

  buf[cur+1]=buf[cur+2]=buf[cur+3]=0;

  TRACE(2, "P=%d; R=%d; N=%d; E=%d; A=%d; S=%d; L=%d;)", p, r, e, a, s, l);

  cur+=4;
    
  return 1;
}//requestoptions::parse


int requestoptions::encode(uchar* buf, int length, int& cur) {
  //encode the object header
  stobj.length=4;    
  stobj.encode(buf, length, cur);

  buf[cur]  = p<<7;
  buf[cur] |= r<<6;
  buf[cur] |= n<<5;
  buf[cur] |= e<<4;
  buf[cur] |= a<<3;
  buf[cur] |= s<<2;
  buf[cur] |= l<<1;  

  return 1;
}//requestoptions::encode




////////////////////////////////////////////////////////////////
//                     DIAGNOSTICS-OPTIONS
////////////////////////////////////////////////////////////////

diagnosticsoptions::diagnosticsoptions() {
  
}//diagnosticsoptions


int diagnosticsoptions::parse(uchar* buf, int length, int& cur) {
  
  TRACE(2, "DIAGNOSTICS-OPTIONS( ", "");  
  
  r=buf[cur]>>7;     //request routing table
  n=buf[cur]>>6;     //request neighbor table
  e=buf[cur]>>5;     //request resource table
  bwf=buf[cur]>>4;   //send bw
  rl=buf[cur]>>3;    //request relay list 
  ta=buf[cur]>>2;    //request TURN allocations

  buf[cur+1]=buf[cur+2]=buf[cur+3]=0;

  TRACE(2, "R=%d; N=%d; E=%d; BW=%d; RL=%d; TA=%d)", r, n, e, bwf, rl, ta);

  cur+=4;
    
  return 1;
}//diagnosticsoptions::parse


int diagnosticsoptions::encode(uchar* buf, int length, int& cur) {
  //encode the object header
  stobj.length=4;    
  stobj.encode(buf, length, cur);

  buf[cur]  = r<<7;
  buf[cur] |= n<<6;
  buf[cur] |= e<<5;
  buf[cur] |= bwf<<4;
  buf[cur] |= rl<<3;
  buf[cur] |= ta<<2;

  cur+=4;

  return 1;
}//diagnosticsoptions::encode




/*
peerinfo& peerinfo::operator=(const peerinfo& lstpeer) {

  isPresent = lstpeer.isPresent;
  stobj = lstpeer.stobj;
  idlen = lstpeer.idlen;

  memcpy((void*)peerid, (const void*)lstpeer.peerid, idlen);    
  
  printf("operator = %s\n", lstpeer.peerid);

  
  //stunhashed = lstpeer.stunhashed;
  //stuptime = lstpeer.stuptime;
  //staddr = lstpeer.staddr;
  

  return *this;
  
}//peerinfo::operator=


peerinfo::peerinfo(const peerinfo& lstpeer) {

  isPresent = lstpeer.isPresent;
  stobj = lstpeer.stobj;
  idlen = lstpeer.idlen;

  memcpy((void*)peerid, (const void*)lstpeer.peerid, idlen);  
  
  printf("copy constructor %s\n", lstpeer.peerid);

//stunhashed = lstpeer.stunhashed;
// stuptime = lstpeer.stuptime;
// staddr = lstpeer.staddr;
  
  
}//peerinfo::peerinfo(const peerinfo& lstpeer)
*/


////////////////////////////////////////////////////////////////
//                        PLOOKUP
////////////////////////////////////////////////////////////////


plookup::plookup() {
  
}

int plookup::parse(uchar* buf, int length, int& cur) {
  
  num = buf[cur] >> 2;
  E = (buf[cur] << 6) >> 7;
  R = (buf[cur] << 7) >> 7;
  cur+=1;
  
  TRACE(2, "PLOOKUP(num=%d; E=%d; R=%d; ", num, E, R);

  //parse object header
  stida.stobj.parse(buf, length, cur);
  stida.isPresent = 1;  
  stida.parse(buf, length, cur);

  if(1==R) {
    stidb.parse(buf, length, cur);
  }

  TRACE(2, ")", "");
  
  return 1;
}//plookup::parse


int plookup::encode(uchar* buf, int length, int& cur) {

  int temp1, temp2;

  temp1=cur;
  stobj.encode(buf, length, cur);
  temp2=cur;

  //encode num and R
  buf[cur] = num<<2;
  buf[cur] |= E<<1; //exact
  buf[cur] |= R;    //range
  cur+=1;

  //encode a
  stida.stobj.objtype = ePEERID;
  stida.encode(buf, length, cur);

  //encode b
  if(1==R) {
    stidb.stobj.objtype = ePEERID;
    stidb.encode(buf, length, cur);
  }

  stobj.length = cur - temp2;
  stobj.encode(buf, length, temp1);

  return 1;

}//plookup::encode


////////////////////////////////////////////////////////////////
//                        EXPIRES
////////////////////////////////////////////////////////////////

expires::expires() {
  utime=0;
}//expires
  
int expires::parse(uchar* buf, int length, int& cur) {
  utime=ntohl(*((uint32*)(buf+cur)));
    
  TRACE(2, "EXPIRES(utime=0x%x; )", utime);

  //update current pointer
  cur+=4;
  
  return 1;
    
}//expires::parse
  
  
int expires::encode(uchar* buf, int length, int& cur) {
  //encode the object header
  stobj.length=4;    
  stobj.encode(buf, length, cur);
    
  //the time
  buf[cur] = utime >> 24;
  buf[cur+1] = utime >> 16;
  buf[cur+2] = utime >> 8;
  buf[cur+3] = utime;
  
  //update the current pointer
  cur+=4;
  
  return 1;
}//expires::encode



/////////////////////////////////////////////////////////////
//                        P2P-OPTIONS
////////////////////////////////////////////////////////////////

p2poptions::p2poptions() {
  p2palg=0; 
  halg=0;
  halglen=0; 
  hbase=0;
  oidlen=0;  
}//p2poptions::p2ptopions


int p2poptions::parse(uchar* buf, int size, int& cur) {

  //halg = (buf[cur] << 2) >> 2;
  
  halg    = buf[cur];
  halglen = buf[cur+1];  
  p2palg  = buf[cur+2];
  hbase   = buf[cur+3];
  oidlen  = buf[cur+4];

  //overlay id
  memcpy(oid, (const void*)(buf+cur+5), oidlen);
    
  cur += 5+oidlen;

  TRACE(2, "P2P-Options( Hash-Algorithm=%s; Hash-Algorithm-Len=%d; P2P-Algorithm=%s; Hash-base=%d; OID-len=%d; Oid=0x", phalg[halg], halglen, pp2palg[p2palg], hbase, oidlen);


  for(int i=0; i<(int32)oidlen; i++) {
    TRACE(2, "%x", oid[i]);
  }
  TRACE(2, ")", "");


  return 1;
}//p2poptions::parse


int p2poptions::encode(uchar* buf, int size, int& cur) {

  //encode object header
  stobj.length=5+oidlen;
  stobj.encode(buf, size, cur);

  buf[cur] = halg;
  buf[cur+1] = halglen;
  buf[cur+2] = p2palg;
  buf[cur+3] = hbase;  
  buf[cur+4] = oidlen;

  memcpy((void*)(buf+cur+5), oid, oidlen);

  cur += 5+oidlen;

  return 1;
}//p2poptions::encode



////////////////////////////////////////////////////////////////
//                       ROUTING TABLE
////////////////////////////////////////////////////////////////

routingtable::routingtable() {
  
}

int routingtable::parse(uchar* buf, int size, int& cur) {
  int length;
  struct peerinfo tp;
  
  length = stobj.length+cur;

  num = buf[cur];
  cur+=1;
  

  TRACE(2, "ROUTINGTABLE(Num=%d; ", num);

  while( length > cur) {

    TRACE(2, "\n        ", "");

    //zero object header and peerinfo structures
    struct peerinfo lstpeer;
    
    //parse the object header
    lstpeer.stobj.parse(buf, size, cur);
    lstpeer.isPresent = 1;
    
    //parse the object now
    lstpeer.parse(buf, size, cur);

    //add it to the table
    table.push_back(lstpeer);

    //printf("push on the table\n");

  }//end while
  
  TRACE(2, "\n    )","");


  return 1;
}//routingtable::parse


int routingtable::encode(uchar* buf, int size, int& cur) {
  
  list<peerinfo>::iterator itr;
  struct peerinfo lstpeer;
  int temp1, temp2;

  temp1=cur;
  stobj.encode(buf, size, cur);
  temp2 = cur;

  //get number of entries
  num = table.size();
  buf[cur] = num;  

  cur+=1;
  
  for(itr=table.begin(); itr!=table.end(); itr++) {

    (*itr).encode(buf, size, cur);
    
  }//end for

  temp2 = cur - temp2;
  stobj.length = temp2;
  stobj.encode(buf, size, temp1);

  return 1;
}//routingtable::encode



////////////////////////////////////////////////////////////////
//                       NEIGHBOR TABLE
////////////////////////////////////////////////////////////////

neighbortable::neighbortable() {
  
}

int neighbortable::parse(uchar* buf, int size, int& cur) {
  int length;
  struct obj_hdr lstobj_hdr;
  struct peerinfo lstpeer;

  
  length = stobj.length+cur;

  num = buf[cur];
  cur+=1;

  TRACE(2, "NEIGHBORTABLE(Num=%d; ", num);

  while( length > cur) {

    TRACE(2, "\n        ", "");

    //zero object header and peerinfo structures
    memset((void*)&lstobj_hdr, 0, sizeof(lstobj_hdr));
    memset((void*)&lstpeer, 0, sizeof(lstpeer));
    
    //parse the object header
    lstobj_hdr.parse(buf, size, cur);    
    lstpeer.stobj = lstobj_hdr;
    lstpeer.isPresent = 1;

    //parse the object now
    lstpeer.parse(buf, size, cur);
    
    //add it to the table
    table.push_back(lstpeer);    
    

  }//end while
  
  TRACE(2, "\n    )","");

  return 1;
}//neighbortable::parse



int neighbortable::encode(uchar* buf, int size, int& cur) {
  
  list<peerinfo>::iterator itr;
  struct peerinfo lstpeer;
  int temp1, temp2;

  temp1=cur;
  stobj.encode(buf, size, cur);

  //get number of entries
  num = table.size();
  buf[cur] = num;
  temp2 = cur;

  cur+=1;

  
  for(itr=table.begin(); itr!=table.end(); itr++) {
    lstpeer = *itr;
    lstpeer.encode(buf, size, cur);
  }
  

  temp2 = cur - temp2;
  stobj.length = temp2;
  stobj.encode(buf, size, temp1);

  return 1;
}//neighbortable::encode


////////////////////////////////////////////////////////////////
//        RESOURCE OBJECT AND RELATED TLVs
////////////////////////////////////////////////////////////////

resourceid::resourceid() {

}

int resourceid::parse(uchar* buf, int length, int& cur) {

  //TODO: update length
  if(stobj.length > HASHLEN) {
    return 0;
  }

  TRACE(2, "Resource-ID=0x", "");

  memcpy(id, (const void*)(buf+cur), stobj.length);

  for(int i=0; i<stobj.length; i++) {
    TRACE(2, "%x", id[i]);
  }

  //update buf
  cur += stobj.length;

  return 1;
  
}//resourceid::parse


int resourceid::encode(uchar* buf, int length, int& cur) {

  //encode the object header
  stobj.encode(buf, length, cur);
  
  //copy the resource id
  memcpy( (buf+cur), id, stobj.length);
  
  //Update length
  cur += stobj.length;

  return 1;
  
}//resourceid::encode



owner::owner() {

}//owner

int owner::parse(uchar* buf, int size, int& cur) {

  return 1;
}//owner::parse


int owner::encode(uchar* buf, int size, int& cur) {

  return 1;
}//owner::encode

rlookup::rlookup() {
  
}


int rlookup::parse(uchar* buf, int length, int& cur) {
  
  int curlen=0;
  struct obj_hdr stobj_hdr;
  
  TRACE(2, "RLOOKUP(", "");

  conttype = buf[cur];
  subtype  = buf[cur+1];
  cur+=2;

  curlen = cur+stobj.length - 2;
  
  while(curlen > cur) {
    //parse object header
    stobj_hdr.parse(buf, length, cur);
    

    switch(stobj_hdr.objtype) {

    case eRESOURCEID:
      stresid.isPresent = 1;
      stresid.stobj = stobj_hdr;
      stresid.parse(buf, length, cur);
      break;
      
    case eOWNER:
      stowner.isPresent = 1;
      stowner.stobj = stobj_hdr;
      stowner.parse(buf, length, cur);
      break;

    case ePEERINFO:
      stpeerinfo.isPresent = 1;
      stpeerinfo.stobj = stobj_hdr;
      stpeerinfo.parse(buf, length, cur);
      break;

    case eUNHASHEDID:
      stunhashedid.isPresent = 1;
      stunhashedid.stobj = stobj_hdr;
      stunhashedid.parse(buf, length, cur);
      break;

    default:
      break;
    }

    TRACE(2, "; ", "");
  }//end while

  
  TRACE(2, ")", "");
  
  return 1;
}//rlookup::parse


int rlookup::encode(uchar* buf, int length, int& cur) {
  int temp1, temp2;
    
  temp1 = cur;
  stobj.encode(buf, length, cur);
  temp2 = cur;
  
  buf[cur]=conttype;
  buf[cur+1]=subtype;
  cur+=2;

  //Resource-Id
  if(stresid.isPresent) {
    stresid.stobj.objtype = eRESOURCEID;
    stresid.encode(buf, length, cur);
  } else {
    TRACE(1, "rlookup::parse...Cannot find ResourceId\n", "");
    return 0;    
  }

  //Owner
  if(stowner.isPresent) {
    stowner.stobj.objtype = eOWNER;
    stowner.encode(buf, length, cur);
  }

  //peerinfo
  if(stpeerinfo.isPresent) {
    stpeerinfo.stobj.objtype = ePEERINFO;
    stpeerinfo.encode(buf, length, cur);
  }

  //unhashedid
  if(stunhashedid.isPresent) {
    stunhashedid.stobj.objtype = eUNHASHEDID;
    stunhashedid.encode(buf, length, cur);
  }

  temp2 = cur - temp2;
  stobj.length = temp2;
  stobj.encode(buf, length, temp1);

  return 1;

}//rlookup::encode





resourceobjectvalue::resourceobjectvalue() {
  isPresent=0;
}
  
int resourceobjectvalue::parse(uchar* buf, int length, int& cur) {
  int i=0;

  //TODO: update length
  if(stobj.length > HASHLEN) {
    return 0;
  }
  
  TRACE(2,"ResourceObjectValue=","");

  //copy the resourceobjectvalue into buf
  memcpy(value, (buf+cur), stobj.length);

  for(i=0; i<stobj.length;i++) {
    TRACE(2, "%c",value[i]);
  }
  //TRACE(2, "; ", "");

  //terminate with a null
  value[i]='\0';

  //update buf
  cur += stobj.length;

  return 1;
  
}//resourceobjectvalue::parse
  
  
int resourceobjectvalue::encode(uchar* buf, int length, int& cur) {

  //encode the object header
  stobj.encode(buf, length, cur);
  
  //copy the unhashed Id
  memcpy( (buf+cur), value, stobj.length);
  
  //Update length
  cur += stobj.length;
  
  return 1;
  
}//resourceobjectvalue::encode



resourceobject::resourceobject() {
  isrbuf=0;
  isPresent=0;
}//resourceobject


void resourceobject::Initialize() {

  stresid.isPresent=0;;
  stowner.isPresent=0;;
  stpeerinfo.isPresent=0;;  
  stunhashedid.isPresent=0;;
  stsignature.isPresent=0;;
  stexpires.isPresent=0;;
  stx509.isPresent=0;;
  strov.isPresent=0;;
  staddr.isPresent=0;

}//Initialize


int resourceobject::parse(uchar* buf, int length, int& cur) {
  
  int curlen=0;
  int tcur=0;
  struct obj_hdr stobj_hdr;
  
  TRACE(2, "RESOURCEOBJECT(", "");

  //pointer to the start of buffer object
  tcur=cur;

  conttype = buf[cur];
  subtype  = buf[cur+1];
  cur+=2;

  TRACE(2, "CONTENT=%s; ", pcontent_type[conttype]);

  curlen = cur+stobj.length - 2;
  
  while(curlen > cur) {
    //parse object header
    stobj_hdr.parse(buf, length, cur);
    
    switch(stobj_hdr.objtype) {

    case eRESOURCEID:
      stresid.isPresent = 1;
      stresid.stobj = stobj_hdr;
      stresid.parse(buf, length, cur);
      break;
      
    case eOWNER:
      stowner.isPresent = 1;
      stowner.stobj = stobj_hdr;
      stowner.parse(buf, length, cur);
      break;

    case ePEERINFO:
      stpeerinfo.isPresent = 1;
      stpeerinfo.stobj = stobj_hdr;
      stpeerinfo.parse(buf, length, cur);
      break;

    case eUNHASHEDID:
      stunhashedid.isPresent = 1;
      stunhashedid.stobj = stobj_hdr;
      stunhashedid.parse(buf, length, cur);
      break;

    case eEXPIRES:
      stexpires.isPresent = 1;
      stexpires.stobj = stobj_hdr;
      stexpires.parse(buf, length, cur);
      break;

    case eX509:
      stx509.isPresent = 1;
      stx509.stobj = stobj_hdr;
      stx509.parse(buf, length, cur);
      break;

    case eSIGNATURE:
      stsignature.isPresent = 1;
      stsignature.stobj = stobj_hdr;
      stsignature.parse(buf, length, cur);
      break;

    case eRESOURCEOBJECTVALUE:
      strov.isPresent = 1;
      strov.stobj = stobj_hdr;
      strov.parse(buf, length, cur);
      break;

    case eADDRESSINFO:
      staddr.isPresent =1;
      staddr.stobj = stobj_hdr;
      staddr.parse(buf, length, cur);
      break;

    default:
      break;
    }    

    TRACE(2, "; ", "");
  }//end while

  TRACE(2, "); ", "");

  //copy the raw resource object in the buffer. I did not use the
  //resource-object length stored in object header because a size of
  //the object received maybe less than the size indicated in object
  //header length variable. This happens due to UDP MTU issues.
  memcpy( rbuf, (buf+(tcur-4)), (cur-tcur+4));
  
  return 1;

}//resourceobject::parse


int resourceobject::encode(uchar* buf, int length, int& cur) {
  int temp1, temp2;  

  //Check if flag was set to encode raw buffer
  if(1 == isrbuf) {
    stobj.length = rbuflen - 4;
    stobj.encode(buf, length, cur);
    memcpy( (buf+cur), rbuf, rbuflen-4);

    printf("premature return ============\n");
    return 1;    
  }//end if
  
  
  temp1 = cur;
  stobj.encode(buf, length, cur);
  temp2 = cur;
  
  buf[cur]=conttype;
  buf[cur+1]=subtype;
  cur+=2;


  //Resource-Id
  if(stresid.isPresent) {
    stresid.stobj.objtype = eRESOURCEID;
    stresid.encode(buf, length, cur);
  } else {    
    TRACE(1, "resourceobject::encode...Cannot find ResourceId\n", "");
    return 0;    
  }

  //Owner
  if(stowner.isPresent) {
    stowner.stobj.objtype = eOWNER;
    stowner.encode(buf, length, cur);
  }

  //peerinfo
  if(stpeerinfo.isPresent) {
    stpeerinfo.stobj.objtype = ePEERINFO;
    stpeerinfo.encode(buf, length, cur);
  }

  //unhashedid
  if(stunhashedid.isPresent) {
    stunhashedid.stobj.objtype = eUNHASHEDID;
    stunhashedid.encode(buf, length, cur);
  }

  //signature
  if(stsignature.isPresent) {
    stsignature.stobj.objtype = eSIGNATURE;
    stsignature.encode(buf, length, cur);
  }

  //expires
  if(stexpires.isPresent) {
    stexpires.stobj.objtype = eEXPIRES;
    stexpires.encode(buf, length, cur);
  }

  //x509
  if(stx509.isPresent) {
    stx509.stobj.objtype = eX509;
    stx509.encode(buf, length, cur);
  }

  //resourceobjectvalue
  if(strov.isPresent) {
    strov.stobj.objtype = eRESOURCEOBJECTVALUE;
    strov.encode(buf, length, cur);
  }

  //staddressinfo
  if(staddr.isPresent) {
    staddr.stobj.objtype = eADDRESSINFO;
    staddr.encode(buf, length, cur);
  }

  temp2 = cur - temp2;
  stobj.length = temp2;
  stobj.encode(buf, length, temp1);

  return 1;

}//resourceobject::encode




////////////////////////////////////////////////////////////////
//                        SECURITY
////////////////////////////////////////////////////////////////

csr::csr() {

}

int csr::parse(uchar* buf, int length, int& cur) {

  //TODO: update length
  if(stobj.length > CERTLEN) {
    return 0;
  }

  TRACE(2, "CSR(0x", "");

  memcpy(certreq, (const void*)(buf+cur), stobj.length);

  for(int i=0; i<stobj.length; i++) {
    TRACE(2, "%x", certreq[i]);
  }
  TRACE(2, ") ", "");

  //update buf
  cur += stobj.length;

  return 1;
  
}//csr::parse


int csr::encode(uchar* buf, int length, int& cur) {

  //encode the object header
  stobj.encode(buf, length, cur);
  
  //copy the peer id
  memcpy( (buf+cur), certreq, stobj.length);
  
  //Update length
  cur += stobj.length;

  return 1;
  
}//csr::encode



x509::x509() {

}

int x509::parse(uchar* buf, int length, int& cur) {

  //TODO: update length
  if(stobj.length > CERTLEN) {
    return 0;
  }

  TRACE(2, "X509(0x", "");

  memcpy(cert, (const void*)(buf+cur), stobj.length);

  for(int i=0; i<stobj.length; i++) {
    TRACE(2, "%x", cert[i]);
  }
  TRACE(2, ") ", "");

  //update buf
  cur += stobj.length;

  return 1;
  
}//x509::parse


int x509::encode(uchar* buf, int length, int& cur) {

  //encode the object header
  stobj.encode(buf, length, cur);
  
  //copy the peer id
  memcpy( (buf+cur), cert, stobj.length);
  
  //Update length
  cur += stobj.length;

  return 1;
  
}//x509::encode




signature::signature() {

}

int signature::parse(uchar* buf, int length, int& cur) {

  //TODO: update length
  if(stobj.length > CERTLEN) {
    return 0;
  }

  TRACE(2, "CSR(0x", "");

  memcpy(sign, (const void*)(buf+cur), stobj.length);

  for(int i=0; i<stobj.length; i++) {
    TRACE(2, "%x", sign[i]);
  }
  TRACE(2, ") ", "");

  //update buf
  cur += stobj.length;

  return 1;
  
}//signature::parse


int signature::encode(uchar* buf, int length, int& cur) {

  //encode the object header
  stobj.encode(buf, length, cur);
  
  //copy the peer id
  memcpy( (buf+cur), sign, stobj.length);
  
  //Update length
  cur += stobj.length;

  return 1;
  
}//signature::encode

////////////////////////////////////////////////////////////////
//                TURNALLOC
////////////////////////////////////////////////////////////////
turnalloc::turnalloc() {
}

int turnalloc::parse(uchar* buf, int size, int& cur) {
  struct obj_hdr stobj_hdr;

  //parse port
  relayport=ntohs( *( (uint16*)(buf+cur)) );
  cur+=2;

  //parse bandwidth
  stobj_hdr.parse(buf, size, cur);
  stbw.isPresent=1;
  stbw.stobj=stobj_hdr;
  stbw.parse(buf, size, cur);

  //parse addressinfo
  stobj_hdr.parse(buf, size, cur);
  staddr.isPresent=1;
  staddr.stobj=stobj_hdr;

  staddr.parse(buf, size, cur);
  
  return 1;
}

int turnalloc::encode(uchar* buf, int size, int& cur) {
  int32 temp1, temp2;

  temp1=cur;
  stobj.encode(buf, size, cur);
  temp2=cur;

  //encode relayport
  buf[cur]=(relayport>>8);
  buf[cur+1]=(relayport<<8)>>8;

  cur+=2;

  //If bandwith field is present
  if(stbw.isPresent) {
    stbw.stobj.objtype = eBW;
    stbw.encode(buf, size, cur);
  }

  if(staddr.isPresent) {
    staddr.stobj.objtype = eADDRESSINFO;
    staddr.encode(buf, size, cur);
  }

  stobj.length=cur-temp2;
  stobj.encode(buf, size, temp1);
  
  return 1;
}

////////////////////////////////////////////////////////////////
//                 DIAGNOSTICS
////////////////////////////////////////////////////////////////
diagnosticsobject::diagnosticsobject() {
}

int diagnosticsobject::parse(uchar* buf, int size, int& cur) {

  struct obj_hdr stobj_hdr;
  int32 curlen;
  struct resourceobject stro;
  struct turnalloc stturnalloc;
  struct peerinfo stp;
  struct cpuutil stcpu;

  TRACE(2, "DIAGNOSTICS(", "");

  curlen = cur+stobj.length;
  
  while(curlen > cur) {
    //parse object header
    stobj_hdr.parse(buf, size, cur);
    
    switch(stobj_hdr.objtype) {

    case eNEIGHBORTABLE:
      stnt.stobj=stobj_hdr;
      stnt.parse(buf, size, cur);
      break;

    case eROUTINGTABLE:
      strt.stobj=stobj_hdr;
      strt.parse(buf, size, cur);
      break;

    case eRESOURCEOBJECT:
      stro.Initialize();
      stro.stobj=stobj_hdr;
      stro.parse(buf, size, cur);
      lstro.push_back(stro);
      break;
      
    case eBW:
      stbw.sbw=stbw.rbw=0;
      stbw.isPresent=1;
      stbw.stobj=stobj_hdr;
      stbw.parse(buf, size, cur);
      break;

    case eCPUUTIL:
      stcpu.cpuusage=0;
      stcpu.isPresent=1;
      stcpu.stobj=stobj_hdr;
      stcpu.parse(buf, size, cur);
      
      //cpu utilization. 1==machine, 0==p2p program
      if(1==stcpu.ismach) {
        sttcpuutil=stcpu;
      } else {
        stcpuutil=stcpu;
      }
      break;

    case eTURNALLOC:
      stturnalloc.stobj=stobj_hdr;
      stturnalloc.isPresent=1;
      stturnalloc.parse(buf, size, cur);
      stlturnalloc.push_back(stturnalloc);
      break;

    case ePEERINFO:
      stp.stobj=stobj_hdr;
      stp.isPresent=1;
      stp.parse(buf, size, cur);
      stlrelaylist.push_back(stp);
      break;
      
    default:
      break;
    }//wnd switch

  }//end while
    
  TRACE(2, ") ", "");

  return 1;

}//parse


int diagnosticsobject::encode(uchar*buf, int size, int& cur) {
  int temp1, temp2;
  uchar flag[1000]={0};
  struct timeval curr_time;
  int32 c, count, num;
  list<struct resourceobject>::iterator i1;
  list<turnalloc>::iterator i2;
  list<peerinfo>::iterator i3;

  
  if(stnt.table.size() > 0 || strt.table.size() > 0 || lstro.size() > 0 || stbw.isPresent 
     || stlturnalloc.size() > 0) {

    temp1=cur;
    stobj.encode(buf, size, cur);
    temp2=cur;
    
    //If bandwith field is present
    if(stbw.isPresent) {
      stbw.stobj.objtype = eBW;
      stbw.encode(buf, size, cur);
    }

    //cpuutilization of the program
    if(stcpuutil.isPresent) {
      stcpuutil.stobj.objtype = eCPUUTIL;
      stcpuutil.encode(buf, size, cur);
    }

    //total machine utilization
    if(sttcpuutil.isPresent) {
      sttcpuutil.stobj.objtype = eCPUUTIL;
      sttcpuutil.encode(buf, size, cur);
    }

    //neighbor table
    if(stnt.table.size() > 0) {
      stnt.stobj.objtype = eNEIGHBORTABLE;
      stnt.encode(buf, size, cur);
    }

    //routing table
    if(strt.table.size() > 0) {
      strt.stobj.objtype = eROUTINGTABLE;
      strt.encode(buf, size, cur);
    }

    if(lstro.size() > 0) {

      //TEMP CODE BEGINS for random number generation
      //set the seed
      gettimeofday(&curr_time, NULL);
      srand(curr_time.tv_sec+curr_time.tv_usec);
      
      //set the array to 0
      for(int i=0; i<1000;i++) {
        flag[i]=0;
      }
      
      count=1;
      num=lstro.size();
      for(int i=0; i<num && i<1000 && count <= 15; i++) {
        if(num > 15) {
          do {
            c = (int) (rand() % num);
          } while (flag[c] != 0);
        } else {
          c=count-1;
        }
   
        flag[c]=1;
        count++;
      }//end for
  

      count=0;
      //TEMP CODE ENDS for random number generation


      for(i1=lstro.begin(); i1!=lstro.end(); i1++) {      
        //TEMP CODE BEGINS
        if(0 == flag[count++]) {
          continue;
        }
        //TEMP CODE ENDS

        (*i1).encode(buf, size, cur);

      }//end for
      
    }//end if lstro.size() > 0  

    if(stlturnalloc.size() > 0) {
      for(i2=stlturnalloc.begin(); i2!=stlturnalloc.end(); i2++) {
	(*i2).stobj.objtype = eTURNALLOC;
	(*i2).isPresent = 1;
	(*i2).encode(buf, size, cur);
      }
    }//end stturnalloc.ispresent

    if(stlrelaylist.size() > 0) {
      for(i3=stlrelaylist.begin(); i3!=stlrelaylist.end(); i3++) {
	(*i3).stobj.objtype = ePEERINFO;
	(*i3).isPresent = 1;
	(*i3).encode(buf, size, cur);
      }
    }//end if stlrelaylist

    stobj.length=cur-temp2;
    stobj.encode(buf, size, temp1);
  }

  return 1;
}//encode



////////////////////////////////////////////////////////////////
//                 NODEVAL
////////////////////////////////////////////////////////////////

nodeval::nodeval() {

}//nodeval


int nodeval::parse(uchar* buf, int size, int& cur) {

  struct obj_hdr stobj_hdr;
  struct bw tbw;
  struct cpuutil tcpuutil;
  struct memory tmem;
  int32 curlen;

  TRACE(2, "NODEVAL(", "");

  curlen = cur+stobj.length;
  
  while(curlen > cur) {
    //parse object header
    stobj_hdr.parse(buf, size, cur);
    
    switch(stobj_hdr.objtype) {

    case eBW:
      //reset the tbw struct because we use it for
      //both total machine bw and program bw
      memset(&tbw, 0, sizeof(tbw));
      
      tbw.isPresent=1;
      tbw.stobj=stobj_hdr;
      tbw.parse(buf, size, cur);
      
      if(1==tbw.ismach) {
        sttbw=tbw;
      } else if(0 == tbw.ismach) {
        stbw=tbw;
      }
      break;

    case eCPUUTIL:
      memset(&tcpuutil, 0, sizeof(tcpuutil));

      tcpuutil.isPresent=1;
      tcpuutil.stobj=stobj_hdr;
      tcpuutil.parse(buf, size, cur);
      
      if(1==tcpuutil.ismach) {
        sttcpuutil=tcpuutil;
      } else if(0 == tcpuutil.ismach) {
        stcpuutil=tcpuutil;
      }
      break;

    case eMEMORY:
      memset(&tmem, 0, sizeof(tmem));

      tmem.isPresent=1;
      tmem.stobj=stobj_hdr;
      tmem.parse(buf, size, cur);
      
      if(1==tmem.ismach) {
        sttmem=tmem;
      } else if(0 == tmem.ismach) {
        stmem=tmem;
      }
      break;

    default:
      break;
    }//end switch

  }//end while
    
  TRACE(2, ") ", "");

  return 1;

}//parse



int nodeval::encode(uchar*buf, int size, int& cur) {
  
  int temp1, temp2;
  temp1=cur;
  stobj.encode(buf, size, cur);
  temp2=cur;
  
 
  //Program specific fields  
  //If bandwith field is present
  if(stbw.isPresent) {
    stbw.stobj.objtype = eBW;
    stbw.encode(buf, size, cur);
  }

  //cpuutilization field
  if(stcpuutil.isPresent) {
    stcpuutil.stobj.objtype = eCPUUTIL;
    stcpuutil.encode(buf, size, cur);
  }

  //memory
  if(stmem.isPresent) {
    stmem.stobj.objtype = eMEMORY;
    stmem.encode(buf, size, cur);
  }

  //Machine-specific fields
  //If bandwith field is present
  if(sttbw.isPresent) {
    sttbw.stobj.objtype = eBW;
    sttbw.encode(buf, size, cur);
  }

  //cpuutilization field
  if(sttcpuutil.isPresent) {
    sttcpuutil.stobj.objtype = eCPUUTIL;
    sttcpuutil.encode(buf, size, cur);
  }

  //memory
  if(sttmem.isPresent) {
    sttmem.stobj.objtype = eMEMORY;
    sttmem.encode(buf, size, cur);
  }

  //update the length
  stobj.length=cur-temp2;
  stobj.encode(buf, size, temp1);

  return 1;
}//encode



////////////////////////////////////////////////////////////////
//                        MESSAGE
////////////////////////////////////////////////////////////////


message::message() {

  stcmn_hdr.version = 1;
  stcmn_hdr.ttl = 255;
  //stcmn_hdr.porc = 1;   //default is to peer
  //stcmn_hdr.rori = 0;   //default to iterative routing
  //stcmn_hdr.ack = 0;

  stpeerinfo.isPresent = 0;  
  stexpires.isPresent=0;
  stp2poptions.isPresent=0;
  strt.isPresent=0;
  stnt.isPresent=0;

  //stro.isPresent=0;
  //stro.isrbuf=0;

}//message::message


void message::Initialize() {

  stcmn_hdr.version = 1;
  //stcmn_hdr.ttl = 255;
  //stcmn_hdr.porc = 1;   //default is to peer
  //stcmn_hdr.rori = 0;   //default to iterative routing
  stcmn_hdr.ack = 0;
  
  stpeerinfo.Initialize();

  stlpeerinfo.clear();  //clear the peerinfo list
  lstro.clear();        //clear the resource-object list

  stexpires.isPresent=0;
  stp2poptions.isPresent=0;
  
  strt.isPresent=0;
  strt.table.clear();

  stnt.isPresent=0;
  strt.table.clear();

  strl.isPresent=0;
  stpl.isPresent=0;

  stdiagoptions.isPresent=0;
  stdiagoptions.e=0;
  stdiagoptions.r=0;
  stdiagoptions.n=0;
  stdiagoptions.bwf=0;

  stdiagnostics.isPresent=0;
  stdiagnostics.strt.num=0;
  stdiagnostics.strt.table.clear();
  stdiagnostics.stnt.num=0;
  stdiagnostics.stnt.table.clear();
  stdiagnostics.stbw.sbw=0;
  stdiagnostics.stbw.rbw=0;  
  stdiagnostics.stcpuutil.isPresent=0;
  stdiagnostics.sttcpuutil.isPresent=0;

  stdiagnostics.lstro.clear(); //clear the resource list

  //nodevalue for checking relay functions
  stnodeval.isPresent=0;
  stnodeval.stbw.isPresent=0;
  stnodeval.stcpuutil.isPresent=0;
  stnodeval.stmem.isPresent=0;

  stnodeval.sttbw.isPresent=0;
  stnodeval.sttcpuutil.isPresent=0;
  stnodeval.sttmem.isPresent=0;

}//message::Initialize


int message::parse(uchar* buf, int size) {
  int cur=0;
  int length;
  struct peerinfo stP;
  struct resourceobject stro;

  //parse common header
  stcmn_hdr.parse(buf, size, cur);

  //length
  length = stcmn_hdr.length;
  //printf("parse length is: %d\n", length);  


  //If length is not correct, return immediately
  if(length != size-cur ){
    TRACE(2, "Message:Parse Parse error: incorrect packet\n", "");
    return -1;
  }

  
  length=length+cur;
    
  while( length > cur) {
    
    stobj_hdr.parse(buf, size, cur);

    //printf("obj header: %s length: %d, cur: %d\n", pobjtype[stobj_hdr.objtype], length+12, cur);
    TRACE(2, "    ", "");


    switch(stobj_hdr.objtype) {
      
    case ePEERINFO:
      if(0 == stpeerinfo.isPresent) {
        stpeerinfo.isPresent = 1;
        stpeerinfo.stobj = stobj_hdr;
        stpeerinfo.parse(buf, size, cur);
      } else {
        stP.isPresent = 1;
        stP.stobj = stobj_hdr;
        stP.parse(buf, size, cur);
        stlpeerinfo.push_back(stP);
      }
      break;          
      
    case eEXPIRES:
      stexpires.isPresent = 1;
      stexpires.stobj = stobj_hdr;        
      stexpires.parse(buf, size, cur);
      break;

    case eP2POPTIONS:
      stp2poptions.isPresent = 1;
      stp2poptions.stobj = stobj_hdr;
      stp2poptions.parse(buf, size, cur);
      break;

    case eROUTINGTABLE:
      strt.isPresent = 1;
      strt.stobj = stobj_hdr;
      strt.parse(buf, size, cur);
      break;

    case eNEIGHBORTABLE:
      stnt.isPresent = 1;
      stnt.stobj = stobj_hdr;
      stnt.parse(buf, size, cur);
      break;

    case eRESOURCEOBJECT:
      stro.Initialize();

      stro.isPresent=1;
      stro.stobj = stobj_hdr;
      stro.parse(buf, size, cur);

      //Insert in the resource object list
      lstro.push_back(stro);
      break;

    case eLOOKUPPEER:
      stpl.isPresent=1;
      stpl.stobj = stobj_hdr;
      stpl.parse(buf, size, cur);
      break;

    case eRLOOKUP:
      strl.isPresent=1;
      strl.stobj = stobj_hdr;
      strl.parse(buf, size, cur);
      break;

    case eDIAGNOSTICSOPTIONS:
      stdiagoptions.isPresent=1;
      stdiagoptions.stobj = stobj_hdr;
      stdiagoptions.parse(buf, size, cur);
      break;

    case eDIAGNOSTICS:
      stdiagnostics.isPresent=1;
      stdiagnostics.stobj = stobj_hdr;
      stdiagnostics.parse(buf, size, cur);
      break;

    case eNODEVAL:
      stnodeval.isPresent=1;
      stnodeval.stobj=stobj_hdr;
      stnodeval.parse(buf, size, cur);
      break;  
      
    default:
      break;
    }//end switch

    TRACE(2, "\n", "");
    
  }
  
  return 0;
  
}//message::parse


  
int message::encode(uchar* buf, int size) {
  int cur=0, temp=0, temp1;
  uint32 count=0;
  list<peerinfo>::iterator itr;
  list<struct resourceobject>::iterator ri;

  //encode common header
  stcmn_hdr.encode(buf, size, cur);
  temp1 = cur;
  
  //encode Peer-Info object
  if(stpeerinfo.isPresent) {
    stpeerinfo.stobj.objtype = ePEERINFO;
    stpeerinfo.encode(buf, size, cur);
  }    

  //encode the rest of Peer-Info objects
  if(stlpeerinfo.size() > 0) {
    
    for(itr=stlpeerinfo.begin(); itr!=stlpeerinfo.end(); itr++) {
      itr->stobj.objtype = ePEERINFO;
      itr->encode(buf, size, cur);
    }    
  }
    
  //encode Expires
  if(stexpires.isPresent) {
    stexpires.stobj.objtype = eEXPIRES;
    stexpires.encode(buf, size, cur);
  }    

  //encode P2P-Options
  if(stp2poptions.isPresent) {
    stp2poptions.stobj.objtype = eP2POPTIONS;
    stp2poptions.encode(buf, size, cur);
  }

  //encode Routing-table
  if(strt.isPresent) {
    strt.stobj.objtype = eROUTINGTABLE;
    strt.encode(buf, size, cur);
  }

  //encode Neighbor-table
  if(stnt.isPresent) {
    stnt.stobj.objtype = eNEIGHBORTABLE;
    stnt.encode(buf, size, cur);
  }

  //encode resource-object
  if(lstro.size() > 0) {
    for(ri=lstro.begin(); ri!=lstro.end(); ri++) {

      if(count+15 < lstro.size() ) {
        count++;
        continue;
      }

      (*ri).stobj.objtype = eRESOURCEOBJECT;
      (*ri).encode(buf, size, cur);
    }    
  }//end if

  //encode plookup
  if(stpl.isPresent) {
    stpl.stobj.objtype = eLOOKUPPEER;
    stpl.encode(buf, size, cur);
  }

  //encode rlookup
  if(strl.isPresent) {
    strl.stobj.objtype = eRLOOKUP;
    strl.encode(buf, size, cur);
  }

  //encode diagnostics
  if(stdiagnostics.isPresent){
    stdiagnostics.stobj.objtype = eDIAGNOSTICS;
    stdiagnostics.encode(buf,size,cur);
  }

  //encode diagnostics options
  if(stdiagoptions.isPresent){
    stdiagoptions.stobj.objtype = eDIAGNOSTICSOPTIONS;
    stdiagoptions.encode(buf,size,cur);
  }

  //encode nodeval
  if(stnodeval.isPresent) {
    stnodeval.stobj.objtype = eNODEVAL;
    stnodeval.encode(buf,size,cur);
  }
  
  stcmn_hdr.length = cur - temp1;

  //printf("encode length is: %d\n", stcmn_hdr.length);
  stcmn_hdr.encode(buf, size, temp);
  
  //encode each object
  return cur;
  
}//message::encode



string message::getTransId() {
  string str;
  char temp[HASHLEN*2];
  int32 len;

  len=idlen;
  if(stcmn_hdr.reqoritype == eBOOTSTRAP || stcmn_hdr.reqoritype == eAUTHENTICATE) {
    len=4;
  }
  
  //Source-Id
  uchar_to_hexstring(stcmn_hdr.srcid, len, temp);

  //Now compute the (source-Id, trasaction-Id) tuple and store it
  str = temp;
  sprintf(temp,"%u", stcmn_hdr.transid);
  str += temp;

  return str;

}//message::getTransId
