/*
 * 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 "bootstrap.h"

#include <stdlib.h>

#ifndef _WENGO
#include "sha1.h"
#include "md5.h"
#include "md4.h"
#include "sha256.h"
#include "trace.h"
#endif

#ifdef _WENGO
#include "p2pp/utils/sha1.h"
#include "p2pp/utils/md5.h"
#include "p2pp/utils/md4.h"
#include "p2pp/utils/sha256.h"
#include "p2pp/common/trace.h"
#endif

Bootstrap::Bootstrap() {
  m_uiTransId=0;

}

Bootstrap::~Bootstrap() {
  
}


void Bootstrap::Initialize() {

  m_stPeer.isPresent=0;

  //TEMP CODE
  memset(m_ID, 0, 255);
  //TEMP CODE ENDS

  FILE* fp;
  fp = fopen("nodelist", "w+");
  fclose(fp);

  Node::Initialize();

}//Initialize


void Bootstrap::OnTimer() {
  Node::OnTimer();
}//OnTimer


int32 Bootstrap::PublishObject (char8* pchKey, int32 klen, char8* pchValue, int32 vlen, void* (*)(void*, int32), void* pApp) {

  return 1;
}//Insert


int32 Bootstrap::LookupObject (char8* pchKey, int32 klen, void* (*)(void*, int32), void* pApp ) {

  return 1;
}//RLookup


int32 Bootstrap::LookupPeer (char8* pchKey, int32 klen, void* (*)(void*, int32), void* pApp ) {

  return 1;
}//PLookup

void Bootstrap::ComposeDiagnosticsResp(char* pchIP, int32 port, transtype tt) {
  int32 size;
  peerinfo stP;
  struct message tMsg;
  FILE *fp;

  tMsg.Initialize();

  tMsg.stcmn_hdr = m_msg.stcmn_hdr;

  //common header
  tMsg.stcmn_hdr.msgtype = (tt == eUDP)? eRESPONSEACK:eRESPONSE;
  tMsg.stcmn_hdr.ack = 0;
  tMsg.stcmn_hdr.respcode = 200;
  memcpy(tMsg.stcmn_hdr.respid, m_ID, idlen); //srcid
  tMsg.stcmn_hdr.rlen = idlen;
  TRACE(2, "ID len: %d\n", idlen);
  memcpy(tMsg.stcmn_hdr.respid, m_ID, idlen); //srcid
  tMsg.stcmn_hdr.rlen = idlen;

  //peer-info object
  getSelfPeer(tMsg.stpeerinfo);

  //printf("requesting: r %d e %d\n", m_msg.stdiagoptions.r, m_msg.stdiagoptions.e);

  //If routing table
  //routing and /or neighbor table
  stP=tMsg.stpeerinfo;
  fp=fopen("nodelist", "r");
  if(fp) {
    do {
      memset(stP.stunhashed.unhashed, 0, HASHLEN);
      fscanf(fp, "%s %d %s\n", stP.staddr.addr[0],&stP.staddr.port[0], stP.stunhashed.unhashed);
      stP.stunhashed.stobj.length = strlen((char*)stP.stunhashed.unhashed);
      stP.staddr.isPresent=1;
      stP.isPresent=1;
      stP.stunhashed.isPresent=1;
      tMsg.stlpeerinfo.push_back(stP);
    }while(!feof(fp));
  }
  fclose(fp);
  
  //Diagnostics is always present because we return the bandwidth
  tMsg.stdiagnostics.isPresent=1;

  //encode the message
  size=tMsg.encode(m_buf, BUFSIZE);

  //For debugging purposes, parse the message
  message s1;
  s1.parse(m_buf, size);

  //Create a transaction and pass it the response
  Transaction* pTrans = new Transaction();
  pTrans->setParams(m_pobjTransport, tMsg.stcmn_hdr.transid, tMsg.stcmn_hdr.srcid, idlen, eRESPONSEACK, eGETDIAGNOSTICS, tt);
  pTrans->Send(m_buf, size, pchIP, port);
  m_TMap[pTrans->getTransId()] = pTrans;

}//ComposeDiagnosticsResp


int32 Bootstrap::OnReceive(uchar* buf, int len, char* pchIP, int32 port, transtype tt) {
  int size;
  int tlen;
  struct peerinfo stPeer;
  struct message tmsg;
  string str="";
  char temp[100];
  uchar puchTdigest[32];  
 
  //Initialize the message
  m_msg.Initialize();
  m_msg.parse(buf, len);

  //Ignore ACK messages
  if(1 == m_msg.stcmn_hdr.ack) {
    return 1;
  }
  
  //Ignore received responses
  if(m_msg.stcmn_hdr.msgtype != eREQUEST && m_msg.stcmn_hdr.reqoritype != eBOOTSTRAP) {
    return 1;
  }

  //Compose Diagnostic reponse if there is request for diagnostics
  if(m_msg.stcmn_hdr.reqoritype==eGETDIAGNOSTICS) {
    ComposeDiagnosticsResp(pchIP, port, eTCP);
    return 1;
  }


  //If IP address and port already exist in the list, remove them
  removeSimilar(m_msg.stpeerinfo);

  //copy of peerinfo received
  m_stPeer = m_msg.stpeerinfo;
  
  //Increment transaction Id. Used for generating a unique peerid
  m_uiTransId++;
  sprintf(temp, "%u", (uint32)m_uiTransId);
  str=temp;

  if(m_msg.stpeerinfo.stunhashed.isPresent) {
    cout<<"Message received from UID "<<(char*)m_msg.stpeerinfo.stunhashed.unhashed<<" IP: "<<m_msg.stpeerinfo.staddr.addr[0]<<" port: "<<m_msg.stpeerinfo.staddr.port[0] <<" packet IP: " <<pchIP <<" packet port: "<< port <<endl;
    m_msg.stpeerinfo.stunhashed.unhashed[m_msg.stpeerinfo.stunhashed.stobj.length]='\0';
    str += (char*)m_msg.stpeerinfo.stunhashed.unhashed;
  }
  
  //Generate the SHA1
  if(m_HASH_ALG == eA_SHA1) {
    printf("Generating a SHA1 ID\n");    

    sha1_wrapper((uchar*)str.c_str(), str.size(), puchTdigest);   
    tlen = 20;    
  }
  else if(m_HASH_ALG == eA_MD4) {
    printf("Generating a MD4 ID\n");

    md4_csum((uchar*)str.c_str(), str.size(), puchTdigest);   
    tlen=16;
  }
  else if(m_HASH_ALG == eA_MD5) {
    printf("Generating a MD5 ID\n");    

    md5_csum((uchar*)str.c_str(), str.size(), puchTdigest);
    tlen=16;   
  }
  else if(m_HASH_ALG == eA_SHA256) {
    printf("Generating a SHA256 ID\n");

    sha2_csum((uchar*)str.c_str(), str.size(), puchTdigest);
    tlen=32;
    
  }  else {
    //Generate a random ID
    printf("Generating a normal ID\n");
    uchar pos;
    do {
      pos=rand()%256;
    }while(m_ID[pos] != 0);

    m_ID[pos]=1;

    tlen=4;

    //TODO: FIX IT
  }

  memcpy(m_stPeer.stpeerid.id, puchTdigest, tlen);
  m_stPeer.stpeerid.stobj.length = tlen;  
  for(int i=0; i<tlen; i++) {
    printf("%02x", m_stPeer.stpeerid.id[i]);
  }
  printf("\n");
  ComposeBootstrapResp();

  //encode the message
  size=m_msg.encode(m_buf, BUFSIZE);

  //parse the message
  tmsg.parse(m_buf, size);

  //send the message
  m_pobjTransport->Send(m_buf, size, pchIP, port, tt);


  //If a peer and
  //If the list is already full
  //then pop the first element of the list
  if(ePEER == m_msg.stcmn_hdr.porc) {

    //commented for planetlab
    /*
    if(m_BSP.size() == 20) {
      m_BSP.pop_front();
    }
    */
    //m_stPeer.stunhashedid.isPresent=0;

    if(m_BSP.size() <= 20) {
      m_BSP.push_back(m_stPeer);
    }
  }
    


  //TEMP CODE BEGINS
  //write the new IP address and port to a file
  if(ePEER == m_msg.stcmn_hdr.porc) {
    FILE* fp;
    fp = fopen("nodelist", "a+");
    fprintf(fp, "%s %d %s\n", m_stPeer.staddr.addr[0], m_stPeer.staddr.port[0], m_stPeer.stunhashed.unhashed);
    fclose(fp);
  }
  //TEMP CODE ENDS

  
  return Node::OnReceive(buf, len, pchIP, port, tt);

}//OnReceive


int32 Bootstrap::removeSimilar(const struct peerinfo& stPeer) {
  list<peerinfo>::iterator itr;
  int32 ret = 0;

  for(itr=m_BSP.begin(); itr!=m_BSP.end(); itr++) {
    ret=compareAddr(stPeer, *itr);
    if(1==ret) {
      break;
    }
  }//end for
  
  if(1==ret) {
    m_BSP.erase(itr);
    TRACE(2, "Bootstrap::removeSimilar...removing peer\n", "");
  }  

  return 1;
}//removeSimilar


int32 Bootstrap::compareAddr(const struct peerinfo& stFirst, const struct peerinfo& stSecond) {
  map<string, port_trans> addrmap;
  map<string, port_trans>::iterator itr;  
    
  TRACE(3, "Bootstrap::compareAddr...Begins\n", "" );

  for(int i=0; i<stFirst.staddr.num && i<stSecond.staddr.num; i++) {
    if(0 == strcmp(stFirst.staddr.addr[i], stSecond.staddr.addr[i]) &&
       stFirst.staddr.port[i] == stSecond.staddr.port[i]) {     
      
      return 1;
    }
  }//end for  
  
  return 0;

}//isAddrPresent



int32 Bootstrap::getSelfPeer(struct peerinfo& stP) {

  map<string, port_trans> addrmap;
  map<string, port_trans>::iterator itr;

  //PeerInfo
  stP.stobj.objtype = ePEERINFO;
  stP.isPresent = 1;

  //Peer-Id
  memset(stP.stpeerid.id, 0, 4);
  stP.stpeerid.id[3]=1;
  stP.stpeerid.stobj.length=4;
  stP.stpeerid.isPresent=1;

  //Get the addressinfo object from transport
  m_pobjTransport->getAddrPort(addrmap);

  //Fill the table
  int i=0;
  for(itr=addrmap.begin(); itr!=addrmap.end(); itr++, i++) {

    strncpy(stP.staddr.addr[i], (char*)itr->first.c_str(), 16);
    stP.staddr.port[i] = itr->second.port;
    stP.staddr.tt[i]   = itr->second.tt;
    stP.staddr.ht[i]   = itr->second.ht;
    stP.staddr.ipver[i]=4;

    stP.staddr.R[i] = 0;
    stP.staddr.foundation[i] = 0;
    stP.staddr.compid[i] = 0;
    stP.staddr.priority[i] = 0;
    
  }//end for

  //The number of address info objects
  stP.staddr.num = i;
  stP.staddr.isPresent=1;

  return i;

}//getSelfPeer


void Bootstrap::ComposeBootstrapResp() {
  list<peerinfo>::iterator litr;   
  struct peerinfo stP;

  m_msg.Initialize();

  //header
  m_msg.stcmn_hdr.msgtype=eRESPONSEACK;
  m_msg.stcmn_hdr.ack = 0;
  m_msg.stcmn_hdr.respcode=200;
  //src and destination id's
  memset(m_msg.stcmn_hdr.srcid, 0, 4);
  m_msg.stcmn_hdr.slen = 4;

  memset(m_msg.stcmn_hdr.respid, 0, 4);
  m_msg.stcmn_hdr.respid[3]=1;
  m_msg.stcmn_hdr.rlen = 4;

  //self peerinfo object
  getSelfPeer(stP);
  m_msg.stpeerinfo = stP;
  m_msg.stpeerinfo.isPresent = 1;  

  //return the peer with its peer-id
  m_msg.stlpeerinfo.push_back(m_stPeer);
  
  //insert list of other peers
  for(litr=m_BSP.begin(); litr!= m_BSP.end(); litr++) {
    m_msg.stlpeerinfo.push_back(*litr);
  }//for

  //p2poptions
  m_msg.stp2poptions.isPresent=1;
  m_msg.stp2poptions.halg = m_HASH_ALG;
  m_msg.stp2poptions.halglen = m_HASH_SIZE;
  m_msg.stp2poptions.hbase = m_BASE;
  m_msg.stp2poptions.p2palg = m_P2P;
  m_msg.stp2poptions.oidlen=strlen(pp2palg[m_P2P]);
  strcpy((char*)m_msg.stp2poptions.oid, pp2palg[m_P2P]);  

}//ComposeBootstrapResp

