/*
 * 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.
 */

#ifndef _WENGO
#include "client.h"
#include "trace.h"
#endif


#ifdef _WENGO
#include "p2pp/node/client.h"
#include "p2pp/common/trace.h"
#endif

#include "stimanager.h"

Client::Client() {

  //size of connected peers list is 10.
  m_CPsize=10;
  
  m_CPrefresh=20; //20 seconds
  gettimeofday(&m_tCP, NULL);
  m_tCP=addtimeval(m_tCP, m_CPrefresh, 0);

  m_CPka=60;      //base timer is 60 seconds
  m_CPrand=30;    //[60,90s] interval

}//Client



Client::~Client() {
  
}//~Client


void Client::Initialize() {
  m_uiTransId = 0;

  //m_objIL.setParams((Peer*)this);

  for(int i=0; i<m_HASH_SIZE; i++) {
    m_ID[i]=0;
  }

  m_eState = ePBOOTSTRAP;

  //Init TURN server
  //m_objSTI.InitializeServer();

  Peer::Initialize();

}//Initialize



void Client::OnTimer() {

  map<string, Transaction*>::iterator itr;
  list<string>::iterator litr;
  list<string> lint;
  list<LObject>::iterator i1;
  list<Internalpeerinfo>::iterator ipitr;
  struct LObject objLO;
  int32 ret;


  //CODE FOR interacting with command line
  for(i1=m_LO.begin(); i1 != m_LO.end(); i1++) {
    if( (*i1).ritype == ePUBLISHOBJECT) {

      if( (*i1).changeID == 0) {
        m_objRM.PublishObject( (char*)(*i1).strKey.c_str(), (*i1).strKey.size(), (char*)(*i1).strVal.c_str(), (*i1).strVal.size(), (*i1).tt, (*i1).callback, (*i1).pApp);
      } else {
        m_objRM.ChangeUserID( (char*)(*i1).strKey.c_str(), strlen( (char*)(*i1).strKey.c_str()) );
      }

    } else if((*i1).ritype == eLOOKUPOBJECT) {
      
      if( (*i1).conttype == eSTRINGVAL) {
        m_objRM.LookupObject((char*)(*i1).strKey.c_str(), (*i1).strKey.size(), (*i1).tt, (*i1).callback, (*i1).pApp);
      } else if( (*i1).conttype == eUSERINFO ) {
        m_objRM.LookupUser((char*)(*i1).strKey.c_str(), (*i1).strKey.size(), (*i1).tt, (*i1).callback, (*i1).pApp);
      } else if( (*i1).conttype == eSIP ) {
        m_objRM.LookupSIP((char*)(*i1).strKey.c_str(), (*i1).strKey.size(), (*i1).tt, (*i1).callback, (*i1).pApp);
      }
    } else if( (*i1).ritype == eGETDIAGNOSTICS ) {
      m_objMH.GetDiagnostics( (char*)(*i1).pchIP, (*i1).port, (*i1).tt, (*i1).diagtype, (*i1).callback, (*i1).pApp );
    } else if ( (*i1).ritype == eLEAVE ) {
      if( 1 == (*i1).disconnect) {
        m_eState = ePDISCONNECTED;

        //create a copy of call back function
        objLO.callback = (*i1).callback;
        objLO.pApp = (*i1).pApp;
        break;
      }
    }
    
  }//end for

  m_LO.clear();
  //CODE FOR interacting with command line ENDS
  

  ////////////////////////////////////////////////////////////////////  
  //  Process transaction maps starts
  ////////////////////////////////////////////////////////////////////
  
  if(ePDISCONNECTED != m_eState ) {

    //Transaction management code BEGINS
    //Check if transaction has timedout
    for(itr=m_TMap.begin(); itr!=m_TMap.end(); itr++) {
      
      ret = itr->second->OnTimer();
      
      //the transaction has expired
      if(0 == ret) {
        OnTransactionTimeout(itr->second);
	
        TRACE(2, "Client::OnTimer...deleting transaction state: %d\n", m_eState);
	
        //The transaction was allocated so it must be deleted
        delete itr->second;
        lint.push_back(itr->first);
      }
      
    }//end for
  
  
    for(litr=lint.begin(); litr!=lint.end(); litr++) {
      itr = m_TMap.find(*litr);
      if(itr != m_TMap.end() ) {
        m_TMap.erase(itr);
      }
    }//end for
    //Transaction management code ENDS

  } else {

    TRACE(2, "Client::OnTimer...deleting transaction state: %d (DISCONNECTED) map size: %d\n", m_eState, m_TMap.size() );
    
    for(itr=m_TMap.begin(); itr!=m_TMap.end(); itr++) {	
      //The transaction was allocated so it must be deleted
      delete itr->second;
    }//end for  

    //remove all transactions from map
    m_TMap.clear();
  }


  ////////////////////////////////////////////////////////////////////  
  //  Process transaction maps ENDS
  ////////////////////////////////////////////////////////////////////

  switch(m_eState) {

  case ePBOOTSTRAP:
    
    ComposeBootstrap();
    m_eState = ePBOOTSTRAPSENT;
    break;

  case ePCONNECTED:
    //if connectivity refresh timer is expired, check all the entries.
    //if any are expired, send them a refresh.
    if(isTimerExpired(m_tCP)) {
      
      for(ipitr=m_CP.begin(); ipitr!=m_CP.end(); ipitr++) {
        //If keepalive timer is expired, send it a keep-alive
        if( isTimerExpired(ipitr->m_expKA) ) {
          ComposeKeepAlive(ipitr->m_stP);
          setKATimer(ipitr->m_expKA);
        }
      }//end for

      //If connected peers list size is less than 10, get the routing table from
      //the first node in connected peers list.
      if(m_CP.size() > 0 && m_CP.size() < m_CPsize) {
        ComposeExchangeTable(m_CP.begin()->m_stP);
      }

      //set refresh timer
      setCPrefreshtimer(m_tCP);

    }//end if

    m_objRM.OnTimer();
    m_objSTI.OnTimer();
    break;

  case ePDISCONNECTED:
    TRACE(1, "Client::OnTimer...Transport disconnecting\n", "");
    m_pobjTransport->Disconnect();
    
    m_objMH.Disconnect(); //Maintenance helper disconnect
    m_objRM.Disconnect(); //Resource manager disconnect

    objLO.callback( (void*)&objLO, 200);
    break;

  default:
    break;

  }//end switch

  Peer::OnTimer();  

}//OnTimer


void Client::setKATimer(struct timeval& stT) {
  int32 temp=rand()%m_CPrand;
  temp+=m_CPka;

  gettimeofday(&stT, NULL);
  stT=addtimeval(stT,temp,0);

}//setKATimer


void Client::setCPrefreshtimer(struct timeval& stT) {
  gettimeofday(&stT, NULL);
  stT=addtimeval(stT, m_CPrefresh, 0);
}//setCPrefreshtimer


/**
* 1 if expired
* 0 if not expired
*/
int32 Client::isTimerExpired(struct timeval& stT) {
  struct timeval cur_time, diff_time;
  gettimeofday(&cur_time, NULL);

  diff_time=difftimeval(stT, cur_time);
  if(0 == diff_time.tv_sec && 0 == diff_time.tv_usec) {
    return 1;
  }

  return 0;
}//isCPrefreshexpired


int32 Client::OnReceive(uchar* buf, int len, char* pchIP, int32 port, transtype tt) {

  map<string, Transaction*>::iterator itr;
  struct sockaddr_in staddr;
  int addrlen;
  
  if(STIManager::IsStunPacket(buf, len)) {
    getSockAddr_in(pchIP, port, &staddr); 
    addrlen=sizeof(staddr);
    m_objSTI.ParseStunPacket(buf, len, &staddr, &addrlen,m_pobjTransport->getUDPSocket(), eSOCK_P2PP);
    return 1 ;
  } 
    
  //Initialize the message structure
  m_msg.Initialize();

  TRACE(2, "Client::OnReceive...received from IP address: %s port: %d\n", pchIP, port);

  //Parse the message
  m_msg.parse(buf, len);
  
  TRACE(2, "Client::OnReceive...received transid: %s\n", (char*)m_msg.getTransId().c_str() );

  
  TRACE(2, "Client::OnReceive...state: %d msgtype: %s\n", m_eState, pmsgtype[m_msg.stcmn_hdr.msgtype]);
  

  //At this point ACKs and retransmissions have been matched to the
  //existing transactions. So we have only new requests, response,
  //responseACK, and indications at this point. Now process them.  

  //if ACK
  if(1 == m_msg.stcmn_hdr.ack ) {
    OnACK(pchIP, port, tt);
    
  } else if(eREQUEST == m_msg.stcmn_hdr.msgtype) {    
    OnRequest(pchIP, port, tt);

  } else if(eRESPONSE == m_msg.stcmn_hdr.msgtype || eRESPONSEACK == m_msg.stcmn_hdr.msgtype) {
    return OnResponse(pchIP, port, tt);

  } else if(eINDICATION == m_msg.stcmn_hdr.msgtype) {
    OnIndication(pchIP, port, tt);
  }
  
  //Call Peer::OnReceive
  Peer::OnReceive(buf, len, pchIP, port, tt);

  return 1;
}//OnReceive



int32 Client::OnACK(char* pchIP, int32 port, transtype tt) {
  map<string, Transaction*>::iterator itr;

  //Find the transaction
  itr=m_TMap.find(m_msg.getTransId());

  //If transaction does not exist, discard the ACK
  if(itr==m_TMap.end() ) {
    TRACE(2, "Client::OnACK...ACK received...transaction does not exist %d\n", m_msg.stcmn_hdr.transid);
    return 1;
  }
     
  switch(m_msg.stcmn_hdr.msgtype) {
      
  case eREQUEST:
    if(eREQUEST == itr->second->m_msgtype) {
      TRACE(1, "Client::OnACK...ACK received for REQUEST\n", "" );
      itr->second->OnACK(m_msg);
    } else {
      TRACE(1, "Client::OnACK...expecting ACK for REQUEST but received otherwise\n", "" );
    }
    break;

  case eRESPONSE:
  case eRESPONSEACK:
  case eINDICATION:

    if(eRESPONSE == itr->second->m_msgtype || eRESPONSEACK == itr->second->m_msgtype || 
       eINDICATION == itr->second->m_msgtype) {

      TRACE(1, "Client::OnReceive....ACK received for RESPONSE, RESPONSEACK or INDICATION...Transaction size: %d\n", m_TMap.size() );
      delete itr->second;
      m_TMap.erase(itr);
      TRACE(2, "Client::OnReceive.....Transaction removed size: %d\n", m_TMap.size() );
    } else {
      TRACE(2, "Client::OnACK...expecting ACK for RESP, RESPACK, INDIC but received otherwise\n", "" );
    }
    break;

  default:
    break;
  }//end switch
  

  return 1;
}//OnACK



int32 Client::OnRequest(char* pchIP, int32 port, transtype tt) {

  list<peerinfo> lstP;
  list<resourceobject > lstro;

  //If received over unreliable transport send the ACK immediately
  if(tt == eUDP || tt == eDTLS) {   
    ComposeACK(pchIP, port);
  }

  switch(m_eState) {

  case ePCONNECTED:

    switch(m_msg.stcmn_hdr.reqoritype) {

    case eGETDIAGNOSTICS:
      //gettable
      //m_pKRT->getTable(lstP, tt);
      lstP.clear();
      lstro.clear();
      m_objRM.getResTable(lstro, tt);
      //m_msg.stdiagoptions.r=0;
      //m_msg.stdiagnostics.n=0;
      m_msg.stdiagoptions.ta=0;
      ComposeDiagnosticsResp(lstP, lstro, m_msg.stpeerinfo.staddr.addr[0], m_msg.stpeerinfo.staddr.port[0], tt);
      break;
      
    default:
      break;
    }//end switch
    break;
    
  default:
    break;

  }//end switch

  return 1;
  
}//OnRequest



int32 Client::OnResponse(char* pchIP, int32 port, transtype tt) {

  struct peerinfo stP;  
  list<peerinfo>::iterator litr;
  map<string, Transaction*>::iterator itr;
  struct LObject objLO;
  string strtransid;
  struct resourceobject ro;
  map <string, port_trans> addrmap;
  map <string, port_trans>::iterator itr2;
  Internalpeerinfo stIP;
  list<Internalpeerinfo>::iterator ipitr;
  struct timeval cur_time;
  struct message tMsg;

  //If received over unreliable transport send the ACK immediately
  if(tt == eUDP || tt == eDTLS) {   
    ComposeACK(pchIP, port);
  }

  //Find the transaction
  itr=m_TMap.find(m_msg.getTransId());

  TRACE(1, "Client::OnResponse...Transaction map size: %d state: %d\n", m_TMap.size(), m_eState ); 

  //response or responseACK received. check for matched requests
  if(itr != m_TMap.end() ) {   
    delete itr->second;
    m_TMap.erase(itr);
    
    TRACE(1, "Client::OnResponse.....Transaction terminated. size: %d\n", m_TMap.size() );

  } else {
    TRACE(1, "Client::OnResponse.....Transaction does not exist %d\n", m_TMap.size() );
    return 1;
  }  
  

  switch(m_eState) {

  case ePBOOTSTRAPSENT:
    if(eBOOTSTRAP==m_msg.stcmn_hdr.reqoritype) {

      //Update global idlen
      idlen = m_msg.stp2poptions.halglen;

      getSelfID();

      TRACE(2, "Client::OnResponse...Bootstrap...num peers: %d\n", m_msg.stlpeerinfo.size() );

      //Create bootstrap node list
      CreateBNList(m_BSP);
      UpdateCPList(m_BSP);

      //Check if another node was returned. If we are the only node,
      //then stlpeerinfo should be one.
      if(m_msg.stlpeerinfo.size() <= 1) {
        printf("Error: no peer\n");
        exit(1);
      }

      //the painful join process
      m_eState = ePLOOKUPSTUN;
      m_eSubState = eS_PNONE;
      
      TRACE(2, "Client::OnResponse...Bootstrap...num peers: %d\n", m_msg.stlpeerinfo.size() );

      objLO.conttype = eSTUN_PORT;
      gettimeofday(&cur_time, NULL);
      srand(cur_time.tv_usec);
      int rndmIndex=rand()%(m_BSP.size());
      for(litr=m_BSP.begin(); litr!=m_BSP.end(); litr++) {
	      if(rndmIndex==0){
          m_objRM.LookupStunPort( *litr, eUDP ) ;
          m_itrSTind=litr;
	        break;
	      }
	      rndmIndex--;
      }    

    }//end if
    break;

  case ePLOOKUPSTUN:

    //store the stun server response
    if(200 == m_msg.stcmn_hdr.respcode) {

      ro=*(m_msg.lstro.begin());
      strncpy(m_STUN_IP, ro.staddr.addr[0],IPADDRLEN);
      m_STUN_PORT=ro.staddr.port[0];
      m_objSTI.UpdateRelayList(ro.stpeerinfo, m_STUN_PORT);

      printf("STUN server found: %s:%d\n", (char*)m_STUN_IP, m_STUN_PORT);

      //initialize STI manager
      m_pobjTransport->getAddrPort(addrmap);
      itr2=addrmap.begin();
      m_objSTI.Initialize(m_STUN_IP, m_STUN_PORT, ro.stpeerinfo, m_BSP, itr2->first.c_str());
      m_objSTI.InitializeClient(m_pobjTransport->getUDPSocket(), itr2->first.c_str(), itr2->second.port);
      //find NAT type and return if behind NAT
      
    }
    m_eState = ePJOIN;
    //Init TURN server
    //m_objSTI.InitializeServer();

    //If the mode was AUTO, our initial state was CLIENT. If the NAT type
    //is open, join as PEER now.
    if(m_objSTI.getNatType()==PJ_STUN_NAT_TYPE_OPEN && m_NODE_TYPE == eAUTO) {
      //exit client mode and enter peer mode
      m_NODE_TYPE=ePEER;
      ComposeJoin( *(m_BSP.begin()) );
      printf("NAT type is open. Joining as PEER\n");
      TRACE(1, "CLient::OnResponse...NAT Type is Open. Joining as Peer\n", "");
      return 0;
    } else {
      ComposeJoin( *(m_BSP.begin()) );
    }
    break;
  
  case ePJOIN:
    
    switch(m_eSubState) {
    
    case eS_PNONE:
      
      if(302 == m_msg.stcmn_hdr.respcode) {
        TRACE(2, "Client::OnResponse...302 for Join...Send Join\n", "");

        m_BSP.pop_front();

        printf("302 response %s %d\n", m_msg.stpeerinfo.staddr.addr[0], m_msg.stpeerinfo.staddr.port[0]);

        //if there is a peerinfo present
        //TODO: insert in the BSP list
        if(m_msg.stlpeerinfo.size() > 0) {
          litr=m_msg.stlpeerinfo.begin();
	  
          ComposeJoin(*litr);
        } else {
          printf("Error in Join...There was no peerinfo object in 302\n");
        }

        //Insert the peer encountered along the path in the connected peers list
        //Does not work in recursive, so will obtain the routing table.
        stIP.m_stP=m_msg.stpeerinfo;
        setKATimer(stIP.m_expKA);
        UpdateCPList(stIP, true);

      } else if(200 == m_msg.stcmn_hdr.respcode) {
        TRACE(2, "Client::OnResponse...200 for Join\n", "");
        m_eState = ePCONNECTED;

        //Insert the peer encountered along the path in the connected peers list
        stIP.m_stP=m_msg.stpeerinfo;
        setKATimer(stIP.m_expKA);
        UpdateCPList(stIP, true);

        printf("JOIN SUCCESSFUL === Peer-Id: 0x");
        for(int i=0; i<idlen; i++) {
          printf("%02x", m_ID[i]);
        }
        printf("\n");

        //Set the keep-alive and update timers
        //setKATTimer();

        //Insert itself as a resource-object in its own
        //resource-object
        //FIXME: UDP
        //inform the application
        objLO.ritype = eBOOTSTRAP;
        objLO.pApp = m_pApp;
        if(appCallBack){
          appCallBack( (void*)&objLO, 200);
        }
        m_objRM.PublishUserInfo( (char*)m_USERID, strlen((char*)m_USERID), eUDP );	
      }//end if(200==respcode)
      break;

    default:
      break;

    }//end switch    

    break; //end join

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

      case ePUBLISHOBJECT:
        //Insert the peer encountered along the path in the connected peers list
        //at the end of the list
        if(m_CP.size() <= m_CPsize) {
          stIP.m_stP=m_msg.stpeerinfo;
          setKATimer(stIP.m_expKA);
          UpdateCPList(stIP, true);
        }
        m_objRM.OnPublishObjectResponse(m_msg);
        break;

      case eLOOKUPOBJECT:
        {
        list<struct resourceobject>::iterator itro;
        for(itro=m_msg.lstro.begin();itro!=m_msg.lstro.end();++itro)  {
          if(itro->conttype==eSTUN_PORT){
            ro=*(m_msg.lstro.begin());
            m_objSTI.UpdateStunPort(ro.stpeerinfo, ro.staddr.port[0]);
          }
        }
        m_objRM.OnLookupObjectResponse(m_msg);
        }
        break;

      case eGETDIAGNOSTICS:
        m_objMH.OnGetDiagnosticsResponse(m_msg);

        //update RTT, bw, CPU and other information
        m_objSTI.UpdateRelayList(m_msg);
        break;

      case eKEEPALIVE:
        //update any RTT
        //m_objSTI.UpdateRelayList(m_msg.stpeerinfo, 1);
        break;

      case eEXCHANGETABLE:
        //Iterate over the exchange table 
        //If our CP list size is less than m_CPsize, insert it in out CP list
        for(list<peerinfo>::iterator litr2=m_msg.strt.table.begin(); (litr2!=m_msg.strt.table.end()) && (m_CP.size() < m_CPsize); litr2++) {          
          stIP.m_stP=*litr2;
          setKATimer(stIP.m_expKA);

          UpdateCPList(stIP, false);
        }//end for
        //update relay list
        //list<peerinfo> lstP;
        //lstP.push_back(m_msg.stpeerinfo);
        //FIXME: we are passing stun port of zero
        m_objSTI.UpdateRelayList(m_msg.stpeerinfo, 0);
        tMsg=m_msg;
        m_objSTI.UpdateRelayList(tMsg.strt.table);

        break;

      }//end switch
        
      
    }//case ePCONNECTED
    
    break;

  default:
    break;

  }//end switch
  

  return 1;

}//OnResponse



uint32 Client::getNextHop(uchar* peerid, uint32 len, struct peerinfo& stP) {
  if(m_CP.size() > 0) {
    stP = (m_CP.begin())->m_stP;
    return 1;
  }

  return 0;

}//getNextHop



int32 Client::OnIndication(char* pchIP, int32 port, transtype tt) {
  
  return 1;
  
}//OnIndication


void Client::OnTransactionTimeout(Transaction* pobjTrans) {
  
  struct peerinfo stpeer;
  struct LObject objLO;
  list<peerinfo>::iterator litr;
  struct timeval cur_time;

  TRACE(2, "Client::OnTransactionTimeout...state %d size: %d\n", m_eState, m_BSP.size() );
  
  //Remove timeout peer from connected peers list
  RemovePeer(pobjTrans->m_pchIP, pobjTrans->m_uiPort);
  //Bug Fixed: no allocate refresh
  m_objSTI.RemovePeerExceptTop(pobjTrans->m_pchIP, pobjTrans->m_uiPort);

  switch(m_eState) {

  case ePLOOKUPSTUN:

    //the painful join process
    m_eSubState = eS_PNONE;

    TRACE(2, "Client::OnTransactionTimeout...LookupSTUN failed: %s\n", m_itrSTind->staddr.addr[0] );
    
    //remove the failed STUN server
    m_BSP.erase(m_itrSTind);
	
    if(m_BSP.size() == 0) {
      m_eState = ePFAILURE;
      
      printf("Client cannot connect to any bootstrap peer. Exiting.\n");
      objLO.ritype = eBOOTSTRAP;
      objLO.pApp = m_pApp;
      
      appCallBack( (void*)&objLO, 400);

    } else {

      //Among the bootstrap candidates returned, randomly select a one
      //and lookup STUN server port. At present, the STUN server to which a 
      //request is sent simply returns itself.
    
      objLO.conttype = eSTUN_PORT;
      gettimeofday(&cur_time, NULL);
      srand(cur_time.tv_usec);
      int rndmIndex=rand()%(m_BSP.size());
      for(litr=m_BSP.begin(); litr!=m_BSP.end(); litr++) {
        if(rndmIndex==0) {
          m_objRM.LookupStunPort( *litr, eUDP ) ;
          m_itrSTind = litr;
          break;
	    }
        rndmIndex--;
      }//end for
    }
    break;

  case ePJOIN:
    
    TRACE(2, "Client::OnTransactionTimeout...size: %d\n", m_BSP.size() );
    m_BSP.pop_front();

    if(m_BSP.size() > 0) {
      
      stpeer = *m_BSP.begin();
      TRACE(2, "Client::OnTransactionTimeout...Sending Join...BSP...size: %d\n", m_BSP.size() );
      ComposeJoin(*m_BSP.begin());

    } else {
      m_eState = ePFAILURE;
      objLO.ritype = eBOOTSTRAP;
      objLO.pApp = m_pApp;
      
      appCallBack( (void*)&objLO, 400);
    }

    break;

  case ePCONNECTED:
    //If connected peers list is zero, inform the application that we have
    //become disconnected.
    if(m_CP.size() == 0) {
      objLO.ritype = pobjTrans->m_reqoritype;
      objLO.pApp = m_pApp;
        
      appCallBack( (void*)&objLO, 400);
      return;
    }

    m_objRM.OnTransactionTimeout(pobjTrans->m_msgtype, pobjTrans->m_reqoritype, pobjTrans->getTransId(), pobjTrans->m_pchIP, pobjTrans->m_uiPort);

    //Maintenance helper transaction timeout
    m_objMH.OnTransactionTimeout(pobjTrans->m_msgtype, pobjTrans->m_reqoritype, pobjTrans->getTransId(), pobjTrans->m_pchIP, pobjTrans->m_uiPort);
    break;

  default:
    break;
  }//end switch
  
}//OnTransactionTimeout



void Client::ShowRT() {
  
}//ShowRT



void Client::ShowNT() {
  
  
}//ShowNT



void Client::ShowRO() {
  
  
}//ShowRO


void Client::ShowCP() {
  
  struct peerinfo stP;

  if(m_CP.size() > 0 ) {
    stP = m_CP.begin()->m_stP;

    printf("------Client------\n");
    printf("Connected Peer IP: %s port: %d\n", stP.staddr.addr[0], stP.staddr.port[0]);
    printf("Total peers in connect list: %d\n", m_CP.size() );
  } 

}//ShowCP

void Client::ShowCPlist() {
  
  list<Internalpeerinfo>::iterator litr;
  struct timeval diffKA_time, curr_time;
  BigInt lpeerid; //b, result, max, bindex, 
  int32 len;

  len = m_HASH_SIZE;

  printf("-------Client-------\n");
  printf("Total CP entries: %d \n", m_CP.size() );

  //current time
  gettimeofday(&curr_time, NULL);

  //iterate over CP list
  for(litr=m_CP.begin(); litr!=m_CP.end(); litr++) {
      
    //create the bigint out of peerid
    uchar_to_bigint(litr->m_stP.stpeerid.id, len, lpeerid);
      
    cout<<endl<<"   "<<"0x"<<lpeerid.ToHex()<<" ";

    //calculate the time this node was last seen
    diffKA_time = difftimeval(litr->m_expKA, curr_time);

    for(int j=0; j<litr->m_stP.staddr.num; j++) {
      cout<<"("<<litr->m_stP.staddr.addr[0]<<" "<<litr->m_stP.staddr.port[0]<<") ";
      printf("KA: %us", (uint32)diffKA_time.tv_sec);

      //if RTT is present
      //printf(" RTT: %ums ", litr->m_tRTT.tv_sec*1000 + litr->m_tRTT.tv_usec / 1000 );	
    }
    cout<<";";

    printf("\n");
     
  }//end for

}//ShowCPlist


void Client::ShowRelayServerList() {
  m_objSTI.ShowRelayServerList();
}//ShowRelayServerList

int32 Client::UpdateCPList(Internalpeerinfo & stIP, bool pushfront) {
  list<Internalpeerinfo>::iterator itr, fitr;
  int32 flag=0;
  
  for(itr=m_CP.begin(); itr!=m_CP.end(); itr++) {
    if( 0 == strcmp(itr->m_stP.staddr.addr[0], stIP.m_stP.staddr.addr[0]) &&
      itr->m_stP.staddr.port[0] == stIP.m_stP.staddr.port[0]) {
      fitr=itr;
      flag=1;
      break;
    }
  }//end for

  //If relay found, return
  if(1==flag) {
    return 0;
  }

  //add to CP list
  if(pushfront==true) {
    m_CP.push_front(stIP);
  }else {
    m_CP.push_back(stIP);
  }

  return 1;
}

int32 Client::UpdateCPList(list<peerinfo>& lstP) {
  Internalpeerinfo stIP;

  for(list<peerinfo>::iterator litr2=lstP.begin(); (litr2!=lstP.end()) && (m_CP.size() < m_CPsize); litr2++) {          
    stIP.m_stP=*litr2;
    setKATimer(stIP.m_expKA);

    UpdateCPList(stIP, false);
  }//end for
  
  return 1;
}//updateCPList

int32 Client::GetSIPObject(struct resourceobject& stRo) {

  map<string, port_trans> addrmap;
  map<string, int> relaymap;
  map<string, port_trans>::iterator itr;
  map<string, int>::iterator itrrelay;

  stRo.isPresent=1;
  stRo.conttype=eSIP;
  stRo.staddr.isPresent=1;
  stRo.staddr.num=1;

  relaymap.clear();
  //add relay address
  int i=0;
  m_objSTI.GetRelayAddr(5060, relaymap);
  if(relaymap.size()) {
    for(itrrelay=relaymap.begin(); itrrelay!=relaymap.end(); itrrelay++, i++) {
      
      strncpy(stRo.staddr.addr[i], (char*)itrrelay->first.c_str(), 16);
      stRo.staddr.port[i] = itrrelay->second;
      stRo.staddr.tt[i]   = eUDP;
      stRo.staddr.ht[i]   = eRELAYED;
      stRo.staddr.ipver[i]=4;
      
      stRo.staddr.R[i] = 0;
      stRo.staddr.foundation[i] = 0;
      stRo.staddr.compid[i] = 0;
      stRo.staddr.priority[i] = 0;
      
    }//end for
  } else {
    //Get the addressinfo object from transport
    m_pobjTransport->getAddrPort(addrmap);
    for(itr=addrmap.begin(); itr!=addrmap.end(); itr++, i++) {
      
      strncpy(stRo.staddr.addr[i], (char*)itr->first.c_str(), 16);
      stRo.staddr.port[i] = 5060;
      stRo.staddr.tt[i]   = itr->second.tt;
      stRo.staddr.ht[i]   = itr->second.ht;
      stRo.staddr.ipver[i]=4;
      
      stRo.staddr.R[i] = 0;
      stRo.staddr.foundation[i] = 0;
      stRo.staddr.compid[i] = 0;
      stRo.staddr.priority[i] = 0;
      
    }//end for
  }//end if

  return 1;
}//GetSIPObject

void Client::UpdateRelayList(list<peerinfo>& lstP){
  m_objSTI.UpdateRelayList(lstP);
}


/**
 * 
 * 0 peer does not exist
 * 1 peer exists and removed.
 */
int32 Client::RemovePeer(const char* pchIP, const uint32 port) {
  list<Internalpeerinfo>::iterator itr, fitr;
  int32 flag=0;
  struct LObject objLO;
  
  for(itr=m_CP.begin(); itr!=m_CP.end(); itr++) {
    if( 0 == strcmp(itr->m_stP.staddr.addr[0], pchIP) &&
      itr->m_stP.staddr.port[0] == port) {
      fitr=itr;
      flag=1;
      break;
    }
  }//end for

  //If relay found, remove it from the relay list
  if(1==flag) {
    m_CP.erase(fitr);
    return 1;
  }

  //If there are no peers in the CP list, we have become disconnected
  //from the network
  if(m_CP.size()==0) {
    objLO.callback=Node::appCallBack;
    objLO.pApp=Node::m_pApp;
    objLO.ritype=eLEAVE;
    Peer::m_eState=ePFAILURE;
    appCallBack( &objLO, 400);
  }

  return 0;
}//RemovePeer

void Client::ShowRelayAllocations() {
  m_objSTI.ShowRelayAllocations();
}//showrelayallocations


