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

#include <stdlib.h>
#include "trace.h"
#include "gettimeofday.h"
#ifndef _WENGO
#include "sha1.h"
#include "md5.h"
#include "md4.h"
#include "sha256.h"
#include "bigint.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/msg/msgheader.h"
#include "p2pp/bigint/bigint.h"
#endif

/*Compare function for sorting using stl*/
bool CompareBasedOnPeerid(message stm1, message stm2) {
  BigInt peerid1, peerid2;
  
  uchar_to_bigint(stm1.stpeerinfo.stunhashed.unhashed, 20, peerid1);
  uchar_to_bigint(stm2.stpeerinfo.stunhashed.unhashed, 20, peerid2);

  if(peerid1<peerid2){
    return true;
  } else {
    return false;
  }
}


Diagnostics::Diagnostics() {

}

Diagnostics::~Diagnostics() {
  
}


void Diagnostics::Initialize() {
  m_stPeer.isPresent=0;

  //TEMP CODE
  memset(m_ID, 0, 255);
  m_ID[3]=2;

  gettimeofday(&m_tRoute, NULL);
  //setRouteTimer();
  setResourceTimer();
  //TEMP CODE ENDS

  Peer::Initialize();

}//Initialize


void Diagnostics::setDiagnosticsFile(const char *filename){

  if(filename){
    memcpy(m_Diagnosticsfile, filename, ID_MAX);
  }
}


int32 Diagnostics::Insert (char8* pchKey, int32 klen, char8* pchValue, int32 vlen, void* (*)(void*, int32), void* pApp) {

  return 1;
}//Insert


int32 Diagnostics::RLookup (char8* pchKey, int32 klen, void* (*)(void*, int32), void* pApp ) {

  return 1;
}//RLookup


int32 Diagnostics::PLookup (char8* pchKey, int32 klen, void* (*)(void*, int32), void* pApp ) {

  return 1;
}//PLookup



int32 Diagnostics::InsertRT(char* pchIP, int32 port, transtype tt, list<message>::iterator& mitr) {
  
  int32 f1=0;
  list<peerinfo>::iterator oitr, iitr;

  TRACE(2, "Diagnostics::InsertRT...called\n", "");

  for(oitr=m_msg.stdiagnostics.strt.table.begin(); oitr!=m_msg.stdiagnostics.strt.table.end(); oitr++) {
    
    if(oitr->staddr.port[0]==0 || strncmp("0.0.0.0",oitr->staddr.addr[0],7)==0)
      continue;

    f1=0;
    for(iitr=mitr->stdiagnostics.strt.table.begin(); iitr!=mitr->stdiagnostics.strt.table.end(); iitr++) {
      
      //if already there
      if(strcmp(iitr->staddr.addr[0],oitr->staddr.addr[0])==0 && iitr->staddr.port[0] == oitr->staddr.port[0] ) {
        f1=1;
        break;
      }
    }//end for
    
    //if not found, then insert
    if(0 == f1) {
      TRACE(2, "Diagnostics::InsertRT...Inserting IP: %s port: %d\n", oitr->staddr.addr[0], oitr->staddr.port[0] );
      memcpy(mitr->stpeerinfo.stunhashed.unhashed, m_msg.stpeerinfo.stunhashed.unhashed, m_HASH_SIZE);
      mitr->stdiagnostics.strt.table.push_back(*oitr);
      mitr->stdiagnostics.strt.num=mitr->stdiagnostics.strt.table.size();
    }
    
  }//end for
  
  return 1;
}//InsertRT


int32 Diagnostics::InsertNT(char* pchIP, int32 port, transtype tt, list<message>::iterator& mitr) {
  
  int32 f1=0;
  list<peerinfo>::iterator oitr, iitr;

  for(oitr=m_msg.stdiagnostics.stnt.table.begin(); oitr!=m_msg.stdiagnostics.stnt.table.end(); oitr++) {
    
    if(oitr->staddr.port[0]==0 || strncmp("0.0.0.0",oitr->staddr.addr[0],7)==0)
      continue;

    f1=0;
    for(iitr=mitr->stdiagnostics.stnt.table.begin(); iitr!=mitr->stdiagnostics.stnt.table.end(); iitr++) {
      
      //if already there
      if(strcmp(iitr->staddr.addr[0],oitr->staddr.addr[0])==0 && iitr->staddr.port[0] == oitr->staddr.port[0] ) {
        f1=1;
        break;
      }
    }//end for
    
    //if not found, then insert
    if(0 == f1) {
      memcpy(mitr->stpeerinfo.stunhashed.unhashed, m_msg.stpeerinfo.stunhashed.unhashed, m_HASH_SIZE);
      mitr->stdiagnostics.stnt.table.push_back(*oitr);
      mitr->stdiagnostics.stnt.num=mitr->stdiagnostics.stnt.table.size();
    }
    
  }//end for
  
  return 1;
}//InsertNT

int32 Diagnostics::InsertRes(char* pchIP, int32 port, transtype tt, list<message>::iterator& mitr) {  

  list<resourceobject>::iterator i1;

  if(m_msg.stdiagnostics.lstro.size() <= 0) {
    return 0;
  }

  //clear the existing
  mitr->stdiagnostics.lstro.clear();
  
  //copy
  for(i1=m_msg.stdiagnostics.lstro.begin(); i1!=m_msg.stdiagnostics.lstro.end(); i1++) {
  
    //    if(i1->staddr.port[0]==0)
    //  continue;
    mitr->stdiagnostics.lstro.push_back(*i1);
  }
  
  return 1;
}//InsertRes



int32 Diagnostics::OnReceive(uchar* buf, int len, char* pchIP, int32 port, transtype tt) {
  struct peerinfo stPeer;
  list<struct message>::iterator mitr;
  map<string, Transaction*>::iterator itr;
  list<peerinfo>::iterator litr;

  m_msg.Initialize();
  m_msg.parse(buf, len);

  //Find the transaction
  itr=m_TMap.find(m_msg.getTransId() );
  if(itr == m_TMap.end() ) {
    TRACE(1, "Diagnostics::OnReceive...transaction does not exist for %s %s\n", preqoritype[m_msg.stcmn_hdr.reqoritype], pmsgtype[m_msg.stcmn_hdr.msgtype]);
    return 1;
  }

  if(eRESPONSE==m_msg.stcmn_hdr.msgtype || eRESPONSEACK == m_msg.stcmn_hdr.msgtype) {
    
    ComposeACK(pchIP, port);
    
    //Remove the transaction from the map
    TRACE(1, "Diagnostics::OnReceive...response received...Transaction size: %d\n", m_TMap.size() );
    delete itr->second;
    m_TMap.erase(itr);
    TRACE(1, "Diagnostics::OnReceive...response received...Transaction removed size: %d\n", m_TMap.size() );  
    
    
  } else {
    
    TRACE(1, "Diagnostics::OnReceive...Other than RESPONSE or RESPONSEACK received size: %d\n", m_TMap.size() );
    return 1;
    
  }//end else



  switch(m_eState){

  case ePCONNECTED:
    
    switch(m_msg.stcmn_hdr.reqoritype) {

    case eGETDIAGNOSTICS:

      if(m_msg.stlpeerinfo.size()) {
	AppendPeerList(m_msg.stlpeerinfo);
	break;
      }
      mitr=m_DL.begin();
      while(mitr!=m_DL.end()){
	if(0 == strcmp(mitr->stpeerinfo.staddr.addr[0], m_msg.stpeerinfo.staddr.addr[0]) &&
	   mitr->stpeerinfo.staddr.port[0] == m_msg.stpeerinfo.staddr.port[0] ) {
	  printf("Diagnostics received from %s %d\n", m_msg.stpeerinfo.staddr.addr[0], m_msg.stpeerinfo.staddr.port[0]);
	  
	  if(m_msg.stdiagnostics.strt.table.size()>0){
	    mitr->stdiagnostics.strt = m_msg.stdiagnostics.strt;
	  }
	  if(m_msg.stdiagnostics.stnt.table.size()>0){
	    mitr->stdiagnostics.stnt = m_msg.stdiagnostics.stnt;
	  }
	  if(m_msg.stdiagnostics.lstro.size()>0){
	    mitr->stdiagnostics.lstro = m_msg.stdiagnostics.lstro;
	  }
	  mitr->stdiagnostics.stbw = m_msg.stdiagnostics.stbw;
	  mitr->stdiagnostics.stcpuutil = m_msg.stdiagnostics.stcpuutil;
	  
	  break;
	}
	mitr++;
      }//end while
      break;
    default:
      break;
    }
  
  default:
    break;  

  }//end switch


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

}//OnReceive

void Diagnostics::SaveRouteData(){
  list<struct message>::iterator itr;
  list<struct peerinfo>::iterator litr;
  list<resourceobject >::iterator i1;
  char filename[100],failednodelist[100],usernamefile[100];
  FILE *failedfd=NULL, *usernamefd=NULL;
  struct timeval sTime;
  string strHost;

  gettimeofday(&sTime, NULL);
  sprintf(failednodelist, "failednodes.txt.%d", sTime.tv_sec);
  sprintf(usernamefile, "user_name");
  
  strncpy(filename, (char*)m_Diagnosticsfile, 100);

  m_pData=fopen(filename, "w"); //"data.xml", "w");
  if(!m_pData){
    
  }
  failedfd=fopen(failednodelist,"w");
  if(!failedfd){

  }
  usernamefd=fopen(usernamefile, "w");
  if(!usernamefd) {
  
  }

  printlocaltime(failedfd);
  printlocaltime(m_pData);
  fprintf(m_pData, "%d\n", m_DL.size());
  
  itr=m_DL.begin();
  while(itr!=m_DL.end()){

    fprintf(m_pData, "%s:%d:%s\n", itr->stpeerinfo.staddr.addr[0], itr->stpeerinfo.staddr.port[0], itr->stpeerinfo.stunhashed.unhashed);

    fprintf(m_pData, "%d\n", itr->stdiagnostics.strt.table.size());

    //Transport::gethostbyaddr(itr->stpeerinfo.staddr.addr[0], strHost);
    strHost=itr->stpeerinfo.staddr.addr[0];
    if( itr->stdiagnostics.stbw.sbw==0 && itr->stdiagnostics.stbw.rbw==0){
      fprintf(failedfd, "%s\n", strHost.c_str());
    } else {
      fprintf(usernamefd, "%s %s %s %d\n", itr->stpeerinfo.stunhashed.unhashed, strHost.c_str(), 
	      itr->stpeerinfo.staddr.addr[0], itr->stpeerinfo.staddr.port[0]); 
    }
    //print bandwidth
    fprintf(m_pData, "%u\n%u\n", itr->stdiagnostics.stbw.sbw, itr->stdiagnostics.stbw.rbw);
    //print cpu utilization
    fprintf(m_pData, "%u\n", itr->stdiagnostics.stcpuutil.cpuusage);

    itr++;
  }
  
  fclose(failedfd);
  fclose(m_pData);
  fclose(usernamefd);
}//SaveRouteData



void Diagnostics::SaveResourceData(){
  list<struct message>::iterator itr;
  list<struct resourceobject>::iterator i1;
  char filename[100];
  
  
  strncpy(filename, (char*)m_Diagnosticsfile, 100);
  strcat(filename, ".res");
  

  //Sort peer-Ids
  m_DL.sort(CompareBasedOnPeerid);

  m_pData=fopen(filename, "w"); //"data.xml", "w");
  fprintf(m_pData, "%d\n", m_DL.size());
  itr=m_DL.begin();
  while(itr!=m_DL.end()){

    fprintf(m_pData, "%s:%d\n", itr->stpeerinfo.staddr.addr[0], itr->stpeerinfo.staddr.port[0]);
    itr++;
  }
  itr=m_DL.begin();
  while(itr!=m_DL.end()){

    fprintf(m_pData, "%s:%d\n", itr->stpeerinfo.staddr.addr[0], itr->stpeerinfo.staddr.port[0]);
    //print size of resource table
    fprintf(m_pData, "%d\n", itr->stdiagnostics.lstro.size() );
    

    for(i1=itr->stdiagnostics.lstro.begin(); i1!=itr->stdiagnostics.lstro.end(); i1++) {      
      if(eUSERINFO == i1->conttype) {
        fprintf(m_pData,"USERINFO %s %s:%d\n", (char*)i1->stpeerinfo.stunhashed.unhashed, i1->stpeerinfo.staddr.addr[0], i1->stpeerinfo.staddr.port[0] );
        
      } else if(eSTRINGVAL == i1->conttype) {
        fprintf(m_pData,"STRINGVAL %s %s\n", (char*)i1->stpeerinfo.stunhashed.unhashed, i1->strov.value );
      }

      //fprintf(
    }//end for
    
    itr++;
  }

  fclose(m_pData);
  
}//SaveResourceData


void Diagnostics::ComposeDiagnosticsToSites(enum diag_type diagtype) {
  list<struct message>::iterator itr;

  //send the message
  itr=m_DL.begin();
  while(itr!=m_DL.end()){

    TRACE(2, "Diagnostics::ComposeDiagnostics... %d\n", m_DL.size() );  

    printf("sending to: %s %d\n", (char8*)itr->stpeerinfo.staddr.addr[0], itr->stpeerinfo.staddr.port[0]);
    m_objMH.GetDiagnostics( (char8*)itr->stpeerinfo.staddr.addr[0], itr->stpeerinfo.staddr.port[0], eUDP, diagtype, NULL, NULL);

    itr++;
  }

}//ComposeRouteDiagnosticsToSites





int32 Diagnostics::isRouteTimerExpired() {

  struct timeval curr_time,difft;
  gettimeofday(&curr_time, NULL);
  
  //difference between current time and timer
  difft=difftimeval(m_tRoute, curr_time);

  if(0==difft.tv_sec && 0==difft.tv_usec){
    return 1;
  }
  return 0;
}//isRoutingExpired


void Diagnostics::setRouteTimer() {
  gettimeofday(&m_tRoute, NULL);
  m_tRoute = addtimeval(m_tRoute, ROUTE_TIMER, 0);  
}//setRoutingExpired



int32 Diagnostics::isResourceTimerExpired() {

  struct timeval cur,difft;
  gettimeofday(&cur, NULL);
  
  //difference between current time and timer
  difft=difftimeval(m_tResource, cur);

  if(0==difft.tv_sec && 0==difft.tv_usec){
    return 1;
  }
  return 0;
}//isResourceExpired


void Diagnostics::setResourceTimer() {
  gettimeofday(&m_tResource, NULL);
  m_tResource = addtimeval(m_tResource, RESOURCE_TIMER, 0);
  
}//setResourceExpired



void Diagnostics::OnTimer(){

  //printf("state: %d\n", m_eState);
  switch(m_eState){

  case ePINITIAL:
    ComposeDiagnosticsToBoot();
    m_eState=ePCONNECTED;
    setRouteTimer();
    setResourceTimer();
    break;
  case ePCONNECTED:
    
    //printf("timer expired\n");
    if(1==isRouteTimerExpired()) {
      printf("before save route dara\n");
      SaveRouteData();
      printf("after save route data\n");
      ComposeDiagnosticsToBoot();
      printf("after diagnostics to boot\n");
      ComposeDiagnosticsToSites(eGET_NONE);
      printf("after diagnostics to sites\n");
      setRouteTimer();
    }

    break;
    
  default:
    break;
  }//end switch

  //Node::OnTimer();

}//OnTimer


void Diagnostics::AppendPeerList(list<peerinfo>& lstP) {
  message msg;
  int found=0;
  list<peerinfo>::iterator itr;
  list<message>::iterator mitr;

  printf("List recvd from Bootstrap size=%d\n", lstP.size());
  for(itr=lstP.begin(); itr!=lstP.end(); itr++) {
    found=0;
    mitr=m_DL.begin();
    while(mitr!=m_DL.end()){
      if(0 == strcmp(mitr->stpeerinfo.staddr.addr[0], itr->staddr.addr[0]) &&
	 mitr->stpeerinfo.staddr.port[0] == itr->staddr.port[0] ) {
	found=1;
	break;
      }
      mitr++;
    }
    if(!found) {
      strcpy(msg.stpeerinfo.staddr.addr[0], itr->staddr.addr[0]);
      msg.stpeerinfo.staddr.port[0] = itr->staddr.port[0];
      memset(msg.stpeerinfo.stunhashed.unhashed, 0, HASHLEN); 
      sprintf((char*)msg.stpeerinfo.stunhashed.unhashed, "%s", (char*)itr->stunhashed.unhashed);
      msg.stpeerinfo.stunhashed.stobj.length=strlen((char*)msg.stpeerinfo.stunhashed.unhashed);
      //      strcpy((char*)msg.stpeerinfo.stunhashed.unhashed, (char*)itr->stunhashed.unhashed );
      msg.stdiagnostics.stnt.num=0;
      msg.stdiagnostics.strt.num=0;
      m_DL.push_back(msg);
      printf( "%s %d %s\n", msg.stpeerinfo.staddr.addr[0],msg.stpeerinfo.staddr.port[0], msg.stpeerinfo.stunhashed.unhashed);
    }

  }
}//ComposeQueryToBoot

void Diagnostics::ComposeDiagnosticsToBoot() {
  FILE *nodefd=NULL;
  list<peerinfo> stlP;
  peerinfo stP;

  //send the message
  TRACE(2, "Diagnostics::ComposeDiagnostics...\n", "");  
  
  nodefd=fopen("nodelist", "r");
  if(NULL==nodefd) {
    m_objMH.GetDiagnostics( m_BOOT_IP, m_BOOT_PORT, eTCP, eGET_RT, NULL, NULL);  
  } else {
    do {
      memset(stP.stunhashed.unhashed, 0, HASHLEN);
      fscanf(nodefd, "%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;
      stlP.push_back(stP);
    }while(!feof(nodefd));
    AppendPeerList(stlP);
  }//end of if

  //m_objMH.GetDiagnostics( "128.59.19.185", 10080, eTCP, eGET_RT, NULL, NULL);
}
