/*
 * WengoPhone, a voice over Internet phone
 * Copyright (C) 2004-2007  Wengo
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

#include "PhoneLine.h"

#include "PhoneLineStateUnknown.h"
#include "PhoneLineStateProgress.h"
#include "PhoneLineStateOk.h"
#include "PhoneLineStateClosed.h"
#include "PhoneLineStateTimeout.h"
#include "PhoneLineStateServerError.h"
#include "PhoneLineStateAuthenticationError.h"

#include <model/SipCallbacks.h>
#include <model/config/ConfigManager.h>
#include <model/config/Config.h>
#include <model/history/History.h>
#include <model/phonecall/PhoneCall.h>
#include <model/profile/UserProfile.h>

#include <sipwrapper/SipWrapper.h>
#include <sipwrapper/SipWrapperFactory.h>
#include <sipwrapper/EnumNatType.h>

#include <sound/AudioDevice.h>
#include <util/Logger.h>
#include <util/SafeDelete.h>

#include <phapiwrapper.h>

#include <cstring>



/////////*************P2PP code start**************////////////
#include "rmanager.h"
#include "peer.h"
#include "p2pconfig.h"
#include "bigint.h"

//#include "chorddist.h"
#include "kaddist.h"

#include "p2pconfig.h"

#include "msgheader.h"
#include "socketi.h"

#include "trace.h"

#include "bootstrap.h"
#include "peer.h"
//#include "chordpeer.h"
#include "kadpeer.h"
#include "bamboopeer.h"
#include "client.h"
#include "diagnostics.h"
#include "stimanager.h"

#include "kaddist.h"
#include "bamboodist.h"
#include "sysutils.h"
#include "threadi.h"

#include <string> 
using namespace std;

//prototype
int create_audio_manager();

//UGLY HACK FIXME
static bool _CallClosed=false;

FILE* _fptrace;
extern string configfile; //defined in main.cpp
int g_tracelevel;
int idlen=20;

//P2PP indicates if peerfound.
bool peerfound;

int InitLogFile (char *filename) {
  _fptrace = fopen(filename, "w+");
  return 1;
}//InitLogFile

Node* gpNode;
STIManager* gpSTSIP; //this is for SIP
STIManager* gpSTAudio;  //this is for audio
STIManager* gpSTVideo;  //this is for video
char gchRemoteSDPType[10];
char gchRemoteSDPAddr[200];

uint32 t;
int tid;
int ctid;
P2PConfig cfg;
Transport transport;

//STI Manager functions for C library
extern "C" void sti_create_send_indication( char *sendind, char *message, int *length);
extern "C" void sti_read_data_from_ind(char *buf, int *len, struct sockaddr_in *sa, int *slen, int sock);

void sti_create_send_indication( char *sendind, char *message, int *length) {
  pj_thread_desc handler_desc;
  pj_thread_t *yetanother_thread;

  //register listener thread
  pj_thread_register("YETANOTHER", handler_desc, &yetanother_thread);

  gpSTSIP->CreateSendIndication(sendind, message, length);
}

void sti_read_data_from_ind(char *buf, int *len, struct sockaddr_in *sa, int *slen, int sock) {
  pj_thread_desc handler_desc;
  pj_thread_t *yetanother_thread;

  //register listener thread
  pj_thread_register("YETANOTHER", handler_desc, &yetanother_thread);

    if(STIManager::IsStunPacket((uchar*)buf, *len)) {
		gpSTSIP->ParseStunPacket((uchar*)buf, *len, sa, slen, sock, eSOCK_SIP);
		gpSTSIP->GetDataFromIndication(buf, len);

		
	}
}

void sti_create_send_indication_audio( char *sendind, char *message, int *length) {
  pj_thread_desc handler_desc;
  pj_thread_t *yetanother_thread;

  //register listener thread
  pj_thread_register("YETANOTHERAUDIO", handler_desc, &yetanother_thread);

  if(gpSTAudio){
	gpSTAudio->CreateSendIndication(sendind, message, length);
  }else{
	memcpy(sendind, message, *length);
  }
}

void sti_read_data_from_stun_audio(char *buf, int *len, struct sockaddr_in *sa, int *slen, int sock) {
  pj_thread_desc handler_desc;
  pj_thread_t *yetanother_thread;

  //register listener thread
  pj_thread_register("YETANOTHERAUDIO", handler_desc, &yetanother_thread);

    if(STIManager::IsStunPacket((uchar*)buf, *len) && gpSTAudio) {
		gpSTAudio->ParseStunPacket((uchar*)buf, *len, sa, slen, sock, eSOCK_AUDIO);
		gpSTAudio->GetDataFromIndication(buf, len);		
	}
}

void sti_create_send_indication_video( char *sendind, char *message, int *length) {
  pj_thread_desc handler_desc;
  pj_thread_t *yetanother_thread;

  //register listener thread
  pj_thread_register("YETANOTHERVIDEO", handler_desc, &yetanother_thread);

  gpSTVideo->CreateSendIndication(sendind, message, length);
}

void sti_read_data_from_stun_video(char *buf, int *len, struct sockaddr_in *sa, int *slen, int sock) {
  pj_thread_desc handler_desc;
  pj_thread_t *yetanother_thread;

  //register listener thread
  pj_thread_register("YETANOTHERVIDEO", handler_desc, &yetanother_thread);

    if(STIManager::IsStunPacket((uchar*)buf, *len)) {
		gpSTVideo->ParseStunPacket((uchar*)buf, *len, sa, slen, sock, eSOCK_VIDEO);
		gpSTVideo->GetDataFromIndication(buf, len);
	}
}

void sti_audio_set_remote_sdp_candidates(char *host,
										 char *srflex, 
										 char *relayed, 
   									     char *remote_sdp_ip, 
										 int remote_sdp_ip_len, 
										 int *remote_sdp_port) {
	
	struct sockaddr_in hostaddr, srflexaddr, relayedaddr;
	char *ptr1, *ptr2;
  int ice_sleep_cnt=0;
  int ret;

  pj_thread_desc handler_desc;
  pj_thread_t *yetanother_thread;

  //register listener thread
  pj_thread_register("YETANOTHERAUDIO", handler_desc, &yetanother_thread);


  if(!gpSTAudio) {
	  ret=create_audio_manager();
  }
	
	if(gpSTAudio->RunningICEChecks()) {
		return;
	}

	if(host){
		ptr1=strtok(host, " ");
		ptr1=strtok(NULL, " ");
		ptr1=strtok(NULL, " ");
		ptr2=strtok(NULL, " ");
		getSockAddr_in(ptr1, atoi(ptr2), &hostaddr); 
		//set candidate	
		gpSTAudio->SetRemoteCandHost(hostaddr);
	}
	if(srflex){
		ptr1=strtok(srflex, " ");
		ptr1=strtok(NULL, " ");
		ptr1=strtok(NULL, " ");
		ptr2=strtok(NULL, " ");
		getSockAddr_in(ptr1, atoi(ptr2), &srflexaddr); 
		//set candidate
		gpSTAudio->SetRemoteCandSrflex(srflexaddr);
	}
	if(relayed){
		ptr1=strtok(relayed, " ");
		ptr1=strtok(NULL, " ");
		ptr1=strtok(NULL, " ");
		ptr2=strtok(NULL, " ");
		getSockAddr_in(ptr1, atoi(ptr2), &relayedaddr); 
		//set candidate	
		gpSTAudio->SetRemoteCandRelayed(relayedaddr);
	}
	
	//run ice connectivity tests
	gpSTAudio->RunICEConnectivityTest();
	memset(remote_sdp_ip, 0, remote_sdp_ip_len);
  
  //UGLY HACK FIXME (gaurav)
  while(!gpSTAudio->IsICEComplete() ) {
    ice_sleep_cnt++;
    if(ice_sleep_cnt==5){ 
      break;
    }
    sleep(1);
  }

	gpSTAudio->GetICEResult(remote_sdp_ip, (unsigned short*)remote_sdp_port, gchRemoteSDPType);

  //Copy current node which is providing TURN service.
  //This is to show in GUI 
  string strRemoteSDP, strMyTURN;
  
  if(gpNode->getNodeType() == eCLIENT || gpNode->getNodeType() == eAUTO) {
      //ret=Transport::gethostbyaddr(remote_sdp_ip, strRemoteSDP );
      //if(0==ret) {
        strRemoteSDP=remote_sdp_ip;
      //}
      //ret=Transport::gethostbyaddr(STIManager::m_lRS.begin()->m_stP.staddr.addr[0], strMyTURN );
      //if(0==ret) {
        strMyTURN=STIManager::m_lRS.begin()->m_stP.staddr.addr[0];
      //}
      sprintf( gchRemoteSDPAddr, "Media -> %s:%d; (%s) \nMy TURN server %s:%d\n", 
        (char*)strRemoteSDP.c_str(), *remote_sdp_port, 
        gchRemoteSDPType,
        (char*)strMyTURN.c_str(), 
        STIManager::m_lRS.begin()->m_stP.staddr.port[0]);
  } else {
      //ret=Transport::gethostbyaddr(remote_sdp_ip, strRemoteSDP );
      //if(0==ret) {
        strRemoteSDP=remote_sdp_ip;
      //}
      sprintf( gchRemoteSDPAddr, "Media -> %s:%d; (%s) \n", 
        (char*)strRemoteSDP.c_str(), *remote_sdp_port,
        gchRemoteSDPType);
  }

}//sti_audio_set_remote_sdp_candidates

void sti_audio_set_remote_sdp_candidates_callee(char *host,
										 char *srflex, 
										 char *relayed, 
   									 char *remote_sdp_ip, 
										 int remote_sdp_ip_len, 
										 int *remote_sdp_port) {
	
	struct sockaddr_in hostaddr, srflexaddr, relayedaddr;
	char *ptr1, *ptr2;
  int ret;

  pj_thread_desc handler_desc;
  pj_thread_t *yetanother_thread;

  //register listener thread
  pj_thread_register("YETANOTHERAUDIO", handler_desc, &yetanother_thread);


  if(!gpSTAudio) {
	  ret=create_audio_manager();
  }
	
	if(gpSTAudio->RunningICEChecks()) {
		return;
	}

	if(host){
		ptr1=strtok(host, " ");
		ptr1=strtok(NULL, " ");
		ptr1=strtok(NULL, " ");
		ptr2=strtok(NULL, " ");
		getSockAddr_in(ptr1, atoi(ptr2), &hostaddr); 
		//set candidate	
		gpSTAudio->SetRemoteCandHost(hostaddr);
	}
	if(srflex){
		ptr1=strtok(srflex, " ");
		ptr1=strtok(NULL, " ");
		ptr1=strtok(NULL, " ");
		ptr2=strtok(NULL, " ");
		getSockAddr_in(ptr1, atoi(ptr2), &srflexaddr); 
		//set candidate
		gpSTAudio->SetRemoteCandSrflex(srflexaddr);
	}
	if(relayed){
		ptr1=strtok(relayed, " ");
		ptr1=strtok(NULL, " ");
		ptr1=strtok(NULL, " ");
		ptr2=strtok(NULL, " ");
		getSockAddr_in(ptr1, atoi(ptr2), &relayedaddr); 
		//set candidate	
		gpSTAudio->SetRemoteCandRelayed(relayedaddr);
	}

}//

void sti_run_ice_checks_callee( char *remote_sdp_ip, 
										 int remote_sdp_ip_len, 
                     int *remote_sdp_port) 
{
  int ice_sleep_cnt=0;                       
  pj_thread_desc handler_desc;
  pj_thread_t *yetanother_thread;

  //register listener thread
  pj_thread_register("YETANOTHERAUDIO", handler_desc, &yetanother_thread);

  //run ice connectivity tests
	gpSTAudio->RunICEConnectivityTest();
	memset(remote_sdp_ip, 0, remote_sdp_ip_len);
  
  //UGLY HACK FIXME (gaurav)
  while(!gpSTAudio->IsICEComplete() ) {
    ice_sleep_cnt++;
    if(ice_sleep_cnt==5){ 
      break;
    }
    sleep(1);
  }

	gpSTAudio->GetICEResult(remote_sdp_ip, (unsigned short*)remote_sdp_port, gchRemoteSDPType);

  //Copy current node which is providing TURN service.
  //This is to show in GUI 
  int ret;
  string strRemoteSDP, strMyTURN;
  if(gpNode->getNodeType() == eCLIENT || gpNode->getNodeType() == eAUTO) {
      //ret=Transport::gethostbyaddr(remote_sdp_ip, strRemoteSDP );
      //if(0==ret) {
        strRemoteSDP=remote_sdp_ip;
      //}
      //ret=Transport::gethostbyaddr(STIManager::m_lRS.begin()->m_stP.staddr.addr[0], strMyTURN );
      //if(0==ret) {
        strMyTURN=STIManager::m_lRS.begin()->m_stP.staddr.addr[0];
      //}
      sprintf( gchRemoteSDPAddr, "Media -> %s:%d; (%s) \nMy TURN server %s:%d\n", 
        (char*)strRemoteSDP.c_str(), *remote_sdp_port, 
        gchRemoteSDPType,
        (char*)strMyTURN.c_str(), 
        STIManager::m_lRS.begin()->m_stP.staddr.port[0]);
  } else {
      //ret=Transport::gethostbyaddr(remote_sdp_ip, strRemoteSDP );
      //if(0==ret) {
        strRemoteSDP=remote_sdp_ip;
      //}
      sprintf( gchRemoteSDPAddr, "Media -> %s:%d; (%s) \n", 
        (char*)strRemoteSDP.c_str(), *remote_sdp_port,
        gchRemoteSDPType);
  }
  /*
  if(STIManager::m_lRS.size()>0) {
      sprintf( gchRemoteSDPAddr, "Media -> %s:%d; (%s) \nMy TURN server %s:%d\n", 
        remote_sdp_ip, *remote_sdp_port, 
        gchRemoteSDPType,
        STIManager::m_lRS.begin()->m_stP.staddr.addr[0], 
        STIManager::m_lRS.begin()->m_stP.staddr.port[0]);
  } else {
      sprintf( gchRemoteSDPAddr, "Media -> %s:%d; (%s) \n", 
        remote_sdp_ip,*remote_sdp_port,
        gchRemoteSDPType);
  }
  */

}//sti_run_ice_checks_callee

////////////////////////////////////////////////////////////////
//              CALL BACK FUNCTIONS for LOOKUP AND INSERT
////////////////////////////////////////////////////////////////
void* OnDisconnect(void *pN, int respcode) {
	if(respcode==200) {
		delete gpNode;	
    gpNode=NULL;
	}	
	return NULL;
}//OnDisconnect

void* OnRLookup(void* pN, int32 respcode) {

  struct LObject* pobjLO;
  list<resourceobject>::iterator i1;

  printf("OnRLookup: %d\n", respcode);

  pobjLO = (LObject*)pN;
  if(NULL == pN) {
    return 0;
  }

  for(i1=pobjLO->lstro.begin(); i1!=pobjLO->lstro.end(); i1++) {
    printf("Value: %s\n", (char*) (*i1).strov.value);
  }//end for

  return 0;
}//OnRLookup


void* OnULookup(void* pN, int32 respcode) {

  struct LObject* pobjLO;
  list<resourceobject>::iterator i1;

  printf("OnULookup: %d\n", respcode);

  if(NULL == pN) {
    printf("Not found\n");
    return 0;
  }

  pobjLO = (LObject*)pN;

  for(i1=pobjLO->lstro.begin(); i1!=pobjLO->lstro.end(); i1++) {
    printf("IP: %s port: %d\n", (char*) (*i1).stpeerinfo.staddr.addr[0], (*i1).stpeerinfo.staddr.port[0]);
  }//end for

  return 0;
}//OnULookup

void* OnSIPLookup(void* pN, int32 respcode) {

  struct LObject* pobjLO;
  list<resourceobject>::iterator i1;

  printf("OnSIPLookup: %d\n", respcode);

  if(NULL == pN) {
    printf("Not found\n");
    return 0;
  }

  pobjLO = (LObject*)pN;

  for(i1=pobjLO->lstro.begin(); i1!=pobjLO->lstro.end(); i1++) {
    printf("SIP IP: %s port: %d\n", (char*) (*i1).staddr.addr[0], (*i1).staddr.port[0]);
  }//end for

  return 0;
}//OnSIPLookup

void* OnPublishObject(void* pN, int32 respcode) {

  printf("OnInsert: %d\n", respcode);

  return 0;
}//OnLookup


void printRNtable(list<peerinfo>& lstP) {
  
  list<peerinfo>::iterator i1;
  int32 len, count=1;
  BigInt lpeerid;

  len = idlen;
  
  for(i1=lstP.begin(); i1!=lstP.end(); i1++) {
    
    //create the bigint out of peerid
    uchar_to_bigint( (*i1).stpeerid.id, len, lpeerid);
    
    printf("(%d) ID: 0x%s IP: %s port: %d ", count, lpeerid.ToHex().c_str(), (*i1).staddr.addr[0], (*i1).staddr.port[0] );

    if( (*i1).stunhashed.isPresent) {
      printf("userid: %s\n", (*i1).stunhashed.unhashed);
    }

    printf("\n");

    count++;
	   
  }//end for
  
}//printRNtable



void* OnGetDiagnostics(void* pN, int32 respcode) {

  struct LObject* pobjLO;
  list<resourceobject>::iterator i1;  
  int32 len, count=1;
  BigInt lresid;
  string str;

  printf("OnGetDiagnostics: %d\n", respcode);

  if(NULL == pN) {
    printf("Not found\n");
    return 0;
  }

  if(respcode != 200) {
    return 0;
  }

  pobjLO = (LObject*)pN;

  //Print routing table
  pobjLO->lstrt.size()>0 ? printf("Routing table\n-------------\n") : 1;
  printRNtable(pobjLO->lstrt);

  //Print neighbor table
  pobjLO->lstnt.size()>0 ? printf("Neighbor table\n-------------\n") : 1;
  printRNtable(pobjLO->lstnt);

  //Prinht resource table
  pobjLO->lstro.size()>0 ? printf("Resource table\n--------------\n") : 1;
  for(i1=pobjLO->lstro.begin(); i1!=pobjLO->lstro.end(); i1++) {
    
    len = idlen;

    //create the bigint out of peerid
    uchar_to_bigint( (*i1).stresid.id, len, lresid);
    
    printf("(%d) ID: %s ", count, lresid.ToHex().c_str() );

    //print unhashed id
    if( (*i1).stunhashedid.isPresent) {
      str = (char*)(*i1).stunhashedid.unhashed;
    }

    if( (*i1).conttype == eUSERINFO) {
      
      printf("USERINFO: %s IP: %s port: %d STORED BY IP: %s port: %d", (char*)str.c_str(), (*i1).stpeerinfo.staddr.addr[0], (*i1).stpeerinfo.staddr.port[0], pobjLO->pchIP, pobjLO->port );
      
    } else if ( (*i1).conttype == eSTRINGVAL) {
      printf("STRINGVAL: %s %s STORED BY IP: %s port: %d", (char*)str.c_str(), (*i1).strov.value, pobjLO->pchIP, pobjLO->port );
    }//end else

    printf("\n");

    count++;
  }//end for

  return 0;
}//OnGetDiagnostics


///////////////////////////////////////////////////////////////////
//            THREAD FUNCTIONS (Listener and Command Line)
//////////////////////////////////////////////////////////////////

#ifdef _LINUX_
inline void* _Listener(void* arg) {
#endif
#ifdef WIN32
DWORD WINAPI _Listener(void* arg) {
#endif
  
  Node* pNode = (Node*)arg;

  pj_thread_desc handler_desc;
  pj_thread_t *listener_thread;

  //register listener thread
  pj_thread_register("LISTENER", handler_desc, &listener_thread);

  //pNode->m_pobjTransport->Initialize(pNode->m_TCP_PORT, pNode->m_UDP_PORT);
  pNode->Initialize();

	if(eAUTO==cfg.m_NODE_TYPE && gpSTSIP->getNatType()==PJ_STUN_NAT_TYPE_OPEN) {
		Peer *pPeer;

			printf("Moving to Peer mode \n");
			if(eCHORD == cfg.m_P2P) {
			  printf("Starting PEER...Algorithm is Chord\n");
			  //pPeer = new ChordPeer();
			} else if(eKADEMLIA == cfg.m_P2P) {
			  printf("Starting PEER...Algorithm is Kademlia\n");
			  pPeer = new KadPeer();
			} else if(eBAMBOO == cfg.m_P2P) {
			  printf("Starting PEER...Algorithm is Bamboo\n");
			  pPeer = new BambooPeer();
			}
		 
			pPeer->setTransport(&transport);
			pPeer->setConfigParams(cfg);
      pPeer->setMode("PEER"); //set the mode to peer
			transport.setNode(pPeer);

			//set node* to peer
			gpNode = pPeer;
		    
		#ifdef _LINUX_
			tid = CreateThreadI(_Listener, (void*)gpNode);
		#endif
		    
		#ifdef WIN32
			tid = (int)CreateThread(0, 0, _Listener, (void*)gpNode, 0, (LPDWORD)&t);
		#endif

  }//if(eAUTO==cfg.m_NODE_TYPE)


  return 0;
}//_Listener


#ifdef _LINUX_
inline void* _CmdListener(void* arg) {
#endif
#ifdef WIN32
DWORD WINAPI _CmdListener(void* arg) {
#endif
  
  char line[500];
  char* ftok;
  char *pchCmd;
  char* pchArg;
  char* pchArg1;
  uint32 sbw, rbw;

  pj_thread_desc handler_desc;
  pj_thread_t *cmd_listener_thread;
  
  //register thread with pjlib
  pj_thread_register("CMDLISTENER", handler_desc, &cmd_listener_thread);
  
  Node* pNode;

  /*
  if(eCHORD == pNode->getP2P()) {
    pChord = (ChordPeer*)pNode;
  } else if(eKADEMLIA == pNode->getP2P()) {
    pKad = (KadPeer*)pNode;
  }
  */

  while(1) {
    printf(">>");
    cin.getline(line, 200);

    //Jugadd for now
    pNode = gpNode;

    if(strlen(line) < 4) {
      continue;
    }

    //tokenize it
    pchCmd = strtok_i(line, " ", &ftok);
    pchArg = strtok_i(NULL, " ", &ftok);
    

    if(0 == strcmp("exit", pchCmd)) {
      exit(0);
    } else if(0 == strcasecmp("showrt", pchCmd)) {
      pNode->ShowRT();
    } else if(0 == strcasecmp("shownt", pchCmd)) {
      pNode->ShowNT();
    } else if(0 == strcasecmp("showro", pchCmd)) {
      pNode->ShowRO();
    } else if(0 == strcasecmp("showbw", pchCmd)) {
      pNode->getTransport()->getSendRecvBW(sbw, rbw);
      printf("Bandwidth\n-------------\nSBW: %u B/s RBW: %u B/s\n", sbw, rbw);
      continue;
    } else if(0 == strcasecmp("showcp", pchCmd)) {
      pNode->ShowCP();
      continue;
    } else if(0 == strcasecmp("showcplist", pchCmd)) {
      pNode->ShowCPlist();
    } else if(0 == strcasecmp("showrelaylist", pchCmd)) {
      pNode->ShowRelayServerList();
    } else if(0 == strcasecmp("showturnalloc", pchCmd)) {
      pNode->ShowTURNAlloc();
    } else if(0 == strcasecmp("showrelayalloc", pchCmd)) {
      pNode->ShowRelayAllocations();
    } else if(0 == strcasecmp("rlookup", pchCmd)) {
      printf("%s %s\n", pchCmd, pchArg);
      pNode->LookupObject(pchArg, strlen(pchArg), eUDP, OnRLookup, NULL, NULL);

    } else if(0 == strcasecmp("ulookup", pchCmd)) {
      printf("%s %s\n", pchCmd, pchArg);
      pNode->LookupUser(pchArg, strlen(pchArg), eUDP, OnULookup, NULL, NULL);

    } else if(0 == strcasecmp("siplookup", pchCmd)) {
      printf("%s %s\n", pchCmd, pchArg);
      pNode->LookupSIP(pchArg, strlen(pchArg), eUDP, OnSIPLookup, NULL, NULL);

	} else if(0 == strcasecmp("insert", pchCmd)) {
      pchArg1 = strtok_i(NULL, " ", &ftok);
      printf("%s %s %s\n", pchCmd, pchArg, pchArg1);
      pNode->PublishObject(pchArg, strlen(pchArg), pchArg1, strlen(pchArg1), eUDP, OnPublishObject, NULL);

    } else if(0 == strcasecmp("getrt", pchCmd)) {
      pchArg1 = strtok_i(NULL, " ", &ftok);
      printf("%s %s %d\n", pchCmd, pchArg, atoi(pchArg1) );
      pNode->GetRT(pchArg, strlen(pchArg), atoi(pchArg1), eTCP, OnGetDiagnostics, NULL);

    } else if(0 == strcasecmp("getnt", pchCmd)) {
      pchArg1 = strtok_i(NULL, " ", &ftok);
      printf("%s %s %s\n", pchCmd, pchArg, pchArg1);
      pNode->GetNT(pchArg, strlen(pchArg), atoi(pchArg1), eTCP, OnGetDiagnostics, NULL);

    } else if(0 == strcasecmp("getro", pchCmd)) {
      pchArg1 = strtok_i(NULL, " ", &ftok);
      printf("%s %s %s\n", pchCmd, pchArg, pchArg1);
      pNode->GetRO(pchArg, strlen(pchArg), atoi(pchArg1), eUDP, OnGetDiagnostics, NULL);
    }//end if
    

    
  }//end while

  return 0;

}//_CmdListener


/*
struct FuncInfo {
	PhoneLine* _pPhoneLine;
	std::string _phoneNumber;
	ConferenceCall* _conferenceCall;
	char* _pIPresult;
};
*/

int create_media_socket(char* buf) {
	int sock;
	char* optval;
	
	sock=socket (PF_INET, SOCK_DGRAM, 0);

	//cannot create socket
	if(sock <= 0) {
		return -1;
	}

	//set to non blocking
  setNonblocking(sock, 1);
	//setBlocking(sock, 1);

	//bind
	bindi(sock, atoi(buf) );

	//set sockopt
	//setsockopt (session->rtp.socket, SOL_SOCKET, SO_REUSEADDR, (void *) &optval, sizeof (optval));
      
	return sock;
}//create_media_socket

int create_audio_manager() {
  PhApiWrapper *p = PhApiWrapper::PhApiWrapperHack;
  char bufA[10], bufV[10];
  int sockA, sockV;
  int addrlen, len;
  unsigned short port;
  struct sockaddr_in staddr;
  unsigned char buf[1500];
  char ipaddr[20], cport[10];
  int32 ret;

  //get the audio video ports
  p->getAudioPort(bufA);
  p->getVideoPort(bufV);

  //create the sockets
  //TODO: be sure to close the sockets

  sockA=create_media_socket(bufA);
  sockV=create_media_socket(bufV);

  //set these sockets in appropriate
  p->setAudioSock(&sockA);
  p->setVideoSock(&sockV);


  //We are now in call. Both for makeCall and incoming call
  STIManager::m_inCall=1;
  

  gpSTAudio = new STIManager;
	gpSTAudio->InitializeClient(sockA, NULL, atoi(bufA));
	gpSTAudio->GetSelfAddr(ipaddr,&port);
	p->setSDPAudioHost(ipaddr, atoi(bufA));

  if(gpSTAudio->getNatType()==PJ_STUN_NAT_TYPE_SYMMETRIC_UDP || 
    gpSTAudio->getNatType()==PJ_STUN_NAT_TYPE_FULL_CONE ||
	  gpSTAudio->getNatType()==PJ_STUN_NAT_TYPE_SYMMETRIC ||
	  gpSTAudio->getNatType()==PJ_STUN_NAT_TYPE_PORT_RESTRICTED ||
	  gpSTAudio->getNatType()==PJ_STUN_NAT_TYPE_RESTRICTED) {

		 //len
		addrlen=sizeof(staddr);

    //wait for allocate response for 5 seconds
    ret=selectforsocket(sockA, 5000);
    
    //receive the stun allocate response data
    if(1 == ret) {		  
      len = recvfrom(sockA, (char*)buf, 1500,  0, 
	                (struct sockaddr*) &staddr, (socklen_t*)&addrlen);
      if(STIManager::IsStunPacket(buf, len)) {
	      gpSTAudio->ParseStunPacket(buf, len, &staddr, &addrlen, sockA, eSOCK_AUDIO);
	      gpSTAudio->GetRelayAddress(ipaddr);
	      gpSTAudio->GetRelayPort(&port);
	      p->setSDPAudioRelay(ipaddr, port);
      }//if 
    }//end if


    //TODO: this code maybe removed
    //because we get the STUN binding response with Allocate response
	  //get srflex addr
	  gpSTAudio->SendBindRequest(sockA, NULL, atoi(bufA));

    //FIXME: too much time spent here if not successful?
    //wait for binding response for 5 seconds
    ret=selectforsocket(sockA, 5000);
    
    //we have not received any response. Now cleanup
    if(1 == ret) {
 		  //receive the stun allocate response data
	    len = recvfrom(sockA, (char*)buf, 1500,  0, 
			  (struct sockaddr*) &staddr, (socklen_t*)&addrlen);    

	    if(STIManager::IsStunPacket(buf, len)) {
		    gpSTAudio->ParseStunPacket(buf, len, &staddr, &addrlen, sockA, eSOCK_AUDIO);
		    gpSTAudio->GetSrflexAddress(ipaddr);
		    gpSTAudio->GetSrflexPort(&port);
		    p->setSDPAudioSrflex(ipaddr,port);
      }
    }//end if

	}//if NAT found
		
	//Set non blocking 
	//setNonblocking(sockA, 1);
	gpSTAudio->ListenForICE();

  //successful
  return 1;
}//create_audio_manager()


void* Peerfound(void* pResult, int32 respcode){
	peerfound=true;
  PhApiWrapper *p = PhApiWrapper::PhApiWrapperHack;
  struct LObject* 	pobjLO = (LObject*)pResult;
  list<resourceobject>::iterator i1;
  char cport[10], ipaddr[50];
  uint16 port;
  int ret;

  printf("OnULookup: %d\n", respcode);

  if(respcode!=200) {
    printf("Not found\n");
    peerfound=false;
    PhoneCall * phoneCall = ((PhoneLine*)(pobjLO->pApp))->getActivePhoneCall();
    if(phoneCall){
      //phoneCall->close(); crashed
      phoneCall->setState(EnumPhoneCallState::PhoneCallStateError);
      //phoneCall->setState(EnumPhoneCallState::PhoneCallStateClosed); crashed
      ((PhoneLine*)(pobjLO->pApp))->clearActivePhoneCall();
    	
      //((PhoneLine*)(pobjLO->pApp))->phoneCallClosedEvent( *((PhoneLine*)(pobjLO->pApp)), *phoneCall);
    }
    return 0;
  }

  i1=pobjLO->lstro.begin();
  sprintf(cport, "%d\0",  i1->staddr.port[0]);
  std::string sipAddr= "sip:" + pobjLO->strKey+ "@" + i1->staddr.addr[0] + ":" + cport;

	
	if(!gpSTAudio){
    ret=create_audio_manager();
	}

  //Set the appropriate SIP contact
  //If we are behind NAT, get the appropriate relay for SIP
  if(STIManager::m_eNatType != PJ_STUN_NAT_TYPE_OPEN 
    && STIManager::m_eNatType != PJ_STUN_NAT_TYPE_UNKNOWN) {
    gpSTSIP->GetRelayAddress(ipaddr);
    gpSTSIP->GetRelayPort(&port);
    
    sprintf(cport, "%d",  port);

    std::string contacturl = "<sip:" +  ((PhoneLine*)(pobjLO->pApp))->getSipAccount().getIdentity() + "@" + ipaddr + ":" + cport + ">";
    p->setContactURL(contacturl);
  }
  
	((PhoneLine*)(pobjLO->pApp))->makeCall(sipAddr, NULL);

  peerfound=false;
  return NULL; //Required in windows.

}//Peerfound

void* JoinCallback(void* pResult, int32 respcode) {

	struct LObject* pobjLO;
	int sock, addrlen;
	struct sockaddr_in staddr,sipsockaddr;
	char ipaddr[32], cport[10];
	int len;
  unsigned short port,sipport;
	unsigned char buf[1500];

  char addrarr[16][IPADDRLEN];   //variables for getaddrmap
  unsigned char htype[16], ttype[16];
  unsigned short portarr[16];
  int32 num=0;

  pobjLO = (LObject*)pResult;

	if( respcode==200 && (NULL != pResult) && 
      (pobjLO->ritype == eJOIN || pobjLO->ritype == eBOOTSTRAP) ) {
    
		//join successful
		((PhoneLine*)(pobjLO->pApp))->setState(EnumPhoneLineState::PhoneLineStateOk);
		PhApiWrapper * p = PhApiWrapper::PhApiWrapperHack;
		if(!p->isRegistered()) {
			p->setRegistered(true);
			p->phoneLineStateChangedEvent(*p, ((PhoneLine*)(pobjLO->pApp))->getLineId(), EnumPhoneLineState::PhoneLineStateOk);
			p->connectedEvent(*p);
		}
		//Initialize stun TODO: change 5060
		p->getUDPSock(&sock);
    len=sizeof(sipsockaddr);
    getsockname(sock, (struct sockaddr*)&sipsockaddr, (socklen_t*)&len);
    sipport=ntohs(sipsockaddr.sin_port);
    gpNode->getTransport()->getAddress(num, addrarr, portarr, htype, ttype ); //get my own ip address from p2pp transport
    strcpy(ipaddr, addrarr[0]);
		gpSTSIP->InitializeClient(sock, NULL, sipport);

		if(gpSTSIP->getNatType()==PJ_STUN_NAT_TYPE_SYMMETRIC_UDP || 
	     gpSTSIP->getNatType()==PJ_STUN_NAT_TYPE_FULL_CONE ||
		   gpSTSIP->getNatType()==PJ_STUN_NAT_TYPE_SYMMETRIC ||
		   gpSTSIP->getNatType()==PJ_STUN_NAT_TYPE_PORT_RESTRICTED ||
		   gpSTSIP->getNatType()==PJ_STUN_NAT_TYPE_RESTRICTED) {

			 //len
			addrlen=sizeof(staddr);

			//receive the stun allocate response data
			len = recvfrom(sock, (char*)buf, 1500,  0, 
				          (struct sockaddr*) &staddr, (socklen_t*)&addrlen);

			if(STIManager::IsStunPacket(buf, len)) {
				gpSTSIP->ParseStunPacket(buf, len, &staddr, &addrlen, sock, eSOCK_SIP);
				gpSTSIP->GetRelayAddress(ipaddr);
				gpSTSIP->GetRelayPort(&port);

        //this fcone is NOT USED
				p->initNAT("fcone", ipaddr, port );
			  sprintf(cport, "%d\0",  port);
				std::string contacturl = "<sip:" + ((PhoneLine*)(pobjLO->pApp))->getSipAccount().getIdentity() + "@" + ipaddr + ":" + cport + ">";
				p->setContactURL(contacturl);
      }//if 
		}//if NAT found 
    else {
      //open nat
   

      p->initNAT("open", ipaddr, sipport );
      sprintf(cport, "%d",  sipport);
      std::string contacturl = "<sip:" + ((PhoneLine*)(pobjLO->pApp))->getSipAccount().getIdentity() + "@" + ipaddr + ":" + cport + ">";
      p->setContactURL(contacturl);

    }
	} else {
		//join failed
  	((PhoneLine*)(pobjLO->pApp))->disconnect();
    transport.Disconnect();
		//_sipAccount.setP2PP(false);
	}
	return NULL;

}//JoinCallback


void* OnTimerCallback(void *ptr) {
	PhoneLine *pLine = (PhoneLine*)ptr;
	pj_thread_desc handler_desc;
	pj_thread_t *main_thread;

    //register listener thread
	if(_CallClosed) {
		_CallClosed=false;
		pj_thread_register("MAIN", handler_desc, &main_thread);
	}
	
	if(gpSTSIP) {
		//gpSTSIP->OnTimer2();
	}
	if(gpSTAudio) {
		//gpSTAudio->OnTimer2();
	}
	return NULL;
}
//////*****************P2PP code end******************//////////

PhoneLine::PhoneLine(SipAccount & sipAccount, UserProfile & userProfile)
	: _sipAccount(sipAccount),
	_userProfile(userProfile) {

	static PhoneLineStateUnknown stateUnknown;
	_phoneLineStateList += &stateUnknown;
	_state = &stateUnknown;

	static PhoneLineStateProgress stateProgress;
	_phoneLineStateList += &stateProgress;

	static PhoneLineStateOk stateOk;
	_phoneLineStateList += &stateOk;

	static PhoneLineStateClosed stateClosed;
	_phoneLineStateList += &stateClosed;

	static PhoneLineStateTimeout stateTimeout;
	_phoneLineStateList += &stateTimeout;

	static PhoneLineStateServerError stateServerError;
	_phoneLineStateList += &stateServerError;

	static PhoneLineStateAuthenticationError stateAuthenticationError;
	_phoneLineStateList += &stateAuthenticationError;

	_activePhoneCall = NULL;

	//Must be called after _phoneLineStateList initialization
	_sipWrapper = SipWrapperFactory::getFactory().createSipWrapper();

	_sipCallbacks = new SipCallbacks(*_sipWrapper, _userProfile);

	_lineId = SipWrapper::VirtualLineIdError;

	initSipWrapper();
	////

	Config & config = ConfigManager::getInstance().getCurrentConfig();
	config.valueChangedEvent
		+= boost::bind(&PhoneLine::configureSipWrapper, this);
}

PhoneLine::~PhoneLine() {
	disconnect();

	//Do not need to delete everything (_phoneLineStateList)
	//since states are static inside the constructor
	_state = NULL;

	OWSAFE_DELETE(_sipCallbacks);

	_sipWrapper = NULL;

	OWSAFE_DELETE(_activePhoneCall);
}

std::string PhoneLine::getMySipAddress() const {
	return "sip:" + _sipAccount.getIdentity() + "@" + _sipAccount.getRealm();
}

int PhoneLine::makeCall(const std::string & phoneNumber, ConferenceCall * conferenceCall) {
		int callId;

		if(_sipAccount.getP2PP()){
			
			if(!peerfound) {
				std::string sipAddr;	
				
				if(phoneNumber.find('@')!=std::string::npos){
					sipAddr=phoneNumber.substr(0, phoneNumber.find('@') );
				}else {
					sipAddr=phoneNumber;
				}

				if (sipAddr.empty()) {
					LOG_ERROR("empty phone number");
					return SipWrapper::CallIdError;
				}

       if(sipAddr.compare((char*)cfg.m_USERID)==0) {
					LOG_ERROR("self call error");
					return SipWrapper::CallIdError;
				}

       for (PhoneCalls::iterator it = _phoneCallMap.begin(); it != _phoneCallMap.end(); ++it) {
					PhoneCall * phoneCall = (*it).second;

					//FIXME this should be a LOG_FATAL()
					if (phoneCall) {
						EnumPhoneCallState::PhoneCallState state = phoneCall->getState();
						if (state != EnumPhoneCallState::PhoneCallStateHold &&
							state != EnumPhoneCallState::PhoneCallStateClosed) {

							LOG_ERROR("cannot place the call=" + sipAddr + ", at least another phone call is not in hold state");
							return SipWrapper::CallIdError;
						}
					}
				}
				SipAddress tmpSipAddr(sipAddr);
				PhoneCall * phoneCall = new PhoneCall(*this, tmpSipAddr);
				phoneCall->setConferenceCall(conferenceCall);
				_activePhoneCall = phoneCall;
				phoneCall->setState(EnumPhoneCallState::PhoneCallStateDialing);

				//Sends the event a new PhoneCall has been created
				phoneCallCreatedEvent(*this, *phoneCall);

				//History: create a HistoryMemento for this outgoing call
				HistoryMemento * memento = new HistoryMemento(
					HistoryMemento::OutgoingCall, tmpSipAddr.getSipAddress(), -1);
				//	_userProfile.getHistory().addMemento(memento);

				gpNode->LookupSIP((char8*)sipAddr.c_str(), sipAddr.length(), eUDP, Peerfound, this, (void*)conferenceCall);
				return 1;
			
			}//!peerfound


			SipAddress sipAddress(phoneNumber);/* = SipAddress::fromString(
				phoneNumber, _sipAccount.getRealm()
			);*/
		   //By salman
			LOG_DEBUG("SALMAN sipAddress=" + sipAddress.getRawSipAddress());

			//Puts all the PhoneCall in the hold state before to create a new PhoneCall
			//holdAllCalls();

			Config & config = ConfigManager::getInstance().getCurrentConfig();
			bool enableVideo = config.getVideoEnable();

			if (enableVideo) {
				//Sets the video device
				_sipWrapper->setVideoDevice(config.getVideoWebcamDevice());
			}

			PhoneCall * phoneCall = getActivePhoneCall();
			//phoneCall->setState(EnumPhoneCallState::PhoneCallStateClosed);
			//phoneCall = new PhoneCall(*this, sipAddress);
			phoneCall->setConferenceCall(conferenceCall);
			_activePhoneCall = phoneCall;


		    callId = _sipWrapper->makeCall(
				_lineId, sipAddress.getRawSipAddress(), enableVideo
			);

			if (callId < 0) {
				//FIXME should be replaced by a LOG_FATAL()
				LOG_ERROR("couldn't place the call, SipWrapper returned an error=" +
					String::fromNumber(callId));
				return callId;
			}
			phoneCall->setCallId(callId);

			//Adds the PhoneCall to the list of PhoneCall
			_phoneCallMap[callId] = phoneCall;

			phoneCall->setState(EnumPhoneCallState::PhoneCallStateDialing);


			//return callId;


	}else {

			if (!_sipAccount.isConnected()) {
				LOG_ERROR("SipAccount not connected");
				return SipWrapper::CallIdError;
			}

			if (phoneNumber.empty()) {
				LOG_ERROR("empty phone number");
				return SipWrapper::CallIdError;
			}

			for (PhoneCalls::iterator it = _phoneCallMap.begin(); it != _phoneCallMap.end(); ++it) {
				PhoneCall * phoneCall = (*it).second;

				//FIXME this should be a LOG_FATAL()
				if (phoneCall) {
					EnumPhoneCallState::PhoneCallState state = phoneCall->getState();
					if (state != EnumPhoneCallState::PhoneCallStateHold &&
						state != EnumPhoneCallState::PhoneCallStateClosed) {

						LOG_ERROR("cannot place the call=" + phoneNumber + ", at least another phone call is not in hold state");
						return SipWrapper::CallIdError;
					}
				}
			}

			SipAddress sipAddress = SipAddress::fromString(
				phoneNumber, _sipAccount.getRealm()
			);

			//Puts all the PhoneCall in the hold state before to create a new PhoneCall
			//holdAllCalls();

			Config & config = ConfigManager::getInstance().getCurrentConfig();
			bool enableVideo = config.getVideoEnable();

			if (enableVideo) {
				//Sets the video device
				_sipWrapper->setVideoDevice(config.getVideoWebcamDevice());
			}

			PhoneCall * phoneCall = new PhoneCall(*this, sipAddress);
			phoneCall->setConferenceCall(conferenceCall);
			_activePhoneCall = phoneCall;
			callId = _sipWrapper->makeCall(
				_lineId, sipAddress.getRawSipAddress(), enableVideo
			);

			if (callId < 0) {
				//FIXME should be replaced by a LOG_FATAL()
				LOG_ERROR("couldn't place the call, SipWrapper returned an error=" +
					String::fromNumber(callId));
				return callId;
			}

			phoneCall->setCallId(callId);

			//Adds the PhoneCall to the list of PhoneCall
			_phoneCallMap[callId] = phoneCall;

			phoneCall->setState(EnumPhoneCallState::PhoneCallStateDialing);

			//Sends the event a new PhoneCall has been created
			phoneCallCreatedEvent(*this, *phoneCall);

			//History: create a HistoryMemento for this outgoing call
			HistoryMemento * memento = new HistoryMemento(
				HistoryMemento::OutgoingCall, sipAddress.getSipAddress(), callId);
			_userProfile.getHistory().addMemento(memento);
	}

	return callId;
}

void PhoneLine::connect() {
	//P2PP

	if (_sipAccount.isConnected())
	{ // Nothing to do if the line is already connected!
		return;
	}
	_lineId = _sipWrapper->addVirtualLine(
		_sipAccount.getDisplayName(),
		_sipAccount.getUsername(),
		_sipAccount.getIdentity(),
		_sipAccount.getPassword(),
		_sipAccount.getRealm(),
		_sipAccount.getSIPProxyServerHostname(),
		_sipAccount.getRegisterServerHostname());

	_sipAccount.setVLineID(_lineId);

	//P2PP
	if(!_sipAccount.getP2PP()){
		_sipWrapper->registerVirtualLine(_lineId);
	}

	LOG_DEBUG("connect username=" + _sipAccount.getUsername()
		+ " server=" + _sipAccount.getRegisterServerHostname()
		+ " lineId=" + String::fromNumber(_lineId));

//////P2PP in progress
/*code to connect to p2pp bootstrap comes here*/
////////////***********P2PP code starts******************/////////////

  if(_sipAccount.getP2PP()) {

    if(Peer::m_eState != ePFAILURE && !gpNode) {
	    //set stun and ICE callbacks for SIP lower library
		  PhApiWrapper * p = PhApiWrapper::PhApiWrapperHack;
  	    
		  //STUN (SEND, DATA) callbacks for SIP

		  p->registerStunCallbacks((void*)&sti_create_send_indication, (void*)&sti_read_data_from_ind);
	      //STUN (SEND, DATA) callbacks for AUDIO
		  p->registerAudioStunCallbacks((void*)&sti_create_send_indication_audio, (void*)&sti_read_data_from_stun_audio);
		  //TODO: STUN (SEND, DATA) callbacks for IM, VIDEO
  		
		  //STUN callbacks for running ICE connectivity on (FIXME) audio
		  p->registerICECallbacks((void*)&sti_audio_set_remote_sdp_candidates,
                              (void*)&sti_audio_set_remote_sdp_candidates_callee,
                              (void*)&sti_run_ice_checks_callee);

	      _sipAccount.setConnected(true);

		  InitLogFile("log-p2pp.txt");
  			
		  pj_thread_desc handler_desc;
		  pj_thread_t *main_thread;

	    //register listener thread
      //FIXME: thread_register 
		  pj_thread_register("MAIN", handler_desc, &main_thread);

		  char* pchCfg = "p2ppconfig.txt";
      //configfile = pchCfg;

	    Peer* pPeer;
	    Client* pClient;

	    //Parse the configuration file
	    cfg.parseFile(pchCfg);

	    //set idlen. To get the default hash function size code uses
	    idlen=cfg.m_HASH_SIZE;

	    //set associations
	    if(eBOOTSTRAP == cfg.m_NODE_TYPE) {

		    if(eCHORD == cfg.m_P2P) {
		      printf("Algorithm is Chord\n");
		    } else if(eKADEMLIA == cfg.m_P2P) {
		      printf("Algorithm is Kademlia\n");
		    } else if(eBAMBOO == cfg.m_P2P) {
		      printf("Algorithm is Bamboo...Bootstrap server\n");
		    }

		    gpNode = new Bootstrap();    

		    gpNode->setTransport(&transport);
		    gpNode->setConfigParams(cfg);
		    transport.setNode(gpNode);
  	    
      } else if(ePEER == cfg.m_NODE_TYPE) {

		    if(eCHORD == cfg.m_P2P) {
		      printf("Starting peer...Algorithm is Chord\n");
		      //pPeer = new ChordPeer();
		    } else if(eKADEMLIA == cfg.m_P2P) {
		      printf("Starting peer...Algorithm is Kademlia\n");
		      pPeer = new KadPeer();
		    } else if(eBAMBOO == cfg.m_P2P) {
		      printf("Starting PEER...Algorithm is Bamboo\n");
		      pPeer = new BambooPeer();
		    }

		    pPeer->setTransport(&transport);
		    pPeer->setConfigParams(cfg);
		    transport.setNode(pPeer);

		    //set node* to peer
		    gpNode = pPeer;
		    gpNode->setCallBack(JoinCallback, (void*)this);
		    gpNode->setOnTimerCallback(OnTimerCallback);

        //this is STIManager for SIP. Only need in client.
        gpSTSIP = new STIManager;
  	
      } else if(eCLIENT == cfg.m_NODE_TYPE) {
		    printf("Starting Client\n");
		    pClient = new Client();

		    pClient->setTransport(&transport);
		    pClient->setConfigParams(cfg);
		    transport.setNode(pClient);

		    gpNode = pClient;
		    gpNode->setCallBack(JoinCallback, (void*)this);		  
		    gpNode->setOnTimerCallback(OnTimerCallback);
  		  
        //this is STIManager for SIP. Only need in client.
        gpSTSIP = new STIManager;
  	  
      } else if(eDIAGNOSTIC_SERVER == cfg.m_NODE_TYPE) {
		    printf("Starting Diagnostic server\n");
		    gpNode = new Diagnostics();

		    gpNode->setTransport(&transport);
		    gpNode->setConfigParams(cfg);
		    ((Diagnostics*)gpNode)->setDiagnosticsFile(cfg.m_Diagnosticsfile);
		    transport.setNode(gpNode);
  	  
      } else if(eAUTO==cfg.m_NODE_TYPE) {
		    printf("Starting NAT detection and Client/Peer mode selection\n");
		    printf("Starting Client\n");
		    pClient = new Client();

		    pClient->setTransport(&transport);
		    pClient->setConfigParams(cfg);
		    transport.setNode(pClient);

		    gpNode = pClient;
		    gpNode->setCallBack(JoinCallback, (void*)this);		  
		    gpNode->setOnTimerCallback(OnTimerCallback);

        //Initially AUTO mode starts as client, so a SIP STIManager
		    gpSTSIP = new STIManager;

	    }//end else


	    //1) Start the timer thread
	    //2) Start the listening sockets and threads
  	  
	  #ifdef _LINUX_
	    tid = CreateThreadI(_Listener, (void*)gpNode);
	    //command line
	    if(1 == cfg.m_CMDLINE) {
        ctid = CreateThreadI(_CmdListener, (void*)gpNode);
	    }
	  #endif

	  #ifdef WIN32
	    tid = (int)CreateThread(0, 0, _Listener, (void*)gpNode, 0, (LPDWORD)&t);

	    if(1 == cfg.m_CMDLINE) {
        ctid = (int)CreateThread(0, 0, _CmdListener, (void*)gpNode, 0, (LPDWORD)&t);
	    }
	  #endif

	  } else {
      //P2PP
      //if gpNode exists
      //This is because Wengo connection times out after sometime, and runs the connect()
      //function (this function) again. In that case, gpNode will be overwritten.
      //To prevent that, we check if gpNode exists, and if it does, we do not create it.
	    _sipAccount.setConnected(true);
		  setState(EnumPhoneLineState::PhoneLineStateOk);
		  PhApiWrapper * p = PhApiWrapper::PhApiWrapperHack;
		  if(!p->isRegistered()) {
			  p->setRegistered(true);
			  p->phoneLineStateChangedEvent(*p, _lineId, EnumPhoneLineState::PhoneLineStateOk);
			  p->connectedEvent(*p);
		  }
	  }
  /***********************
		  if(1 == cfg.m_CMDLINE) {
			WaitForThread(ctid);
		  }
		  ************************/
		  /////Is this the right place?
		 //setState(EnumPhoneLineState::PhoneLineStateOk);
		//_sipWrapper->setRegistered(true);
		//_sipWrapper->phoneLineStateChangedEvent(*_sipWrapper, _lineId, EnumPhoneLineState::PhoneLineStateOk);
		//_owplLineSetState(_lineId, LINESTATE_REGISTERED, LINESTATE_CAUSE_NORMAL);
		//PhApiWrapper * p = PhApiWrapper::PhApiWrapperHack;
		//if(!p->isRegistered()) {
		//	p->setRegistered(true);
		//	p->phoneLineStateChangedEvent(*p, _lineId, EnumPhoneLineState::PhoneLineStateOk);
		//	p->connectedEvent(*p);
		//}
  
  }//if P2PAccount

  /////***********P2PP code ends ***********************/////////////////////


}//connect()


void PhoneLine::disconnect() {
	if (_lineId != SipWrapper::VirtualLineIdError) {
		_sipAccount.setConnected(false);
		_sipWrapper->removeVirtualLine(_lineId);
	}

  //////P2PP in progress
	if(_sipAccount.getP2PP() ){
		gpNode->Disconnect(OnDisconnect, NULL);
		/*transport.uninitialize();*/	
		transport.Disconnect();
		//_sipAccount.setP2PP(false);
		_sipAccount.setConnected(false);
	}
	//////P2PP in progress

  //FIXME: What happens when we are running a SIP account

}//disconnect()


void PhoneLine::checkCallId(int callId) {
	PhoneCall * phoneCall = getPhoneCall(callId);
	if (!phoneCall) {
		LOG_FATAL("unknown phone call callId=" + String::fromNumber(callId));
	}
}

static int g_isFlagAccept=0;

void PhoneLine::acceptCall(int callId) {
	//FIXME crash in some cases
	//checkCallId(callId);
  if(0==g_isFlagAccept) {
    g_isFlagAccept=1;

	  holdCallsExcept(callId);

	  Config & config = ConfigManager::getInstance().getCurrentConfig();
	  bool enableVideo = config.getVideoEnable();

	  if (enableVideo) {
		  //Sets the video device
		  _sipWrapper->setVideoDevice(config.getVideoWebcamDevice());
	  }

	  _sipWrapper->acceptCall(callId, enableVideo);
	  LOG_DEBUG("call accepted callId=" + String::fromNumber(callId));

  }
}

void PhoneLine::rejectCall(int callId) {
	//FIXME crash in some cases
	//checkCallId(callId);

	_sipWrapper->rejectCall(callId);
	//History: retrieve the memento and change its state to rejected
	_userProfile.getHistory().updateCallState(callId, HistoryMemento::RejectedCall);
	LOG_DEBUG("call rejected callId=" + String::fromNumber(callId));
}

void PhoneLine::closeCall(int callId) {
	checkCallId(callId);
	_sipWrapper->closeCall(callId);
	LOG_DEBUG("call closed callId=" + String::fromNumber(callId));
}

void PhoneLine::holdCall(int callId) {
	checkCallId(callId);
	_sipWrapper->holdCall(callId);
	LOG_DEBUG("call hold callId=" + String::fromNumber(callId));
}

void PhoneLine::resumeCall(int callId) {
	checkCallId(callId);
	_sipWrapper->resumeCall(callId);
	LOG_DEBUG("call resumed callId=" + String::fromNumber(callId));
}

void PhoneLine::blindTransfer(int callId, const std::string & sipAddress) {
	checkCallId(callId);
	SipAddress sipUri = SipAddress::fromString(
		sipAddress, getSipAccount().getRealm()
	);
	_sipWrapper->blindTransfer(callId, sipUri.getRawSipAddress());
	LOG_DEBUG("call transfered to=" + sipAddress);
}

void PhoneLine::playSoundFile(int callId, const std::string & soundFile) {
	//No check
	//checkCallId(callId);
	_sipWrapper->playSoundFile(callId, soundFile);
}

void PhoneLine::playDtmf(int callId, char dtmf) {
	//No check
	//checkCallId(callId);
	_sipWrapper->playDtmf(callId, dtmf);
}


CodecList::AudioCodec PhoneLine::getAudioCodecUsed(int callId) {
	return _sipWrapper->getAudioCodecUsed(callId);
}

CodecList::VideoCodec PhoneLine::getVideoCodecUsed(int callId) {
	return _sipWrapper->getVideoCodecUsed(callId);
}

void close_call() {

  PhApiWrapper *p = PhApiWrapper::PhApiWrapperHack;
  pj_thread_desc handler_desc;
  pj_thread_t *yetanother_thread;
  int sock=0;

  //register listener thread
  pj_thread_register("YETANOTHER", handler_desc, &yetanother_thread);

  sock=-1;
  //p->setAudioSock(&sock);
  p->setSDPAudioRelay(NULL,0);
  p->setSDPAudioSrflex(NULL,0);
  if(gpSTAudio) {
    delete gpSTAudio;
  }
  gpSTAudio = NULL;
  _CallClosed=true;

  //We are no longer in call
  STIManager::m_inCall=0;
  g_isFlagAccept=0;
}//close_call

void PhoneLine::setPhoneCallState(int callId, EnumPhoneCallState::PhoneCallState state, const SipAddress & sipAddress) {
    //##############P2PP starts############# 
	PhApiWrapper * p;
	int sock;
  int ret;

  LOG_DEBUG("call state changed callId=" + String::fromNumber(callId) +
	" state=" + EnumPhoneCallState::toString(state) +
	" from=" + sipAddress.getSipAddress());

	//Saves the last state
	EnumPhoneCallState::PhoneCallState lastState = EnumPhoneCallState::PhoneCallStateUnknown;
	PhoneCall * phoneCall = getPhoneCall(callId);
	if (phoneCall) {

		lastState = phoneCall->getState();

		if (phoneCall->getState() == state) {
			//We are already in this state
			//Prevents the state to be applied 2 times in a row
			return;
		}

		phoneCall->setState(state);
	}

	// This should not replace the state machine pattern PhoneCallState/ PhoneLineState
	switch (state) {

	case EnumPhoneCallState::PhoneCallStateUnknown:
		break;

	case EnumPhoneCallState::PhoneCallStateError:
		callClosed(callId);
    close_call();
		break;

	case EnumPhoneCallState::PhoneCallStateResumed:
		holdCallsExcept(callId);
		_activePhoneCall = getPhoneCall(callId);
		break;

	case EnumPhoneCallState::PhoneCallStateTalking:
		_activePhoneCall = getPhoneCall(callId);
		break;

	case EnumPhoneCallState::PhoneCallStateDialing:
		break;

	case EnumPhoneCallState::PhoneCallStateRinging:
		break;

	case EnumPhoneCallState::PhoneCallStateClosed:{
		  pj_thread_desc handler_desc;
		  pj_thread_t *yetanother_thread;

		  //register listener thread
		  pj_thread_register("YETANOTHER", handler_desc, &yetanother_thread);
		if (lastState == EnumPhoneCallState::PhoneCallStateIncoming) {
			//History: retrieve the memento and change its state to missed
			_userProfile.getHistory().updateCallState(
				callId, HistoryMemento::MissedCall);
			LOG_DEBUG("call missed callId=" + String::fromNumber(callId));
		}
		callClosed(callId);
	  //##############P2PP starts############# 
		  p = PhApiWrapper::PhApiWrapperHack;
		  sock=-1;
		  //p->setAudioSock(&sock);
		  p->setSDPAudioRelay(NULL,0);
		  p->setSDPAudioSrflex(NULL,0);
		  if(gpSTAudio) {
			  delete gpSTAudio;
		  }
		  gpSTAudio = NULL;
		  _CallClosed=true;

      //We are no longer in call
      STIManager::m_inCall=0;
      g_isFlagAccept=0;
		  //transport.resumeSelect();

      //##############P2PP ends############# 
		break;
	}
	case EnumPhoneCallState::PhoneCallStateIncoming: {
		//Sends SIP code 180
		//TODO automatically??

//##############P2PP starts############# 
		if(!gpSTAudio){
			ret=create_audio_manager();
		}
    //Set the appropriate SIP contact
    //If we are behind NAT, get the appropriate relay for SIP
    PhApiWrapper *p = PhApiWrapper::PhApiWrapperHack;
    if(STIManager::m_eNatType != PJ_STUN_NAT_TYPE_OPEN) {
      char cport[10], ipaddr[50];
      uint16 port;

      gpSTSIP->GetRelayAddress(ipaddr);
      gpSTSIP->GetRelayPort(&port);
      
      sprintf(cport, "%d",  port);

      std::string contacturl = "<sip:" +  this->getSipAccount().getIdentity() + "@" + ipaddr + ":" + cport + ">";
	    p->setContactURL(contacturl);
    } 

//##############P2PP ends############# 

		_sipWrapper->sendRingingNotification(callId,
			ConfigManager::getInstance().getCurrentConfig().getVideoEnable());

		PhoneCall * phoneCall = new PhoneCall(*this, sipAddress);
		phoneCall->setCallId(callId);

		//Adds the PhoneCall to the list of PhoneCall
		_phoneCallMap[callId] = phoneCall;

		phoneCall->setState(state);

		_activePhoneCall = phoneCall;

		//Sends the event a new PhoneCall has been created
		phoneCallCreatedEvent(*this, *phoneCall);

		//History: create a HistoryMemento for this incoming call
		std::string displayName = sipAddress.getDisplayName();
		if (displayName.empty()) {
			displayName = sipAddress.getUserName();
		}
		HistoryMemento * memento = new HistoryMemento(
				HistoryMemento::IncomingCall, displayName, callId);
		_userProfile.getHistory().addMemento(memento);

		break;
	}

	case EnumPhoneCallState::PhoneCallStateHold:
		break;

	case EnumPhoneCallState::PhoneCallStateMissed:
		//History: retrieve the memento and change its state to missed
		_userProfile.getHistory().updateCallState(callId, HistoryMemento::MissedCall);
		LOG_DEBUG("call missed callId=" + String::fromNumber(callId));
		break;

	case EnumPhoneCallState::PhoneCallStateRedirected:
		break;

	default:
		LOG_FATAL("unknown PhoneCallState=" + EnumPhoneCallState::toString(state));
	}
}

void PhoneLine::callClosed(int callId) {
	//If it crashes inside checkCallId() this is probably due
	//to a duplicated phCALLCLOSED message
	//checkCallId(callId);

	PhoneCall * phoneCall = getPhoneCall(callId);
	if (!phoneCall) {
		//Phone call has been already closed
		return;
	}

	if (_activePhoneCall == phoneCall) {
		_activePhoneCall = NULL;
	}

	//Deletes the PhoneCall that is closed now
	//delete phoneCall;

	//History: update the duration of the memento associated to this phonecall
	_userProfile.getHistory().updateCallDuration(callId,
	 	phoneCall->getDuration());

	//Removes it from the list of PhoneCall
	_phoneCallMap.erase(callId);

	phoneCallClosedEvent(*this, *phoneCall);
}

void PhoneLine::holdCallsExcept(int callId) {
	for (PhoneCalls::iterator it = _phoneCallMap.begin(); it != _phoneCallMap.end(); ++it) {
		PhoneCall * phoneCall = (*it).second;
		if (phoneCall) {
			if (phoneCall->getCallId() != callId &&
				!phoneCall->getConferenceCall()) {

				phoneCall->hold();
			}
		}
	}
}

void PhoneLine::holdAllCalls() {
	holdCallsExcept(SipWrapper::CallIdError);
}

void PhoneLine::setState(EnumPhoneLineState::PhoneLineState state) {
	LOG_DEBUG("PhoneLineState=" + EnumPhoneLineState::toString(state));

	for (unsigned i = 0; i < _phoneLineStateList.size(); i++) {
		PhoneLineState * phoneLineState = _phoneLineStateList[i];
		if (phoneLineState->getCode() == state) {
			if (_state->getCode() != phoneLineState->getCode()) {
				_state = phoneLineState;
				_state->execute(*this);
				
				if (state == EnumPhoneLineState::PhoneLineStateAuthenticationError) {
					return;
				}
				
				LOG_DEBUG("line state changed lineId=" +
					String::fromNumber(_lineId) + " state=" +
					EnumPhoneLineState::toString(_state->getCode()));
				stateChangedEvent(*this, state);
				return;
			} else {
				//Stay in the same state
				return;
			}
		}
	}

	LOG_FATAL("unknown PhoneLineState=" + String::fromNumber(state));
}

PhoneCall * PhoneLine::getPhoneCall(int callId) {
	return _phoneCallMap[callId];
}

IPhoneLine::PhoneCallList PhoneLine::getPhoneCallList() const {
	PhoneCallList phoneCallList;

	for (PhoneCalls::const_iterator it = _phoneCallMap.begin(); it != _phoneCallMap.end(); ++it) {
		PhoneCall * phoneCall = (*it).second;
		if (phoneCall) {
			phoneCallList += phoneCall;
		}
	}
	return phoneCallList;
}

void PhoneLine::initSipWrapper() {
	configureSipWrapper();
	_sipWrapper->init();
}

void PhoneLine::configureSipWrapper() {
	//TODO check if Settings keys have changed
	Config & config = ConfigManager::getInstance().getCurrentConfig();

	//Setting plugin path
	string pluginPath = config.getCodecPluginPath();
	_sipWrapper->setPluginPath(pluginPath);

	//Setting proxy
	string proxyServer = config.getNetworkProxyServer();
	if (!proxyServer.empty()) {
		unsigned proxyPort = config.getNetworkProxyPort();
		string proxyLogin = config.getNetworkProxyLogin();
		string proxyPassword = config.getNetworkProxyPassword();
		_sipWrapper->setProxy(proxyServer, proxyPort, 
			proxyLogin, proxyPassword);
	}

	//Setting HTTP tunnel
	if (_sipAccount.isHttpTunnelNeeded()) {
		if (_sipAccount.httpTunnelHasSSL()) {
			_sipWrapper->setTunnel(_sipAccount.getHttpsTunnelServerHostname(),
				_sipAccount.getHttpsTunnelServerPort(), _sipAccount.httpTunnelHasSSL());
		} else {
			_sipWrapper->setTunnel(_sipAccount.getHttpTunnelServerHostname(),
				_sipAccount.getHttpTunnelServerPort(), _sipAccount.httpTunnelHasSSL());
		}
	}

	//Setting SIP proxy
	_sipWrapper->setSIP(_sipAccount.getSIPProxyServerHostname(),
		_sipAccount.getSIPProxyServerPort(),
		_sipAccount.getLocalSIPPort());

	//Setting NAT
	string natType = config.getNetworkNatType();
	_sipWrapper->setNatType(EnumNatType::toNatType(natType));

	//Setting video quality
	_sipWrapper->setVideoQuality(EnumVideoQuality::toVideoQuality(
		config.getVideoQuality()));

	//Sets audio codec preferences
	_sipWrapper->setAudioCodecList(config.getAudioCodecList());

	//Setting audio devices
	_sipWrapper->setCallOutputAudioDevice(AudioDevice(config.getAudioOutputDeviceId()));
	_sipWrapper->setCallInputAudioDevice(AudioDevice(config.getAudioInputDeviceId()));
	_sipWrapper->setRingerOutputAudioDevice(AudioDevice(config.getAudioRingerDeviceId()));

	//AEC + half duplex
	_sipWrapper->enableAEC(config.getAudioAEC());
	_sipWrapper->enableHalfDuplex(config.getAudioHalfDuplex());

	// Presence & IM (SIP/SIMPLE)
	_sipWrapper->enablePIM(_sipAccount.isPIMEnabled());

	// Configure call encryption
	setCallsEncryption(config.getCallEncryptionMode());
}

bool PhoneLine::isConnected() const {
	return (_state->getCode() == EnumPhoneLineState::PhoneLineStateOk);
}

void PhoneLine::flipVideoImage(bool flip) {
	_sipWrapper->flipVideoImage(flip);
}

void PhoneLine::setCallsEncryption(bool enable) {
	_sipWrapper->setCallsEncryption(enable);
}

bool PhoneLine::isCallEncrypted(int callId) {
	return _sipWrapper->isCallEncrypted(callId);
}

bool PhoneLine::hasPendingCalls() const {
	return (_activePhoneCall != NULL);
}

void PhoneLine::closePendingCalls() {
	if (_activePhoneCall) {
		_activePhoneCall->close();
	}
}
