/*
 * 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 _STIMANAGER_H
#define _STIMANAGER_H

#include "types.h"
#include "transport.h"
#include "threadi.h"


extern "C" {
#include "pjnath_client.h"
#include "pjnath_server.h"
}

using namespace std;


enum sockType {eSOCK_NONE=0, eSOCK_P2PP, eSOCK_SIP, eSOCK_AUDIO, eSOCK_VIDEO, eSOCK_OTHER};

struct addr_t{
  char8 ip[IPADDRLEN];
  uint16 port;
  sockType stype; //socket type to indicate whether the socket from which the packet is
                  //received is P2PP socket, SIP socket, or media or any other socket.
};


struct Internalrelaypeerinfo {
  struct peerinfo m_stP;
  struct timeval m_lastseen;
  //other members can be added later for things like security and optimization.
  
  struct timeval m_expKA;
  struct timeval m_expRT;
  
  struct timeval m_tLastSent;
  struct timeval m_tRTT;
  int32 isRTT;

  uint16 m_iStunPort;

  struct bw m_stbw;
  struct cpuutil m_stcpu;

  struct bw m_sttbw;
  struct cpuutil m_sttcpu;
  
  Internalrelaypeerinfo() {
    //Initialize RTT value
    m_tRTT.tv_sec = 0;
    m_tRTT.tv_usec = 0;
    isRTT = 0;
    m_iStunPort = 0;
  }

  void Initialize() {
    m_tLastSent.tv_sec = 0;
    m_tLastSent.tv_usec = 0;
    m_tRTT.tv_sec = 0;
    m_tRTT.tv_usec = 0;
    isRTT = 0;
    m_iStunPort = 0;
  }//Initialize
};

class Peer;


/**
 * STI Manager class - manages STUN TURN ICE
 **/
class STIManager {
 public:

  /**
   * self ip
   **/
  static string m_sSelfIP;
  
  /**
   *NAT type
   **/
  static enum pj_stun_nat_type m_eNatType;

  /**
   * address of my active stun server
   **/
  static struct addr_t m_StunServer;

  /**
   * <port, relay addr_t> address
   * <port, srflex addr_t> map
   * port is for identifying the local protocol
   * e.g., 7080 is p2pp port, 5060 is SIP port.
   **/
  static map<uint16, struct addr_t> m_p2pRelay;
  static map<uint16, struct addr_t> m_p2pSrflex;
  static map<uint16, ClientOptions*> m_p2pCO;  //pair<port, sock>
  static struct mutex_i m_mutex;  //mutex for accessing m_p2pRelay and m_p2pSrflex variables

  /**
   * list of available relay servers
   **/
  static list<struct Internalrelaypeerinfo> m_lRS;

   /**
   * Server options (one server per peer)
   **/
  static ServerOptions m_sSOp;

    /**
   * Peer class object.
   **/
  static Peer* m_pPeer;

  static int32 m_inCall;  //0 not in call, 1 in call

private:

  /**
   * Client options
   **/
  ClientOptions m_sOp;


  int m_bDataRecvd; 

  int m_bRunningICEChecks;

  struct timeval m_sReallocate;

  bool m_bPendingAllocateRefresh;


  //Timer variables for checking the liveness of relay servers.
  int32 m_RT;     //check relay list every KA seconds
  struct timeval m_tRT;

  int32 m_KA;     //relay keep-alive timer
  int32 m_KArand; //relay keep-alive timer random interval e.g., m_KA=60 m_KArand=30 [60s,90s]
  int32 m_RAlloc;

  int32 m_turnserverchanged;

  static uint32 NUMRELAYS;

private:
  void OnTimerKAET();
  
  //functions in which we have mutex
  static int32 InsertList(map<uint16, struct addr_t>& m_mapPort, uint16 port, struct addr_t& addr);
  static int32 EraseList(map<uint16, struct addr_t>& m_mapPort, uint16 port);
  static int32 ClearList(map<uint16, struct addr_t>& m_mapPort);

 public:
  STIManager();
  ~STIManager();


  /**
   * Set the peer pointer in STIManager class so that we can call Compose functions.
   **/
  void setParams(Peer* pPeer);


  /**
   * Initializes PJNATH, PJLIB, PJUTIL, and mutex
   **/
  static int32 Initialize(char8 *pchIP, uint16& iPort, peerinfo& stunpeer, list<peerinfo>& lstP, const char *ownIP);

  /**
   * DeInitializes PJNATH, PJLIB, PJUTIL, and mutex
   **/
  static int32 DeInitialize();

  
  /**
   * Initialize client is called for every port to run ICE on it.
   **/
  void InitializeClient(int sock, const char *pchIP, uint16 uiPort);

  /**
   * Init TURN server. We run TURN server, if we are a peer.
   **/
  void InitializeServer();

  /**
   * Init ICE. Listen for STUN Binding request during ICE procedures. 
  **/
  void ListenForICE();

  /**
   * OnTimer
   **/
  void OnTimer();

  /**
   * ParseStunPacket
   **/
  void ParseStunPacket(uchar *buf, int& len, struct sockaddr_in *staddr, int *addrlen, int sock, enum sockType stype);

  /**
   * IsStunPacket
   **/
  static int IsStunPacket(uchar *buf, int& len);

  /**
   * get client options
   **/
  ClientOptions& getClientOptions() {
    return m_sOp;
  }
  
  /**
   * get NAT type
   **/
  static pj_stun_nat_type getNatType() {
    return m_eNatType;
  }

  /**
   * Get relay assigned to a port
   **/
  void GetRelayAddr(uint16 uiPort, map< string, int>& relaymap);

  /**
   * Binding request on a socket
   **/
  void SendBindRequest(int sock, char *ip, uint16 uiPort);

  /**
  * Get relay address
  **/
  void GetRelayAddress(char *relayaddr);

  /**
  * Get relay port
  **/
  void GetRelayPort(uint16 *relayport);

  /**
  * Get Srflex addr
  **/
  void GetSrflexAddress(char *srflexaddr);

  /**
  * Get Srflex port
  **/
  void GetSrflexPort(uint16 *srflexport);

  /**
  * Create Send indication
  **/
  void CreateSendIndication( char *sendind, char *message, int *length);

  /**
  * Get Data From Indication
  **/
  void GetDataFromIndication(char *buf, int *len);

  /**
  * Set Remote ICE Candidate - Host
  **/
  void SetRemoteCandHost(struct sockaddr_in hostaddr);

  /**
  * Set Remote ICE Candidate - Srflex
  **/
  void SetRemoteCandSrflex(struct sockaddr_in srflexaddr);

  /**
  * Set Remote ICE Candidate - Relayed
  **/
  void SetRemoteCandRelayed(struct sockaddr_in relayedaddr);

  /**
  * Set Local ICE Candidate - Host
  **/
  void SetLocalCandHost(struct sockaddr_in hostaddr);

  /**
  * Set Local ICE Candidate - Srflex
  **/
  void SetLocalCandSrflex(struct sockaddr_in srflexaddr);

  /**
  * Set Local ICE Candidate - Relayed
  **/
  void SetLocalCandRelayed(struct sockaddr_in relayedaddr);

  /**
  *run ice connectivity tests
  **/
  void RunICEConnectivityTest();

  /**
  *Get self address
  **/
  void GetSelfAddr(char *ipaddr, uint16 *port);

	/**
	* Find if running ICE tests.
	**/
  int RunningICEChecks() {
	return m_bRunningICEChecks;
  }

  /**
  *GetICEResult
  **/
  void GetICEResult(char *ip, uint16 *port, char *type);

  void UpdateRelayList(list<peerinfo>& lstP);  
  void UpdateRelayList(peerinfo& stP, uint16 stunport);
  int32 UpdateRelayList(struct message& msg);

  int32 UpdateStunPort(peerinfo& stP, uint16 stunport);

  int32 RemovePeer(const char* pchIP, const uint16 port);
  int32 RemovePeerExceptTop(const char* pchIP, const uint16 port);

  void setKATimer(struct timeval& stT); //refresh timer for relay entry

  int32 FindNewRelay(list<Internalrelaypeerinfo>::iterator& itr, int32 isRemoved);
  
  void ShowRelayServerList();
  void ShowRelayAllocations();

  uint16 GetServerPort();
  int32 GetRelayList(list<struct peerinfo>& stpl);
  int32 GetTurnAllocations(list<struct turnalloc>& stlta);

  int32 IsICEComplete();
};


#endif

