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

#include "peer.h"

//this structure contains values for program
//and machine bw, cpuutil, and memory. if the
//appropriate values are set, then actual bandwidth/cpu/mem
//is not calculated. rather the set bw/cpuutil/mem is used.
extern struct nodeval g_stnodeval;

MaintHelper::MaintHelper() {

}//MaintHelper



MaintHelper::~MaintHelper() {

}//~MaintHelper


void MaintHelper::setParams(Peer* pPeer, RManager* pRM) {
  
  m_pPeer = pPeer;

  m_pRM = pRM;
  
}//setParams


int32 MaintHelper::OnJoinRequest(struct message& msg, char* pchIP, int32 port, transtype tt) {
  struct peerinfo stPSelf, stNP;
  int32 ret;  
  string strtransid;
  LObject objLO;
  int32 respCode;
  
  TRACE(3, "MaintHelper::OnJoinRequest...called\n", "" );
  
  //getself peer
  m_pPeer->getSelfPeer(stPSelf);
  
  //getNextHop
  ret=m_pPeer->getNextHop(msg.stpeerinfo.stpeerid.id, m_pPeer->m_HASH_SIZE, stNP );
  

  //Corner case. LookupObject for STUN is sent before join. So we could have inserted
  //this peer in our routing table. When returning NP, compare the IP address and port

  if(0 == strcmp( msg.stpeerinfo.staddr.addr[0], stNP.staddr.addr[0]) && msg.stpeerinfo.staddr.port[0] == stNP.staddr.port[0] ) {
    ret = 0;
  }

  //Determine if we are the hop or need to redirect
  if(0 == ret) {
    respCode = 200;
  } else {
    respCode = 302;
  }  

  
  //TEMP CODE: may need to redirect the request to a peer with
  //the least number of clients
  //Now client will join
  /*
  if(eCLIENT == msg.stcmn_hdr.porc) {
    respCode = 200;
  }
  */  
  
  //Insert only if peer and 200
  if(200 == respCode) {
    //Insert only if peer
    if(ePEER == msg.stcmn_hdr.porc) {
      m_pPeer->Insert(msg.stpeerinfo, 1);
      TRACE(2,"KadPeer::OnJoinRequest...Table Insert\n", "");
    }
  }

  

  //If iterative or 200 send a response
  if(0 == msg.stcmn_hdr.rori || 200 == respCode) {
    
    if(302 == respCode) {
    printf("iterative: sending 302 %s %d isaddr: %d\n", stNP.staddr.addr[0], stNP.staddr.port[0], stNP.isPresent);
    }  

    m_pPeer->ComposeJoinResp(respCode, stPSelf, stNP, pchIP, port, tt);
  } else {

    objLO.pApp=0;   objLO.callback=0;   objLO.isOrig=0;

    strcpy(objLO.pchIP, pchIP);
    objLO.port = port;
    objLO.tt = tt;

    m_pPeer->ComposeJoin(stNP, tt, strtransid, msg);
    
    m_IP[strtransid] = objLO;
  }


  return 1;

}//OnJoinRequest


int32 MaintHelper::OnJoinResponse(struct message& msg) {

  map<string, LObject>::iterator i1;

  list<peerinfo>::iterator litr;
  struct LObject objLO;
  string strtransid;
  struct peerinfo stNP;  
  
  TRACE(2, "MaintHelper::OnJoinResponse...%d\n",  m_IP.size() );
  
  //Check if we have already processed this message
  i1=m_IP.find( msg.getTransId() );
  if(i1 == m_IP.end() ) {
    TRACE(2, "MaintHelper::OnJoinResponse...response nothing in map_IL respcode: %d transID: %d\n", msg.stcmn_hdr.respcode, msg.stcmn_hdr.transid);
    return 0;
  } 

  //get a local copy of the LObject
  objLO = i1->second;
  
  //Now remove from the lookuptable map
  m_IP.erase(i1);
  
  
  m_pPeer->ComposeJoinResp(objLO.pchIP, objLO.port, objLO.tt, msg);
  
  return 1;


}//OnJoinResponse



int32 MaintHelper::LookupPeer(char8* pchHashKey, int32 len, enum transtype tt, void* (*callback)(void*, int32), void* pApp) {

  struct peerinfo stNP;  //next peer
  LObject objLO;
  int32 ret;
  string strtransid;

  TRACE(4, "MaintHelper::LookupPeer...Begins\n", "");
  
  objLO.strKey = pchHashKey;
  objLO.callback = callback;
  objLO.pApp = pApp;
  objLO.isOrig = 1;
  
  //transport type
  objLO.tt = tt;

  //BUILD THE PLOOKUP OBJECT
  //Store the hash in plookup object
  objLO.stpl.stida.isPresent=1;
  memcpy(objLO.stpl.stida.id, pchHashKey, m_pPeer->m_HASH_SIZE);
  objLO.stpl.stida.stobj.length=idlen;
  objLO.stpl.stida.isPresent=idlen;
  objLO.stpl.num=0;
  objLO.stpl.isPresent = 1;


  //Now the unhashed id
  printf("LookupPeer: hash is: 0x");
  for(int i=0; i<idlen; i++) {
    printf("%x", objLO.stpl.stida.id[i]);
  }
  printf("\n");  
  
  //getNextHop
  ret=m_pPeer->getNextHop(objLO.stpl.stida.id, m_pPeer->m_HASH_SIZE, stNP );

  //where to send
  if(0 == ret) {
    //TRACE(3, "ILHelper::Insert...We are the Insert Node. No need to send\n", "");
    
    TRACE(3, "MaintHelper::LookupPeer...Looking at self...Not found\n", "");
    objLO.callback(NULL, 404);

  } else {
    TRACE(3, "MaintHelper::LookupPeer...Send it to next node\n", "");
    //send the put message
    m_pPeer->ComposeLookupPeer(stNP, objLO, strtransid);
    m_IP[strtransid] = objLO;
  }

  TRACE(3, "MaintHelper::LookupPeer...ends\n", "");

  return 1;

}//LookupPeer


int32 MaintHelper::OnLookupPeerRequest(struct message& msg, char* pchIP, int32 port, transtype tt) {
  struct peerinfo stPSelf, stPNext;
  int32 respCode=0;
  list<struct peerinfo> lstP;
  struct LObject objLO;
  string strtransid;

  lstP.clear();

  //get self peer
  m_pPeer->getSelfPeer(stPSelf);

  //get next hop
  if(0 == m_pPeer->getNextHop(msg.stpl.stida.id, m_pPeer->m_HASH_SIZE, stPNext)) {
    respCode = 200;
  } else {
    respCode = 302;
  }  
  

  //RECURSIVE vs. ITERATIVE CODE
  //Send the response if iterative
  if(0 == msg.stcmn_hdr.rori || 200 == respCode) {

    TRACE(3, "RManager::OnLookupObjectRequest...sending response\n", "");
    if(respCode==302) {
      lstP.push_back(stPNext);
    }
    m_pPeer->ComposeLookupPeerResp(lstP, lstP.size(), pchIP, port, tt, respCode);
    
  } else if(1 == msg.stcmn_hdr.rori && 302 == respCode) {

    //1)
    //recursive request
    objLO.callback = 0;        //this null pointer indicates that we
                           //should send the response upstream instead
                           //of giving it to the application
    strcpy(objLO.pchIP, pchIP);
    objLO.port = port;
    objLO.tt = tt;
      
    //2)    
    m_pPeer->ComposeLookupPeer(stPNext, objLO, strtransid, msg, tt);

    //3)
    m_IP[strtransid] = objLO;
    
  }//end else


  return 1;
}//OnLookupPeerRequest



int32 MaintHelper::OnLookupPeerResponse(struct message& msg) {
  map<string, LObject>::iterator i1;
  list<peerinfo>::iterator litr;
  list<peerinfo> lstP;
  struct LObject objLO;
  string strtransid;

  TRACE(2, "MaintHelper::OnLookupPeerResponse...m_IP map size: %d\n", m_IP.size() );

  //Find the object corresponding to this transaction
  i1=m_IP.find( msg.getTransId() );
  if( i1 != m_IP.end() ) {
    objLO=i1->second;    
  } else {
    TRACE(2, "MaintHelper::OnLookupPeerResponse...OnLookupPeerResponse response nothing in map_IL respcode: %d transID: %d\n", msg.stcmn_hdr.respcode, msg.stcmn_hdr.transid);
    return 0;
  }
  
  //Now remove the old transaction from map
  m_IP.erase(i1);

  if(objLO.callback==0 && msg.stcmn_hdr.rori==1) {
    //send response upstream
    lstP=msg.stlpeerinfo;
    m_pPeer->ComposeLookupPeerResp(lstP, lstP.size(), objLO.pchIP, objLO.port, objLO.tt, 200);
  } else {
    if(200 == msg.stcmn_hdr.respcode || 404 == msg.stcmn_hdr.respcode) {
      TRACE(2, "MaintHelper::OnLookupUserResponse...%d\n", msg.stcmn_hdr.respcode);
      
      if(200 == msg.stcmn_hdr.respcode) {
        printf("Peer found. %s : %d\n", msg.stpeerinfo.staddr.addr[0], msg.stpeerinfo.staddr.port[0]);
        strcpy(objLO.pchIP, msg.stpeerinfo.staddr.addr[0]);
      }
      //call the application function or send the response to client
      //pIL->callback(pIL->pApp, msg.stcmn_hdr.respcode);
      objLO.pPeer=m_pPeer;
      
      //Insert peer received in routing or neighbor table
      m_pPeer->Insert(msg.stpeerinfo,1);
      m_pPeer->Insert(msg.stlpeerinfo,1);
      
    }else {
      //respcode=302 then send query again
      m_pPeer->Insert(msg.stlpeerinfo,1);
      m_pPeer->ComposeLookupPeer(*msg.stlpeerinfo.begin(), objLO, strtransid);
      m_IP[strtransid] = objLO;
    }
  }
  return 1;

}//OnLookupPeerResponse



int32 MaintHelper::Set(char8* pchIP, uint16 port, enum transtype tt, struct nodeval& stnodeval, void* (*callback)(void*, int32), void* pApp) {

  LObject objLO;
  string strtransid;

  TRACE(4, "MaintHelper::Set...Begins\n", "");
  
  objLO.callback = callback;
  objLO.pApp = pApp;
  objLO.isOrig = 1;

  strcpy(objLO.pchIP, pchIP);
  objLO.port = port;
  
  //transport type
  objLO.tt = tt;
  
  gettimeofday(&objLO.senttime, NULL);

  TRACE(3, "MaintHelper::Set...Send it to IP: %s port: %d\n", objLO.pchIP, objLO.port);
  //send the set message
  m_pPeer->ComposeSet(objLO, stnodeval, strtransid);
  m_IP[strtransid] = objLO;


  TRACE(3, "MaintHelper::Set...ends\n", "");

  return 1;

}//Set



int32 MaintHelper::Reset(char8* pchIP, uint16 port, enum transtype tt, struct nodeval& stnodeval, void* (*callback)(void*, int32), void* pApp) {

  LObject objLO;
  string strtransid;

  TRACE(4, "MaintHelper::Reset...Begins\n", "");
  
  objLO.callback = callback;
  objLO.pApp = pApp;
  objLO.isOrig = 1;

  strcpy(objLO.pchIP, pchIP);
  objLO.port = port;
  
  //transport type
  objLO.tt = tt;
  
  gettimeofday(&objLO.senttime, NULL);

  TRACE(3, "MaintHelper::Reset...Send it to IP: %s port: %d\n", objLO.pchIP, objLO.port);
  //send the set message
  m_pPeer->ComposeReset(objLO, stnodeval, strtransid);
  m_IP[strtransid] = objLO;


  TRACE(3, "MaintHelper::Reset...ends\n", "");

  return 1;

}//Reset



int32 MaintHelper::GetDiagnostics(char8* pchIP, uint16 port, enum transtype tt, enum diag_type diagtype, void* (*callback)(void*, int32), void* pApp) {

  LObject objLO;
  string strtransid;

  TRACE(4, "MaintHelper::GetDiagnostics...Begins\n", "");
  
  objLO.callback = callback;
  objLO.pApp = pApp;
  objLO.diagtype = diagtype;
  objLO.isOrig = 1;

  strcpy(objLO.pchIP, pchIP);
  objLO.port = port;
  
  //transport type
  objLO.tt = tt;
  
  gettimeofday(&objLO.senttime, NULL);

  TRACE(3, "MaintHelper::GetDiagnostics...Send it to IP: %s port: %d\n", objLO.pchIP, objLO.port);
  //send the put message
  m_pPeer->ComposeDiagnostics(objLO, strtransid);
  m_IP[strtransid] = objLO;

  TRACE(3, "MaintHelper::GetDiagnostics...ends\n", "");

  return 1;

}//GetDiagnostics



int32 MaintHelper::OnGetDiagnosticsResponse(struct message& msg) {
  
  map<string, LObject>::iterator i1;
  list<peerinfo>::iterator litp;
  list<resourceobject>::iterator litro;
  list<turnalloc>::iterator litta;

  struct LObject objLO;
  
  TRACE(2, "MaintHelper::OnGetDiagnosticsResponse...m_IP map size: %d\n", m_IP.size() );

  //Find the object corresponding to this transaction
  i1=m_IP.find( msg.getTransId() );
  if( i1 != m_IP.end() ) {
    objLO=i1->second;    
  } else {
    TRACE(2, "MaintHelper::OnGetDiagnosticsResponse...nothing in map_IL respcode: %d transID: %d\n", msg.stcmn_hdr.respcode, msg.stcmn_hdr.transid);
    return 0;
  }
  
  //Now remove the old transaction from map
  m_IP.erase(i1);
  
  //routing table
  for(litp = msg.stdiagnostics.strt.table.begin(); litp!= msg.stdiagnostics.strt.table.end(); litp++) {
    objLO.lstrt.push_back(*litp);
  }

  //neighbor table
  for(litp = msg.stdiagnostics.stnt.table.begin(); litp!= msg.stdiagnostics.stnt.table.end(); litp++) {
    objLO.lstnt.push_back(*litp);
  }

  //resource table
  for(litro = msg.stdiagnostics.lstro.begin(); litro!= msg.stdiagnostics.lstro.end(); litro++) {
    objLO.lstro.push_back(*litro);
  }
  
  //relay list
  for(litp=msg.stdiagnostics.stlrelaylist.begin(); litp!=msg.stdiagnostics.stlrelaylist.end() ; litp++) {
    objLO.lstrl.push_back(*litp);
  }

  //turn allocations
  for(litta=msg.stdiagnostics.stlturnalloc.begin(); litta!=msg.stdiagnostics.stlturnalloc.end(); litta++) {
    objLO.lstta.push_back(*litta);
  }

  //bw
  objLO.stbw = msg.stdiagnostics.stbw;  
  //cpu usage of the program
  objLO.stcpuutil = msg.stdiagnostics.stcpuutil;
  //cpu usage of the machine
  objLO.sttcpuutil = msg.stdiagnostics.sttcpuutil;

  //If we had send the request
  if(objLO.isOrig) {
    if(NULL!=objLO.callback) {
      objLO.pPeer=m_pPeer;
      objLO.callback((void*)&objLO, msg.stcmn_hdr.respcode);
    }
  }

  return 1;

}//OnGetDiagnosticsResponse



int32 MaintHelper::OnTransactionTimeout(enum msgtype mtype, enum reqoritype ritype, string strtransid, char* pchIP, int32 port) {
  
  //PublishObject
  //LookupObject
  
  map<string, LObject>::iterator i1;
  struct LObject objLO;

  i1=m_IP.find(strtransid);
  if(i1 == m_IP.end() ) {
    TRACE(2, "MaintHelper::OnTransactionTimeout...no corresponding transaction\n", "");
    return 1;
  }//end if
  

  switch(ritype) {
  
  case eGETDIAGNOSTICS:
    if(0 != i1->second.callback ) {
      i1->second.callback( (void*)(&(i1->second)), 413);
    }
    break;

  default:
    break;

  }//end switch


  //erase from the inprogress map
  m_IP.erase(i1);

  return 1;

}//OnTransactionTimeout



int32 MaintHelper::OnSetRequest(struct message& msg, char* pchIP, int32 port, transtype tt) {
  
  TRACE(3, "MaintHelper::OnSetRequest...called %s %d\n", pchIP, port );
  
  //program bw, cpu, memory
  if(msg.stnodeval.stbw.isPresent) {
    g_stnodeval.stbw=msg.stnodeval.stbw;
  }
  if(msg.stnodeval.stcpuutil.isPresent) {
    g_stnodeval.stcpuutil=msg.stnodeval.stcpuutil;
  }
  if(msg.stnodeval.stmem.isPresent) {
    g_stnodeval.stmem=msg.stnodeval.stmem;
  }

  //machine's bw, cpu, memory
  if(msg.stnodeval.sttbw.isPresent) {
    g_stnodeval.sttbw=msg.stnodeval.sttbw;
  }
  if(msg.stnodeval.sttcpuutil.isPresent) {
    g_stnodeval.sttcpuutil=msg.stnodeval.sttcpuutil;
  }
  if(msg.stnodeval.sttmem.isPresent) {
    g_stnodeval.sttmem=msg.stnodeval.sttmem;
  }

  m_pPeer->ComposeSetResp(pchIP, port, tt); 

  return 1;

}//OnSetRequest


int32 MaintHelper::OnResetRequest(struct message& msg, char* pchIP, int32 port, transtype tt) {
  
  TRACE(3, "MaintHelper::OnResetRequest...called %s %d\n", pchIP, port );
  
  //program bw, cpu, memory
  if(msg.stnodeval.stbw.isPresent) {
    g_stnodeval.stbw.isPresent=0;
  }
  if(msg.stnodeval.stcpuutil.isPresent) {
    g_stnodeval.stcpuutil.isPresent=0;
  }
  if(msg.stnodeval.stmem.isPresent) {
    g_stnodeval.stmem.isPresent=0;
  }

  //machine's bw, cpu, memory
  if(msg.stnodeval.sttbw.isPresent) {
    g_stnodeval.sttbw.isPresent=0;
  }
  if(msg.stnodeval.sttcpuutil.isPresent) {
    g_stnodeval.sttcpuutil.isPresent=0;
  }
  if(msg.stnodeval.sttmem.isPresent) {
    g_stnodeval.sttmem.isPresent=0;
  }

  m_pPeer->ComposeResetResp(pchIP, port, tt); 

  return 1;

}//OnResetRequest



int32 MaintHelper::OnResponse(struct message& msg) {
  
  map<string, LObject>::iterator i1;
  list<peerinfo>::iterator litp;
  list<resourceobject>::iterator litro;

  struct LObject objLO;
  
  TRACE(2, "MaintHelper::OnResponse...m_IP map size: %d\n", m_IP.size() );

  //Find the object corresponding to this transaction
  i1=m_IP.find( msg.getTransId() );
  if( i1 != m_IP.end() ) {
    objLO=i1->second;    
  } else {
    TRACE(2, "MaintHelper::OnResponse...nothing in map_IL respcode: %d transID: %d\n", msg.stcmn_hdr.respcode, msg.stcmn_hdr.transid);
    return 0;
  }
  
  //Now remove the old transaction from map
  m_IP.erase(i1);
  
  //If we had send the request
  if(objLO.isOrig) {
    objLO.pPeer=m_pPeer;
    objLO.callback((void*)&objLO, msg.stcmn_hdr.respcode);
  }

  return 1;

}//OnResponse


void MaintHelper::Disconnect() {

  //remove the inprogress transactions
  m_IP.clear();
  
}//Disconnect


