/*
 * 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 "trace.h"
#include "stimanager.h"
#include "peer.h"


//Initialize static variables
string STIManager::m_sSelfIP="";
uint32 STIManager::NUMRELAYS;
int32 STIManager::m_inCall;
enum pj_stun_nat_type STIManager::m_eNatType;

struct addr_t STIManager::m_StunServer;

map<uint16, struct addr_t> STIManager::m_p2pRelay;
map<uint16, struct addr_t> STIManager::m_p2pSrflex;
map<uint16, ClientOptions*> STIManager::m_p2pCO;
mutex_i STIManager::m_mutex;

list<struct Internalrelaypeerinfo> STIManager::m_lRS;

ServerOptions STIManager::m_sSOp;

Peer* STIManager::m_pPeer;

STIManager::STIManager () : m_bDataRecvd(0),
			    m_bPendingAllocateRefresh(false)
{
  m_RT=20; //check relay list every KA seconds

  gettimeofday(&m_tRT, NULL);
  m_tRT=addtimeval(m_tRT, m_RT, 0);

  m_KA=60; //keepalive base
  m_KArand=30;
  //m_RAlloc=30;
  m_RAlloc=5;

  STIManager::NUMRELAYS=10;

  //inCall is initially 0
  STIManager::m_inCall=0;
  
  //turn server changed flag
  //if our turn server timesout, we set this flag to 1
  m_turnserverchanged=0;

}//STIManager::STIManager


STIManager::~STIManager() {
  //close TURN server
  //shutdown_server();
  //destroy ice sess
  uint16 port;
  map<uint16, struct addr_t>::iterator itr1;
  map<uint16, ClientOptions*>::iterator itr2;

  if(m_sOp.sock) {
    close(m_sOp.sock);
  }

  destroy_ice_sess(&m_sOp,m_sOp.ice);
  port=ntohs(m_sOp.peer_addr.sin_port);
  
  EraseList(m_p2pSrflex, port);
  EraseList(m_p2pRelay, port);

  /*
  itr1=m_p2pSrflex.find(port);
  if(itr1!=m_p2pSrflex.end()){
	  m_p2pSrflex.erase(itr1);
  }  
  itr1=m_p2pRelay.find(port);
  if(itr1!=m_p2pRelay.end()){
	  m_p2pRelay.erase(itr1);
  }
  */

  itr2=m_p2pCO.find(port);
  if(itr2!=m_p2pCO.end()){
  	m_p2pCO.erase(itr2);
  }
}//STIManager::~STIManager


void STIManager::setParams(Peer* pPeer) {
  m_pPeer = pPeer;  
}//setParams


/**
 * OnTimer() is only called from Client and P2PP code.
 * It is not called from SIP or RTP.
 * 1) Check STUN Allocate timer for the top most relay entry
 *   If timer expired
 *     Remove the port for which timer expired
 *     Pop the top most relay list
 *     If relay list is empty 
 *       return
 *     Find the first entry in the relay list with a non-zero stun port
 *     If found, 
 *       Change relay server in m_sOp structure
 *   Send STUN Allocate refresh request to the server in m_sOp structure.
 *
 * 2) Check relay keep-alive timer
 *  If an entry in the relay list has P2PP keepalive timer expired
 *    If stun port is zero
 *      send lookupstunport
 *    else
 *      send keepalive
 */

void STIManager::OnTimer() {
  map<uint16, struct addr_t>::iterator itr1;
  list<Internalrelaypeerinfo>::iterator litr, ritr;
  Internalrelaypeerinfo stRIP;
  int flag=0, ret=0, isRemoved=0;
  struct timeval cur_time, diff_time;
  
  //TODO: OnTimer is
  //if(m_nat_type==open) {
  //  return;
  //} FIXME
  if(m_eNatType==PJ_STUN_NAT_TYPE_UNKNOWN
    || m_eNatType==PJ_STUN_NAT_TYPE_ERR_UNKNOWN
    || m_eNatType==PJ_STUN_NAT_TYPE_OPEN ) {
      return;
  }

  gettimeofday(&cur_time, NULL);
	diff_time=difftimeval(m_sReallocate, cur_time);

  //always reset this flag
  m_turnserverchanged=0;

  if(0==diff_time.tv_sec && 0==diff_time.tv_usec) {

    //if TURN server does not respond to allocate refresh
	  if(m_bPendingAllocateRefresh==true) {	    

      //Remove the TURN server for all corresponding TURN allocations
      for(map<uint16, ClientOptions*>::iterator itr1=m_p2pCO.begin(); itr1!=m_p2pCO.end(); itr1++ ) {
        //port corresponds to the protocol for which this stimanager is running.
        uint16 port=ntohs(itr1->second->peer_addr.sin_port);

        //clear relay which is no more valid
        EraseList(m_p2pRelay, port);
        EraseList(m_p2pSrflex, port);

      }//end for


      if(m_lRS.size() > 0) {
        //if server at top of relay list is same as in m_StunServer then remove
        //this is to guard againt removal from both 'removepeer and ontimer'
        if(m_lRS.begin()->m_iStunPort==m_StunServer.port &&
          strcmp(m_lRS.begin()->m_stP.staddr.addr[0], m_StunServer.ip)==0 ) {
            
            //Remove it from the CP list
            m_pPeer->RemovePeer(m_lRS.begin()->m_stP.staddr.addr[0], m_lRS.begin()->m_stP.staddr.port[0]);
            m_turnserverchanged=1;

            m_lRS.pop_front();
            isRemoved = 1;
        }
      }
	    
      //printf("relay list size: %d\n", m_lRS.size() );

      //If the relay list has become empty for some reason.
      if(m_lRS.size() == 0) {
        TRACE(1, "STIManager::OnTimer...relay service list size is ZERO. UpdateRelay process MUST RUN\n", "");
        return;
      }      

    }//end if

    
    ret=FindNewRelay(ritr, isRemoved);
    //If we found a better candidate and we were not in call
    if(1==ret && 0 == STIManager::m_inCall) {
      stRIP=*ritr;
      m_lRS.erase(ritr);       //remove it from the list
      m_lRS.push_front(stRIP); //insert at the top of the list      

      flag=1;
      //Now copy the stun server with a non-zero stun port into pjnath stun structures
      strncpy(m_StunServer.ip, stRIP.m_stP.staddr.addr[0], IPADDRLEN);  	    
      m_StunServer.port=stRIP.m_iStunPort;        
    }//end if
      
  
    for(map<uint16, ClientOptions*>::iterator itr1=m_p2pCO.begin(); itr1!=m_p2pCO.end(); itr1++ ) {
      if(flag==1) {
        strcpy(itr1->second->ch_srv_addr, m_StunServer.ip);
		    sprintf(itr1->second->ch_srv_port, "%d", m_StunServer.port);
        change_stun_srv(itr1->second, itr1->second->ch_srv_addr, itr1->second->ch_srv_port);

        printf("Trying new stun server: %s:%s\n", itr1->second->ch_srv_addr, itr1->second->ch_srv_port);
        
      }

      //Send an Allocate request to the relay entry. At this point, we know that
      //there must be at least one relay entry in our relay list. It could be that 
      //that we could not select a new STUN server because all of them had a zero
      //port. In this case, ClientOptions in m_p2pCO will point to
      //to the old relay because our new relay has a zero stun port.
      //There is no harm in sending another Allocate request to our failed relay. 
      //FIXME
      send_allocate_request(itr1->second->sock/*pobjTrans->getUDPSocket()*/, 1, itr1->second);

    }//for
  
    m_bPendingAllocateRefresh=true;
    gettimeofday(&m_sReallocate, NULL);
    m_sReallocate=addtimeval(m_sReallocate, m_RAlloc, 0);


  }//if reallocate timer expired


  //(2) Check keep-alive timers of relay list
  //We check each relay list timer every m_tRT seconds
  //It is possible for KA timer of an entry to remain zero
  //for a while till the next m_tRT timer is expired.
  OnTimerKAET();

}//STIManager::OnTimer


/**
 * OnTimerKAET ... OnTimerKeepAliveExchangeTable()
 * 
 *
 */
void STIManager::OnTimerKAET() {
  struct timeval diff_time, cur_time;  
  list<Internalrelaypeerinfo>::iterator itr, ritr;

  gettimeofday(&cur_time, NULL);
  
  //check if relay list maintenance timer has expired
  diff_time=difftimeval(m_tRT, cur_time);

  //relay list maintenance timer expired
  if(0 == diff_time.tv_sec && 0 == diff_time.tv_usec) {    
    
    /*
    //code for checking if a better relay exists
    ret=FindNewRelay(ritr, 0);
    if(1==ret) {
      rtt=ritr->m_tRTT.tv_sec*1000+ritr->m_tRTT.tv_usec/1000;

      printf( "NEW RELAY is: %s %d %u\n", ritr->m_stP.staddr.addr[0], 
        ritr->m_stP.staddr.port[0], rtt);
    }
    //code ends
    */

    for(itr=m_lRS.begin(); itr!=m_lRS.end(); itr++) {
      diff_time=difftimeval(itr->m_expKA, cur_time);

      //if not refreshed, refresh it now.
      if(0 == diff_time.tv_sec && 0 == diff_time.tv_usec) {
        
        if(0 == itr->m_iStunPort) {
          m_pPeer->m_objRM.LookupStunPort(itr->m_stP, eUDP);
        } else {       
          //m_pPeer->ComposeKeepAlive(itr->m_stP);

          //we do not request any other field.
          //for future, we need to request machine's cpu, memory, idletime, bw
          m_pPeer->m_objMH.GetDiagnostics(itr->m_stP.staddr.addr[0], itr->m_stP.staddr.port[0], 
                                          eUDP, eGET_NONE, NULL, NULL);  
        }

        //the time we send this request to calculate RTT
        gettimeofday( &(itr->m_tLastSent), NULL);

        //set the next keep-alive timer
        setKATimer(itr->m_expKA);
      }

    }//end for

    //Always obtain exchangetable from the first node
    if(m_lRS.size() < STIManager::NUMRELAYS && m_lRS.size()>0) {
      m_pPeer->ComposeExchangeTable(m_lRS.begin()->m_stP);
    }
    //set next relay list check timer
    gettimeofday(&m_tRT, NULL);
    m_tRT=addtimeval(m_tRT, m_RT, 0);

  }//end if

}//OnTimerKAET



void STIManager::setKATimer(struct timeval& stT) {
  int32 temp;
  

  //set the next KA value
  temp=rand()%m_KArand;
  temp+=m_KA;

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

}//setRTTimer


/**
 *Initialize()
 *Initialize my STUN IP port, in peer and client. We want to determine our NAT
 *type with this STUN server.
 */
int32 STIManager::Initialize(char8 *pchIP, uint16& iPort, struct peerinfo& stunpeer, list<peerinfo>& lstP, const char *ownIP) {
  list<peerinfo>::iterator itr;
  Internalrelaypeerinfo ipeerinfo;

  //create the mutex...MUST be the first thing in this function
  AllocMutexI(m_mutex);

  m_sSelfIP=ownIP;
  //initialize
  ClearList(m_p2pRelay);
  ClearList(m_p2pSrflex);

  m_lRS.clear();
  m_eNatType=PJ_STUN_NAT_TYPE_UNKNOWN;

  //store Stun Server addr
  strncpy(m_StunServer.ip, pchIP, IPADDRLEN);
  m_StunServer.port=iPort;

  //create list of Relay servers
  ipeerinfo.m_stP=stunpeer;
  ipeerinfo.m_iStunPort=iPort;
  m_lRS.push_back(ipeerinfo);
  for(itr=lstP.begin(); itr!=lstP.end() && m_lRS.size()<=STIManager::NUMRELAYS; itr++) {
    //If STUN/TURN server already exists in our list
    //do not add it
    if(strcmp(itr->staddr.addr[0],stunpeer.staddr.addr[0])==0 
      && itr->staddr.port[0] == stunpeer.staddr.port[0]) {
        continue;
    }
    ipeerinfo.Initialize();
    ipeerinfo.m_iStunPort=0;
    ipeerinfo.m_stP = *itr;

    //insert new STUN/TURN server at the back of the list
    //StunPort is zero, and we should obtain it using LookupSTI.
    m_lRS.push_back(ipeerinfo);
  }
  
  //initialize pjlib, pjlib-util, and pjnath libraries.
  //IMP: this function sets the pjlog level. Change the level to 5 for debugging.
  init_pjnath();  

  return 1;
}//Initialize


int32 STIManager::DeInitialize() {
  //TODO: call this function from peer destructor
  DeallocMutexI(m_mutex);

  return 1;
}//DeInitialize


/**
 *InitializeClient()
 *This function performs NAT detection and stores the result in m_eNatType.
 *For every other NAT type, it sends a TURN Allocate request.
 **/
void STIManager::InitializeClient(int sock, const char *pchIP, uint16 iPort) {
  //create client options object
  memset(&m_sOp, 0, sizeof(struct client_options));
  strcpy(m_sOp.ch_srv_addr, m_StunServer.ip);
  sprintf(m_sOp.ch_srv_port,"%d", m_StunServer.port);

  //insert in <port, ClientOptions&> map
  m_p2pCO[(uint16)iPort]=&m_sOp;

  //initialize client options object
  m_sOp.sock=sock;
  m_sOp.peer_addr.sin_family=AF_INET;
  m_sOp.peer_addr.sin_addr.s_addr=inet_addr(m_sSelfIP.c_str());
  m_sOp.peer_addr.sin_port=htons(iPort);
  memset(m_sOp.peer_addr.sin_zero,'\0', sizeof(m_sOp.peer_addr.sin_zero));

  m_sOp.use_fingerprint=0;
  init_client(&m_sOp);

  //set local ice cand - host	
  set_ice_local_cand_host(&m_sOp,(pj_sockaddr_in*)&m_sOp.peer_addr);

  m_bDataRecvd=0;
  m_bRunningICEChecks=0;

  //perform nat detection (ONLY ONCE)
  if(m_eNatType==PJ_STUN_NAT_TYPE_UNKNOWN) {
	  perform_detection(&m_sOp);
	  m_eNatType=m_sOp.nat_type;
  }

  //if NATtype!= open then allocate and store relay address
  if(m_eNatType==PJ_STUN_NAT_TYPE_SYMMETRIC_UDP || 
     m_eNatType==PJ_STUN_NAT_TYPE_FULL_CONE ||
     m_eNatType==PJ_STUN_NAT_TYPE_SYMMETRIC ||
     m_eNatType==PJ_STUN_NAT_TYPE_PORT_RESTRICTED ||
     m_eNatType==PJ_STUN_NAT_TYPE_RESTRICTED) {
    send_allocate_request(sock/*pobjTrans->getUDPSocket()*/, 1,
			  &m_sOp);
  }
}//InitializeClient


/*
 *This function is called from media. When we receive remote SDP, we 
 *want to run ICE connectivity tests on it.
 */
void STIManager::ListenForICE() {
	listen_for_ice(&m_sOp);
}//InitICE


/*
 *InitializeServer()
 *called from peer after peer changes its state to ePCONNECTED.
 */
void STIManager::InitializeServer() {
  //initialize STUN server
  memset(&m_sSOp, 0, sizeof(struct server_options));
  init_server(&m_sSOp);
}//InitializeServer


void STIManager::ParseStunPacket(uchar *buf, int& len, struct sockaddr_in *from, int *fromlen, int sock, enum sockType stype) {

  struct sockaddr_in response;
  int result;
  pj_stun_msg_type restype;
  struct addr_t saddr;

  //Mutex for media packets. When we are processing media packets, it is possible that
  //we receive another media packet.
  if(m_sOp.ice->mutex) {
    pj_mutex_lock(m_sOp.ice->mutex);
  }

  //copy the socket (required if incoming msg is STUN BIND REQUEST
  //socket will be used to send a BIND response
  m_sOp.sock = sock;
  //repeated 100 Trying ... bug fixed
  m_sOp.datalen=0;

  //printf("stun packet recvd\n");
  if(0==parse_stun_msg(&m_sOp, buf, len, (pj_sockaddr_in*) from, *fromlen, &restype, &result, (pj_sockaddr_in*)&response)){
    TRACE(2, "Transport::parse_stun_msg...error\n", "");
    printf("Transport::parse_stun_msg...error\n");
  } else {
    TRACE(2, "Transport::Transport::parse_stun_msg...success\n", "");
    
    switch(m_sOp.response_type) {
    case PJ_STUN_ALLOCATE_RESPONSE:
      //TODO: check if allocate response was successful or not

      //save relay addr
      strcpy(saddr.ip,  (char *)pj_inet_ntoa( m_sOp.response_addr.sin_addr));
      saddr.port= pj_ntohs( m_sOp.response_addr.sin_port);
      saddr.stype=stype;

      InsertList(m_p2pRelay, ntohs(m_sOp.peer_addr.sin_port), saddr);
      //m_p2pRelay[ntohs(m_sOp.peer_addr.sin_port)]= saddr;
      //save srflex addr
      strcpy(saddr.ip,  (char *)pj_inet_ntoa( m_sOp.binding_addr.sin_addr));
      saddr.port= pj_ntohs( m_sOp.binding_addr.sin_port);
      saddr.stype=stype;

      InsertList(m_p2pSrflex, ntohs(m_sOp.peer_addr.sin_port), saddr);
      //m_p2pSrflex[ntohs(m_sOp.peer_addr.sin_port)]= saddr;

      //printf("STIManager::parse_stun_msg...allocate response for %d relay %s:%d\n", ntohs(m_sOp.peer_addr.sin_port), saddr.ip, saddr.port); 
	    set_ice_local_cand_relay(&m_sOp, (pj_sockaddr_in*)&m_sOp.response_addr);

      //Repubhlish userinfo from client
      //FIXME: check port
      if(1==m_turnserverchanged && eSOCK_SIP==stype) {
        m_pPeer->m_objRM.RePublishUserInfo();
        m_turnserverchanged=0;
      }

      gettimeofday(&m_sReallocate, NULL);
	    m_sReallocate = addtimeval(m_sReallocate, m_RAlloc, 0);
	    if(m_bPendingAllocateRefresh){
	      m_bPendingAllocateRefresh=false;
	    }
      break;

	  case PJ_STUN_BINDING_RESPONSE:
      strcpy(saddr.ip,  (char *)pj_inet_ntoa(m_sOp.binding_addr.sin_addr));
      saddr.port= pj_ntohs(m_sOp.binding_addr.sin_port);
      saddr.stype=stype;
      
      InsertList(m_p2pSrflex, ntohs(m_sOp.peer_addr.sin_port), saddr);
      //m_p2pSrflex[ntohs(m_sOp.peer_addr.sin_port)]= saddr;

      //printf("STIManager::parse_stun_msg...binding response for %d srflex %s:%d\n", ntohs(m_sOp.peer_addr.sin_port), saddr.ip, saddr.port); 
	    set_ice_local_cand_srflex(&m_sOp, (pj_sockaddr_in*)&m_sOp.binding_addr);
      break;

	  case PJ_STUN_DATA_INDICATION:
		  m_bDataRecvd=1;
		  break;

	  case PJ_STUN_BINDING_REQUEST:
		  break;

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

  if(m_sOp.ice->mutex){
    pj_mutex_unlock(m_sOp.ice->mutex);
  }

}//ParseStunPacket

int STIManager::IsStunPacket(uchar *buf, int& len) {
  return stun_msg_check(buf, len);
}//IsStunPacket
 
void STIManager::GetRelayAddr(uint16 iPort, map< string, int>& relaymap) {
  map<uint16, struct addr_t>::iterator iter;

  LockMutexI(m_mutex);
  //=========Lock Mutex============
  iter=m_p2pRelay.find(iPort);
  if(iter!=m_p2pRelay.end()){
    relaymap[string(iter->second.ip)]= iter->second.port;
  }
  //=========UnLock Mutex==========
  UnlockMutexI(m_mutex);

}//GetRelayAddr


void STIManager::SendBindRequest(int sock, char *pchIP, uint16 iPort) {
	send_bind_request(sock, &m_sOp);
}//SendBindRequest


void STIManager::GetRelayAddress(char *relayaddr) {
  struct addr_t saddr;

  LockMutexI(m_mutex);
  //=========Lock Mutex============
  saddr=m_p2pRelay[ntohs(m_sOp.peer_addr.sin_port)];
  strcpy(relayaddr,saddr.ip);
  //=========UnLock Mutex==========
  UnlockMutexI(m_mutex);

}//GetRelayAddress


void STIManager::GetRelayPort(uint16 *relayport) {
  struct addr_t saddr;
  LockMutexI(m_mutex);
  //=========Lock Mutex============
  saddr=m_p2pRelay[ntohs(m_sOp.peer_addr.sin_port)];
  *relayport=saddr.port;
  //=========UnLock Mutex==========
  UnlockMutexI(m_mutex);

}//STIManager::GetRelayPort


void STIManager::GetSrflexAddress(char *srflexaddr) {
  struct addr_t saddr;
  LockMutexI(m_mutex);
  //=========Lock Mutex============
  saddr=m_p2pSrflex[ntohs(m_sOp.peer_addr.sin_port)];
  strcpy(srflexaddr,saddr.ip);
  //=========UnLock Mutex==========
  UnlockMutexI(m_mutex);

}//STIManager::GetSrflexAddress

void STIManager::GetSrflexPort(uint16 *srflexport) {
  struct addr_t saddr;

  LockMutexI(m_mutex);
  //=========Lock Mutex============
  saddr=m_p2pSrflex[ntohs(m_sOp.peer_addr.sin_port)];
  *srflexport=saddr.port;
  //=========UnLock Mutex==========
  UnlockMutexI(m_mutex);

}//STIManager::GetSrflexPort


void STIManager::CreateSendIndication( char *sendind, char *message, int *length) {
	if(m_eNatType==PJ_STUN_NAT_TYPE_OPEN || m_eNatType==PJ_STUN_NAT_TYPE_UNKNOWN || m_bDataRecvd==0){
	    memcpy(sendind, message, *length);
		return;
	}
  
  if(m_sOp.ice->mutex){
		pj_mutex_lock(m_sOp.ice->mutex);
	}
	create_send_ind(&m_sOp, message, length);
	*length = m_sOp.pktlen;
	memcpy(sendind, m_sOp.pkt, m_sOp.pktlen);
  if(m_sOp.ice->mutex) {
    pj_mutex_unlock(m_sOp.ice->mutex);
  }

}//STIManager::CreateSendIndication

void STIManager::GetDataFromIndication(char *buf, int *len) {
	if(m_sOp.ice->mutex){
		pj_mutex_lock(m_sOp.ice->mutex);
	}
	*len = m_sOp.datalen; //strlen(m_sOp.data);
	//buf = m_sOp.data;
	memcpy(buf, m_sOp.data, *len);

	if(m_sOp.ice->mutex){
		pj_mutex_unlock(m_sOp.ice->mutex);
	}

}//STIManager::GetDataFromIndication

void STIManager::SetRemoteCandHost(struct sockaddr_in hostaddr){
	m_sOp.rcand_cnt++;
  create_ice_cand(&m_sOp.rcand[m_sOp.rcand_cnt-1], 
		  1,
		  PJ_ICE_CAND_TYPE_HOST,
		  m_sOp.ice,
		  (struct pj_sockaddr_in*)&hostaddr,
		  (struct pj_sockaddr_in*)&hostaddr,
		  sizeof(hostaddr));
}//STIManager::SetRemoteCandHost

void STIManager::SetRemoteCandSrflex(struct sockaddr_in srflexaddr){
	m_sOp.rcand_cnt++;
  create_ice_cand(&m_sOp.rcand[m_sOp.rcand_cnt-1], 
		  1,
		  PJ_ICE_CAND_TYPE_SRFLX,
		  m_sOp.ice,
		  (struct pj_sockaddr_in*)&srflexaddr,
		  (struct pj_sockaddr_in*)&srflexaddr,
		  sizeof(srflexaddr));
}//STIManager::SetRemoteCandSrflex

void STIManager::SetRemoteCandRelayed(struct sockaddr_in relayaddr){
	m_sOp.rcand_cnt++;
  create_ice_cand(&m_sOp.rcand[m_sOp.rcand_cnt-1], 
		  1,
		  PJ_ICE_CAND_TYPE_RELAYED,
		  m_sOp.ice,
		  (struct pj_sockaddr_in*)&relayaddr,
		  (struct pj_sockaddr_in*)&relayaddr,
		  sizeof(relayaddr));
}//STIManager::SetRemoteCandRelayed

void STIManager::SetLocalCandHost(struct sockaddr_in hostaddr){
	set_ice_local_cand_host(&m_sOp, (struct pj_sockaddr_in*)&hostaddr);
}//STIManager::SetLocalCandHost

void STIManager::SetLocalCandSrflex(struct sockaddr_in srflexaddr){
	set_ice_local_cand_srflex(&m_sOp, (struct pj_sockaddr_in*)&srflexaddr);
}//STIManager::SetLocalCandSrflex

void STIManager::SetLocalCandRelayed(struct sockaddr_in relayaddr){
	set_ice_local_cand_relay(&m_sOp, (struct pj_sockaddr_in*)&relayaddr);
}//STIManager::SetLocalCandRelayed

void STIManager::RunICEConnectivityTest() {	
  m_bRunningICEChecks=1;
  start_check(&m_sOp, m_sOp.rcand);
  //sleep(10);
}//STIManager::RunICEConnectivityTest

void STIManager::GetSelfAddr(char *ipaddr, uint16 *port) {
	sprintf(ipaddr, "%s", m_sSelfIP.c_str());
	*port=ntohs(m_sOp.peer_addr.sin_port);
}//STIManager::GetSelfAddr

void STIManager::GetICEResult(char *ip, uint16 *port, char *type) {
	get_valid_rcand(&m_sOp, ip, port, type);
}//STIManager::GetICEResult


void STIManager::UpdateRelayList(list<peerinfo>& lstP) {
  //create list of Relay servers
  list< struct peerinfo >::iterator iter;
  list< struct Internalrelaypeerinfo >::iterator itr;
  Internalrelaypeerinfo ipeerinfo;
  struct addr_t sAddr;
  int flag=0;

  //Insert only 10 relays in the list
  //TODO: check relay location before inserting them.

  for(iter=lstP.begin(); iter!=lstP.end() && m_lRS.size()<=STIManager::NUMRELAYS; iter++) {
    flag=0;
    sAddr.port = iter->staddr.port[0];
    strncpy(sAddr.ip, iter->staddr.addr[0], IPADDRLEN);
    
    //Check if the relay already exist in our list
    for(itr=m_lRS.begin(); itr!=m_lRS.end(); itr++) {      
      if(strncmp(itr->m_stP.staddr.addr[0] , sAddr.ip, IPADDRLEN)==0 && itr->m_stP.staddr.port[0]==sAddr.port) {
        flag=1;
	      //printf("found  %s:%d = %s:%d\n",sAddr.ip,sAddr.port, itr->ip, itr->port);
	      break;
      }
    }//end for
    
    //If the relay already exists, continue
    if(flag==1) {
      continue;
    }

    setKATimer(ipeerinfo.m_expKA);
    ipeerinfo.m_stP = *iter;

    if(m_lRS.size()<=STIManager::NUMRELAYS) {
      ipeerinfo.Initialize();
      
      //initialize the time send and KA timer
      gettimeofday( &(ipeerinfo.m_tLastSent), NULL);
      setKATimer(ipeerinfo.m_expKA);

      m_lRS.push_back(ipeerinfo);

      //get the stun port
      m_pPeer->m_objRM.LookupStunPort(ipeerinfo.m_stP, eUDP);
    }//end if

  }//end for

}///STIManager::UpdateRelayList

void STIManager::UpdateRelayList(peerinfo& stP, uint16 stunport) {
  //create list of Relay servers
  list< struct Internalrelaypeerinfo >::iterator itr;
  Internalrelaypeerinfo ipeerinfo;
  //printf("RelaySize %d\n", m_lRS.size());

  //Insert only 10 relays in the list
  //TODO: check relay location before inserting them.

  if(m_lRS.size()>=STIManager::NUMRELAYS) {
    return;
  }

  //Check if the relay already exist in our list
  for(itr=m_lRS.begin(); itr!=m_lRS.end(); itr++) {      
    if(strncmp(itr->m_stP.staddr.addr[0] , stP.staddr.addr[0], IPADDRLEN)==0 
      && itr->m_stP.staddr.port[0]==stP.staddr.port[0]) {
      //printf("found  %s:%d = %s:%d\n",sAddr.ip,sAddr.port, itr->ip, itr->port);
      return;
    }
  }//end for
  
  setKATimer(ipeerinfo.m_expKA);
  ipeerinfo.m_stP=stP;
  ipeerinfo.m_iStunPort=stunport;

  //initialize the time send and KA timer
  gettimeofday( &(ipeerinfo.m_tLastSent), NULL);
  setKATimer(ipeerinfo.m_expKA);

  m_lRS.push_back(ipeerinfo);

  //get the stun port
  m_pPeer->m_objRM.LookupStunPort(ipeerinfo.m_stP, eUDP);

  //printf("RelaySize %d\n", m_lRS.size());
}///STIManager::UpdateRelayList


/*
 * updates RTT, bw, cpu utilization.
 * does not insert in the list
 */
int32 STIManager::UpdateRelayList(struct message& msg) {
  
  //create list of relay servers
  list< struct Internalrelaypeerinfo >::iterator itr;
  Internalrelaypeerinfo ipeerinfo;
  struct timeval cur_time;
  struct peerinfo stP;

  //peerinfo object
  stP=msg.stpeerinfo;

  //Check if the relay already exist in our list
  for(itr=m_lRS.begin(); itr!=m_lRS.end(); itr++) {      
    
    if(strncmp(itr->m_stP.staddr.addr[0] , stP.staddr.addr[0], IPADDRLEN)==0 
      && itr->m_stP.staddr.port[0]==stP.staddr.port[0]) {
      //printf("found  %s:%d = %s:%d\n",sAddr.ip,sAddr.port, itr->ip, itr->port);

      //this check is redundant because we call this function only from
      //keepalive or diagnostic?

      gettimeofday(&cur_time, NULL);
      itr->m_tRTT=difftimeval(cur_time, itr->m_tLastSent);
      itr->isRTT=1;

      //update program bw if present
      if(1==msg.stdiagnostics.stbw.isPresent) {
        itr->m_stbw=msg.stdiagnostics.stbw;
      }

      //update program cpu if present
      if(1==msg.stdiagnostics.stcpuutil.isPresent) {
        itr->m_stcpu=msg.stdiagnostics.stcpuutil;
      }

      //update machine cpu if present
      if(1==msg.stdiagnostics.sttcpuutil.isPresent) {
        itr->m_sttcpu=msg.stdiagnostics.sttcpuutil;
      }
      
      return 1;
    }
  }//end for  

  return 0;
  
}///STIManager::UpdateRelayList



/**
 * 
 * 0 peer does not exist
 * 1 peer exists and removed.
 */
int32 STIManager::RemovePeer(const char* pchIP, const uint16 port) {
  list<Internalrelaypeerinfo>::iterator itr, fitr;
  int32 flag=0;
  
  for(itr=m_lRS.begin(); itr!=m_lRS.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_lRS.erase(fitr);
    return 1;
  }

  return 0;
}//RemovePeer

/**
 *
 **/
int32 STIManager::RemovePeerExceptTop(const char* pchIP, const uint16 port) {
  list<Internalrelaypeerinfo>::iterator itr, fitr;
  int32 flag=0;
  
  //THIS function  _CANNOT_ remove topmost entry in relaylist (m_lRS)
  if(m_lRS.size()<=1) {
    return 0;
  }

  itr=m_lRS.begin();
  itr++;
  for(; itr!=m_lRS.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_lRS.erase(fitr);
    return 1;
  }

  return 0;
}//RemovePeer


int32 STIManager::UpdateStunPort(peerinfo& stP, uint16 stunport) {
  list<Internalrelaypeerinfo>::iterator itr;
  struct timeval cur_time;
  uint32 rtt=0;
  
  //update stun port and RTT
  for(itr=m_lRS.begin(); itr!=m_lRS.end(); itr++) {
    if( 0 == strcmp(itr->m_stP.staddr.addr[0], stP.staddr.addr[0]) &&
      itr->m_stP.staddr.port[0] == stP.staddr.port[0]) {
        itr->m_iStunPort=stunport;

        //update rtt
        gettimeofday( &cur_time, NULL);
        itr->m_tRTT=difftimeval(cur_time, itr->m_tLastSent);
        itr->isRTT=1;
        rtt=itr->m_tRTT.tv_sec*1000+itr->m_tRTT.tv_usec/1000;

        return 1;
    }
  }//end for

  return 0;
}

void STIManager::ShowRelayServerList() {
  
  list<Internalrelaypeerinfo>::iterator litr;
  struct timeval diffKA_time, curr_time;
  BigInt lpeerid; //b, result, max, bindex, 
  int32 len, i=1;
  uint32 rtt;

  len = m_pPeer->m_HASH_SIZE;

  printf("\n");
  printf("-------Stun/Turn/ICE Manager-------\n");
  printf("Total Relay server entries: %d \n", m_lRS.size() );

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

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

    //calculate the time keep alive will be sent next to this node
    diffKA_time = difftimeval(litr->m_expKA, curr_time);

    for(int j=0; j<litr->m_stP.staddr.num; j++) {
      //show rtt
      rtt=litr->m_tRTT.tv_sec*1000+litr->m_tRTT.tv_usec/1000;

      cout<<"("<<litr->m_stP.staddr.addr[0]<<" "<<litr->m_stP.staddr.port[0]<<" stun port: "<<litr->m_iStunPort<<") ";
      printf("KA: %us RTT: %ums RBW: %d SBW: %d (bytes/s) cpu: %d mcpu: %d %%", (uint32)diffKA_time.tv_sec, rtt, 
        litr->m_stbw.rbw, litr->m_stbw.sbw, litr->m_stcpu.cpuusage, litr->m_sttcpu.cpuusage);

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

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

void STIManager::ShowRelayAllocations() {
  map<uint16, struct addr_t>::iterator itr1;

  LockMutexI(m_mutex);
  //=========Lock Mutex============
  printf("\nRelay address:\n");
  for(itr1=m_p2pRelay.begin(); itr1!=m_p2pRelay.end(); itr1++) {
    printf("For port %d relay allocation is %s:%d\n", itr1->first, itr1->second.ip, itr1->second.port);
  }
  printf("Server reflexive address:\n");
  for(itr1=m_p2pSrflex.begin(); itr1!=m_p2pSrflex.end() ; itr1++) {
    printf("For port %d srflx allocation is %s:%d\n", itr1->first, itr1->second.ip, itr1->second.port);
  }
  //=========UnLock Mutex==========
  UnlockMutexI(m_mutex);
  
}//ShowRelayAllocations



uint16 STIManager::GetServerPort() {
  return get_stun_srv_port();
}//GetServerPort


int32 STIManager::InsertList(map<uint16, struct addr_t>& m_mapPort, uint16 port, struct addr_t& addr) {
  LockMutexI(m_mutex);
  m_mapPort[port]=addr;
  UnlockMutexI(m_mutex);

  return 1;
}//InsertList


int32 STIManager::EraseList(map<uint16, struct addr_t>& m_mapPort, uint16 port) {
  map<uint16, struct addr_t>::iterator itr1;
  
  LockMutexI(m_mutex);
  //=========Lock Mutex============
  itr1=m_mapPort.find(port);
  if(itr1!=m_mapPort.end()){
    m_mapPort.erase(itr1);
  }
  //=========UnLock Mutex==========
  UnlockMutexI(m_mutex);

  return 1;
}//EraseList


int32 STIManager::ClearList(map<uint16, struct addr_t>& m_mapPort) {
  LockMutexI(m_mutex);
  //=========Lock Mutex============
  m_mapPort.clear();
  //=========UnLock Mutex==========
  UnlockMutexI(m_mutex);
  return 1;
}//ClearList

int32 STIManager::GetRelayList(list<struct peerinfo>& stpl) {
  list<Internalrelaypeerinfo>::iterator litr;

  //iterate over CP list
  for(litr=m_lRS.begin(); litr!=m_lRS.end(); litr++) {
    stpl.push_back( (*litr).m_stP );
  }//for

  return 1;
}//getRelayList

//FIXME
//MAX_CLIENTS is mentioned in src/stun/turn_usage.c
#define MAX_CLIENTS 8000
int32 STIManager::GetTurnAllocations(list<struct turnalloc>& stlta) {
  pjnath_turn_allocation ta[MAX_CLIENTS];
  int size=0;
  struct turnalloc stta;

  get_turn_allocations(ta, &size);

  for(int i=0; i < size; ++i) {
    stta.isPresent=1;
    stta.staddr.isPresent=1;
    stta.stbw.isPresent=1;
    strncpy(stta.staddr.addr[0], ta[i].ip, IPADDRLEN);
    stta.staddr.port[0]=ta[i].port;
    stta.staddr.num=1;

    //printf("ip: %s port: %d\n",stta.staddr.addr[0],  stta.staddr.port[0]);
    stta.relayport=ta[i].relayport;
    stta.stbw.sbw=ta[i].sbw;
    stta.stbw.rbw=ta[i].rbw;
    stlta.push_back(stta);
  }//for

  return 1;
}//GetTurnAllocations


int32 STIManager::FindNewRelay(list<Internalrelaypeerinfo>::iterator& itr, int32 isRemoved) {
  list<Internalrelaypeerinfo>::iterator litr, ritr, citr;
  uint32 rRTT, cRTT=0, bRTT;

  //0 so we cannot find a relay
  if(0==m_lRS.size()) {
    return 0;
  }

  //if relay list is 1, then return it
  if(1==m_lRS.size() && 1==isRemoved) {
    itr=m_lRS.begin();
    return 1;
  } else if(1==m_lRS.size() && 0==isRemoved) {
    return 0;
  }

  //WE HAVE AT LEAST TWO CANDIDATES IN RELAY LIST NOW

  //get the first candidate in relay list. this is our relay
  ritr=litr=m_lRS.begin();

  //if do not have the RTT for this relay, set it to a high value
  //set reference RTT
  if(0==litr->isRTT) {
    bRTT=rRTT=5000;   //relay is 5 seconds
  } else {
    //Get the RTT for our current relay
    bRTT=rRTT=litr->m_tRTT.tv_sec*1000+litr->m_tRTT.tv_usec/1000;
  }
  

  //now move to the second candidate in our relay list
  litr++;

  //Initialize citr to end of list
  citr=m_lRS.end();

  for(; litr!=m_lRS.end(); litr++) {
    //if we have calculated the RTT
    if(1==litr->isRTT) {
      cRTT=litr->m_tRTT.tv_sec*1000+litr->m_tRTT.tv_usec/1000;

      if(cRTT < rRTT) {
        citr=litr;
        rRTT=cRTT;
      }

    }//end if

  }//end for
  


  //we could not find any relay with a better RTT
  if(citr==m_lRS.end()) {
    if(1==isRemoved && m_lRS.size() > 0) {
      itr=m_lRS.begin();
      return 1;
    } else {
      return 0;
    }
  }//end if



  //check the RTT difference between current relay and this candidate
  //switch only if diff is more than 30ms or we had removed our current 
  //candidate
  if( (bRTT - cRTT) > 30 || 1==isRemoved) {
    itr=citr;
    return 1;
  }

  return 0;

}//FindNewRelay

int32 STIManager::IsICEComplete() {
  int32 ret=is_ice_complete(&m_sOp);
  m_bRunningICEChecks=ret;
  return ret;
}//IsICEComplete

