/*
 * 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 "p2pp/node/rmanager.h"
#include "p2pp/node/peer.h"
#include "p2pp/p2pconfig/p2pconfig.h"
#include "p2pp/bigint/bigint.h"

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

#include "p2pp/p2pconfig/p2pconfig.h"

#include "p2pp/msg/msgheader.h"
#include "p2pp/net/socketi.h"

#include "p2pp/common/trace.h"

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

#include "p2pp/distance/kaddist.h"
#include "p2pp/distance/bamboodist.h"
#include "p2pp/sys/sysutils.h"
#include "p2pp/sys/threadi.h"

#include <string>
using namespace std;

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;
int ctid;
P2PConfig cfg;
Transport transport;
////////////////////////////////////////////////////////////////
//              CALL BACK FUNCTIONS for LOOKUP AND INSERT
////////////////////////////////////////////////////////////////
void* OnDisconnect(void *pN, int respcode) {
	if(respcode==200) {
		delete gpNode;	
	}	
	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* 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;

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

  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;

  
  Node* pNode = (Node*)arg;

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

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

    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("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("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), eTCP, OnGetDiagnostics, NULL);
    }//end if
    

    
  }//end while

  return 0;

}//_CmdListener


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

void* Peerfound(void* pResult, int32 respcode){
	peerfound=true;
	  struct LObject* 	pobjLO = (LObject*)pResult;
	  list<resourceobject>::iterator i1;

	  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();
	  std::string sipAddr= "sip:" + pobjLO->strKey+ "@" + i1->stpeerinfo.staddr.addr[0];
	  ((PhoneLine*)(pobjLO->pApp))->makeCall(sipAddr, NULL);

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

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

	struct LObject* pobjLO= (LObject*)pResult;

	if(respcode==200){
		//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);
		}

	}else {
		//join failed
		((PhoneLine*)(pobjLO->pApp))->disconnect();
	}
	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;
				}

				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->LookupUser((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()){
    	  _sipAccount.setConnected(true);

		InitLogFile("log-p2pp.txt");
			
		    
		  char* pchCfg = "p2ppconfig.txt";
		  //configfile = pchCfg;

		  Peer* pPeer;
		  Client* pClient;

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

		  //set idlen
		  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);		  
		} 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);		  
		 } 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);
		  }



		  //1) Start the timer thread
		  //2) Start the listening sockets and threads
		  
		  int tid;
		  uint32 t;
		#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

		  /***********************
		  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);
		//}
	}

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

//////P2PP in progress
}


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();*/	
		_sipAccount.setP2PP(false);
		_sipAccount.setConnected(false);
	}
	//////P2PP in progress
}

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

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

	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 PhoneLine::setPhoneCallState(int callId, EnumPhoneCallState::PhoneCallState state, const SipAddress & sipAddress) {
	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);
		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:
		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);
		break;

	case EnumPhoneCallState::PhoneCallStateIncoming: {
		//Sends SIP code 180
		//TODO automatically??
		_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();
	}
}
