/*
 *  Copyright (C) 2004-2006 Savoir-Faire Linux inc.
 *  Author: Yan Morin <yan.morin@savoirfairelinux.com>
 *  Author : Laurielle Lea <laurielle.lea@savoirfairelinux.com>
 *                                                                              
 *  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., 675 Mass Ave, Cambridge, MA 02139, USA.
 */

#include <errno.h>
#include <time.h>
#include <cstdlib>
#include <iostream>
#include <fstream>

#include <sys/types.h> // mkdir(2)
#include <sys/stat.h>	// mkdir(2)

#include <cc++/socket.h>   // why do I need this here?
#include <ccrtp/channel.h> // why do I need this here?
#include <ccrtp/rtp.h>     // why do I need this here?
#include <cc++/file.h>

#include "manager.h"
#include "audio/audiolayer.h"
#include "audio/audiocodec.h"
#include "audio/tonelist.h"

#include "accountcreator.h" // create new account
#include "voIPLink.h"

#include "user_cfg.h"
#include "gui/guiframework.h"

#ifdef USE_ZEROCONF
#include "zeroconf/DNSService.h"
#include "zeroconf/DNSServiceTXTRecord.h"
#endif

#define fill_config_str(name, value) \
  (_config.addConfigTreeItem(section, Conf::ConfigTreeItem(std::string(name), std::string(value), type_str)))
#define fill_config_int(name, value) \
  (_config.addConfigTreeItem(section, Conf::ConfigTreeItem(std::string(name), std::string(value), type_int)))

ManagerImpl::ManagerImpl (void)
{
  // Init private variables 
  _hasZeroconf = false;
#ifdef USE_ZEROCONF
  _hasZeroconf = true;
  _DNSService = new DNSService();
#endif

  // setup
  _path = ""; 
  _exist = 0;
  _setupLoaded = false;
  _gui = 0;

  // sound
  _audiodriver = 0;
  _dtmfKey = 0;
  _spkr_volume = 0;  // Initialize after by init() -> initVolume()
  _mic_volume  = 0;  // Initialize after by init() -> initVolume()
  _mic_volume_before_mute = 0; 

  // Call
  _nbIncomingWaitingCall=0;
  _hasTriedToRegister = false;

  // initialize random generator for call id
  srand (time(NULL));

#ifdef TEST
  testAccountMap();
  loadAccountMap();
  testCallAccountMap();
  unloadAccountMap();
#endif

  // should be call before initConfigFile
  loadAccountMap();
}

// never call if we use only the singleton...
ManagerImpl::~ManagerImpl (void) 
{
  terminate();

#ifdef USE_ZEROCONF
  delete _DNSService; _DNSService = 0;
#endif

  _debug("%s stop correctly.\n", PROGNAME);
}

void 
ManagerImpl::init() 
{
  initVolume();

  if (_exist == 0) {
    _debug("Cannot create config file in your home directory\n");
  }

  initAudioDriver();
  selectAudioDriver();

  initAudioCodec();

  AudioLayer *audiolayer = getAudioDriver();
  if (audiolayer!=0) {
    unsigned int sampleRate = audiolayer->getSampleRate();

    _debugInit("Load Telephone Tone");
    std::string country = getConfigString(PREFERENCES, ZONE_TONE);
    _telephoneTone = new TelephoneTone(country, sampleRate);

    _debugInit("Loading DTMF key");
    _dtmfKey = new DTMF(sampleRate);
  }

  // initRegisterVoIP was here, but we doing it after the gui loaded... 
  // the stun detection is long, so it's a better idea to do it after getEvents
  initZeroconf();
}

void ManagerImpl::terminate()
{
  saveConfig();

  unloadAccountMap();

  _debug("Unload DTMF Key\n");
  delete _dtmfKey;

  _debug("Unload Audio Driver\n");
  delete _audiodriver; _audiodriver = 0;

  _debug("Unload Telephone Tone\n");
  delete _telephoneTone; _telephoneTone = 0;
}

bool
ManagerImpl::isCurrentCall(const CallID& callId) {
  ost::MutexLock m(_currentCallMutex);
  return (_currentCallId2 == callId ? true : false);
}

bool
ManagerImpl::hasCurrentCall() {
  ost::MutexLock m(_currentCallMutex);
  if ( _currentCallId2 != "") {
    return true;
  }
  return false;
}

const CallID& 
ManagerImpl::getCurrentCallId() {
  ost::MutexLock m(_currentCallMutex);
  return _currentCallId2;
}

void
ManagerImpl::switchCall(const CallID& id ) {
  ost::MutexLock m(_currentCallMutex);
  _currentCallId2 = id;
}


///////////////////////////////////////////////////////////////////////////////
// Management of events' IP-phone user
///////////////////////////////////////////////////////////////////////////////
/* Main Thread */ 
bool
ManagerImpl::outgoingCall(const std::string& accountid, const CallID& id, const std::string& to)
{
  if (!accountExists(accountid)) {
    _debug("! Manager Error: Outgoing Call: account doesn't exist\n");
    return false;
  }
  if (getAccountFromCall(id) != AccountNULL) {
    _debug("! Manager Error: Outgoing Call: call id already exists\n");
    return false;
  }
  if (hasCurrentCall()) {
    _debug("* Manager Info: there is currently a call, try to hold it\n");
    onHoldCall(getCurrentCallId());
  }
  _debug("- Manager Action: Adding Outgoing Call %s on account %s\n", id.data(), accountid.data());
  if ( getAccountLink(accountid)->newOutgoingCall(id, to) ) {
    associateCallToAccount( id, accountid );
    switchCall(id);
    return true;
  } else {
    _debug("! Manager Error: An error occur, the call was not created\n");
  }
  return false;
}

//THREAD=Main : for outgoing Call
bool
ManagerImpl::answerCall(const CallID& id)
{
  stopTone(false); 

  AccountID accountid = getAccountFromCall( id );
  if (accountid == AccountNULL) {
    _debug("Answering Call: Call doesn't exists\n");
    return false;
  }

  if (!getAccountLink(accountid)->answer(id)) {
    // error when receiving...
    removeCallAccount(id);
    return false;
  }

  // if it was waiting, it's waiting no more
  removeWaitingCall(id);
  switchCall(id);
  return true;
}

//THREAD=Main
bool 
ManagerImpl::sendTextMessage(const AccountID& accountId, const std::string& to, const std::string& message) 
{
  if (accountExists(accountId)) {
    return getAccountLink(accountId)->sendMessage(to, message);
  }
  return false;
}

//THREAD=Main
bool
ManagerImpl::hangupCall(const CallID& id)
{
  stopTone(true);

  AccountID accountid = getAccountFromCall( id );
  if (accountid == AccountNULL) {
    _debug("! Manager Hangup Call: Call doesn't exists\n");
    return false;
  }

  bool returnValue = getAccountLink(accountid)->hangup(id);
  removeCallAccount(id);
  switchCall("");

  return returnValue;
}

//THREAD=Main
bool
ManagerImpl::cancelCall (const CallID& id)
{
  stopTone(true);
  AccountID accountid = getAccountFromCall( id );
  if (accountid == AccountNULL) {
    _debug("! Manager Cancel Call: Call doesn't exists\n");
    return false;
  }

  bool returnValue = getAccountLink(accountid)->cancel(id);
  // it could be a waiting call?
  removeWaitingCall(id);
  removeCallAccount(id);
  switchCall("");
  
  return returnValue;
}

//THREAD=Main
bool
ManagerImpl::onHoldCall(const CallID& id)
{
  stopTone(true);
  AccountID accountid = getAccountFromCall( id );
  if (accountid == AccountNULL) {
    _debug("5 Manager On Hold Call: Account ID %s or callid %s desn't exists\n", accountid.c_str(), id.c_str());
    return false;
  }

  bool returnValue = getAccountLink(accountid)->onhold(id);
  removeWaitingCall(id);
  switchCall("");
  
  return returnValue;
}

//THREAD=Main
bool
ManagerImpl::offHoldCall(const CallID& id)
{
  stopTone(false);
  AccountID accountid = getAccountFromCall( id );
  if (accountid == AccountNULL) {
    _debug("5 Manager OffHold Call: Call doesn't exists\n");
    return false;
  }
  bool returnValue = getAccountLink(accountid)->offhold(id);
  switchCall(id);

  if (returnValue) {
    try {
      getAudioDriver()->startStream();
    } catch(...) {
      _debugException("! Manager Off hold could not start audio stream");
    }
  }
  return returnValue;
}

//THREAD=Main
bool
ManagerImpl::transferCall(const CallID& id, const std::string& to)
{
  stopTone(true);
  AccountID accountid = getAccountFromCall( id );
  if (accountid == AccountNULL) {
    _debug("! Manager Transfer Call: Call doesn't exists\n");
    return false;
  }
  bool returnValue = getAccountLink(accountid)->transfer(id, to);
  removeWaitingCall(id);
  removeCallAccount(id);
  switchCall("");

  return returnValue;
}

//THREAD=Main
void
ManagerImpl::mute() {
  _mic_volume_before_mute = _mic_volume;
  setMicVolume(0);
}

//THREAD=Main
void
ManagerImpl::unmute() {
  if ( _mic_volume == 0 ) {
    setMicVolume(_mic_volume_before_mute);
  }
}

//THREAD=Main : Call:Incoming
bool
ManagerImpl::refuseCall (const CallID& id)
{
  stopTone(true);
  AccountID accountid = getAccountFromCall( id );
  if (accountid == AccountNULL) {
    _debug("! Manager OffHold Call: Call doesn't exists\n");
    return false;
  }
  bool returnValue = getAccountLink(accountid)->refuse(id);
  // if the call was outgoing or established, we didn't refuse it
  // so the method did nothing
  if (returnValue) {
    removeWaitingCall(id);
    removeCallAccount(id);
    switchCall("");
  }
  return returnValue;
}

//THREAD=Main
bool
ManagerImpl::saveConfig (void)
{
  _debug("Saving Configuration...\n");
  setConfig(AUDIO, VOLUME_SPKR, getSpkrVolume());
  setConfig(AUDIO, VOLUME_MICRO, getMicVolume());

  _setupLoaded = _config.saveConfigTree(_path.data());
  return _setupLoaded;
}

//THREAD=Main
bool
ManagerImpl::initRegisterVoIPLink() 
{
  _debugInit("Initiate VoIP Links Registration");
  AccountMap::iterator iter = _accountMap.begin();
  while( iter != _accountMap.end() ) {
    if ( iter->second) {
      iter->second->loadConfig();
      if ( iter->second->shouldInitOnStart() ) {
        if ( iter->second->init() && iter->second->shouldRegisterOnStart()) {
            iter->second->registerAccount();
        }
        // init only the first account
        break;
      }
    }
    iter++;
  }
  return true;
}

//THREAD=Main
bool
ManagerImpl::registerVoIPLink(const AccountID& accountId)
{
  _debug("Register VoIP Link\n");
  int returnValue = false;
  // right now, we don't support two SIP account
  // so we close everything before registring a new account
  Account* account = getAccount(accountId);
  if (account != 0) {
    AccountMap::iterator iter = _accountMap.begin();
    while ( iter != _accountMap.end() ) {
      if ( iter->second ) {
        iter->second->unregisterAccount();
        iter->second->terminate();
      }
      iter++;
    }
    returnValue = account->registerAccount();
  }
  return returnValue;
}

//THREAD=Main
bool 
ManagerImpl::unregisterVoIPLink(const AccountID& accountId)
{
  _debug("Unregister VoIP Link\n");
  int returnValue = false;
  if (accountExists( accountId ) ) {
    returnValue = getAccount(accountId)->unregisterAccount();
  }
  return returnValue;
}

//THREAD=Main
bool 
ManagerImpl::sendDtmf(const CallID& id, char code)
{
  AccountID accountid = getAccountFromCall( id );
  if (accountid == AccountNULL) {
    _debug("Send DTMF: call doesn't exists\n");
    return false;
  }

  int sendType = getConfigInt(SIGNALISATION, SEND_DTMF_AS);
  bool returnValue = false;
  switch (sendType) {
  case 0: // SIP INFO
    playDtmf(code);
    returnValue = getAccountLink(accountid)->carryingDTMFdigits(id, code);
    break;

  case 1: // Audio way
    break;
  case 2: // rfc 2833
    break;
  default: // unknown - error config?
    break;
  }
  return returnValue;
}

//THREAD=Main | VoIPLink
bool
ManagerImpl::playDtmf(char code)
{
  // HERE are the variable:
  // - boolean variable to play or not (config)
  // - length in milliseconds to play
  // - sample of audiolayer
  stopTone(false);
  int hasToPlayTone = getConfigInt(SIGNALISATION, PLAY_DTMF);
  if (!hasToPlayTone) return false;

  // length in milliseconds
  int pulselen = getConfigInt(SIGNALISATION, PULSE_LENGTH);
  if (!pulselen) { return false; }

  // numbers of int = length in milliseconds / 1000 (number of seconds)
  //                = number of seconds * SAMPLING_RATE by SECONDS
  AudioLayer* audiolayer = getAudioDriver();

  // fast return, no sound, so no dtmf
  if (audiolayer==0 || _dtmfKey == 0) { return false; }
  // number of data sampling in one pulselen depends on samplerate
  // size (n sampling) = time_ms * sampling/s 
  //                     ---------------------
  //                            ms/s
  int size = (int)(pulselen * ((float)audiolayer->getSampleRate()/1000));

  // this buffer is for mono
  // TODO <-- this should be global and hide if same size
  SFLDataFormat* _buf = new SFLDataFormat[size];
  bool returnValue = false;

  // Handle dtmf
  _dtmfKey->startTone(code);

  // copy the sound
  if ( _dtmfKey->generateDTMF(_buf, size) ) {

    // Put buffer to urgentRingBuffer 
    // put the size in bytes...
    // so size * 1 channel (mono) * sizeof (bytes for the data)
    audiolayer->putUrgent(_buf, size * sizeof(SFLDataFormat));

    try {
      // We activate the stream if it's not active yet.
      if (!audiolayer->isStreamActive()) {
        audiolayer->startStream();
      } else {
        audiolayer->sleep(pulselen); // in milliseconds
      }
    } catch(...) {
      _debugException("Portaudio exception when playing a dtmf");
    }
    returnValue = true;
  }

  // TODO: add caching
  delete[] _buf; _buf = 0;
  return returnValue;
}



// Multi-thread 
bool
ManagerImpl::incomingCallWaiting() {
  ost::MutexLock m(_waitingCallMutex);
  return (_nbIncomingWaitingCall > 0) ? true : false;
}

void
ManagerImpl::addWaitingCall(const CallID& id) {
  ost::MutexLock m(_waitingCallMutex);
  _waitingCall.insert(id);
  _nbIncomingWaitingCall++;
}

void
ManagerImpl::removeWaitingCall(const CallID& id) {
  ost::MutexLock m(_waitingCallMutex);
  // should return more than 1 if it erase a call
  if (_waitingCall.erase(id)) {
    _nbIncomingWaitingCall--;
  }
}

bool
ManagerImpl::isWaitingCall(const CallID& id) {
  ost::MutexLock m(_waitingCallMutex);
  CallIDSet::iterator iter = _waitingCall.find(id);
  if (iter != _waitingCall.end()) {
    return false;
  }
  return true;
}



///////////////////////////////////////////////////////////////////////////////
// Management of event peer IP-phone 
////////////////////////////////////////////////////////////////////////////////
// SipEvent Thread 
bool 
ManagerImpl::incomingCall(Call* call, const AccountID& accountId) 
{
  _debug("Incoming call\n");
  associateCallToAccount(call->getCallId(), accountId);
  if ( !hasCurrentCall() ) {
    call->setConnectionState(Call::Ringing);
    ringtone();
    switchCall(call->getCallId());
  } else {
     addWaitingCall(call->getCallId());
  }

  std::string from = call->getPeerName();
  std::string number = call->getPeerNumber();

  if ( !number.empty() ) {
    from.append(" <");
    from.append(number);
    from.append(">");
  }
  _gui->incomingCall(accountId, call->getCallId(), from);

  return true;
}

//THREAD=VoIP
void
ManagerImpl::incomingMessage(const AccountID& accountId, const std::string& message) {
  if (_gui) {
    _gui->incomingMessage(accountId, message);
  }
}

//THREAD=VoIP CALL=Outgoing
void
ManagerImpl::peerAnsweredCall(const CallID& id)
{
  if (isCurrentCall(id)) {
    stopTone(false);
  }
  if (_gui) _gui->peerAnsweredCall(id);
}

//THREAD=VoIP Call=Outgoing
void
ManagerImpl::peerRingingCall(const CallID& id)
{
  if (isCurrentCall(id)) {
    ringback();
  }
  if (_gui) _gui->peerRingingCall(id);
}

//THREAD=VoIP Call=Outgoing/Ingoing
void
ManagerImpl::peerHungupCall(const CallID& id)
{
  AccountID accountid = getAccountFromCall( id );
  if (accountid == AccountNULL) {
    _debug("peerHungupCall: Call doesn't exists\n");
    return;
  }
  if (isCurrentCall(id)) {
    stopTone(true);
    switchCall("");
  }
  removeWaitingCall(id);
  removeCallAccount(id);
  if (_gui) _gui->peerHungupCall(id);
}

//THREAD=VoIP
void
ManagerImpl::callBusy(const CallID& id) {
  _debug("Call busy\n");
  if (isCurrentCall(id) ) {
    playATone(Tone::TONE_BUSY);
    switchCall("");
  }
  removeCallAccount(id);
  removeWaitingCall(id);
  if(_gui) _gui->displayErrorText( id, "Call is busy");
}

//THREAD=VoIP
void
ManagerImpl::callFailure(const CallID& id) 
{
  _debug("Call failed\n");
  if (isCurrentCall(id) ) {
    playATone(Tone::TONE_BUSY);
    switchCall("");
  }
  if (_gui) {
    _gui->callFailure(id);
  }
  removeCallAccount(id);
  removeWaitingCall(id);
}

//THREAD=VoIP
void 
ManagerImpl::displayTextMessage(const CallID& id, const std::string& message)
{
  if(_gui) {
   _gui->displayTextMessage(id, message);
  }
}

//THREAD=VoIP
void 
ManagerImpl::displayErrorText(const CallID& id, const std::string& message)
{
  if(_gui) {
    _gui->displayErrorText(id, message);
  } else {
    std::cerr << message << std::endl;
  }
}

//THREAD=VoIP
void 
ManagerImpl::displayError (const std::string& error)
{
  if(_gui) {
    _gui->displayError(error);
  }
}

//THREAD=VoIP
void 
ManagerImpl::displayStatus(const std::string& status)
{
  if(_gui) {
    _gui->displayStatus(status);
  }
}

//THREAD=VoIP
void 
ManagerImpl::displayConfigError (const std::string& message)
{
  if(_gui) {
    _gui->displayConfigError(message);
  }
}

//THREAD=VoIP
void
ManagerImpl::startVoiceMessageNotification(const AccountID& accountId, const std::string& nb_msg)
{
  if (_gui) _gui->sendVoiceNbMessage(accountId, nb_msg);
}

//THREAD=VoIP
void
ManagerImpl::stopVoiceMessageNotification(const AccountID& accountId)
{
  if (_gui) _gui->sendVoiceNbMessage(accountId, std::string("0"));
} 

//THREAD=VoIP
void 
ManagerImpl::registrationSucceed(const AccountID& accountid)
{
  Account* acc = getAccount(accountid);
  if ( acc ) { 
    acc->setState(true); 
    if (_gui) _gui->sendRegistrationState(accountid, true);
  }
}

//THREAD=VoIP
void 
ManagerImpl::registrationFailed(const AccountID& accountid)
{
  Account* acc = getAccount(accountid);
  if ( acc ) { 
    acc->setState(false);
    if (_gui) _gui->sendRegistrationState(accountid, false);
  }
}

/**
 * Multi Thread
 */
bool 
ManagerImpl::playATone(Tone::TONEID toneId) {
  int hasToPlayTone = getConfigInt(SIGNALISATION, PLAY_TONES);
  if (!hasToPlayTone) return false;

  if (_telephoneTone != 0) {
    _toneMutex.enterMutex();
    _telephoneTone->setCurrentTone(toneId);
    _toneMutex.leaveMutex();

    try {
      AudioLayer* audiolayer = getAudioDriver();
      if (audiolayer) { audiolayer->startStream(); }
    } catch(...) {
      _debugException("Off hold could not start audio stream");
      return false;
    }
  }
  return true;
}

/**
 * Multi Thread
 */
void 
ManagerImpl::stopTone(bool stopAudio=true) {
  int hasToPlayTone = getConfigInt(SIGNALISATION, PLAY_TONES);
  if (!hasToPlayTone) return;

  if (stopAudio) {
    try {
      AudioLayer* audiolayer = getAudioDriver();
      if (audiolayer) { audiolayer->stopStream(); }
    } catch(...) {
      _debugException("Stop tone and stop stream");
    }
  }

  _toneMutex.enterMutex();
  if (_telephoneTone != 0) {
    _telephoneTone->setCurrentTone(Tone::TONE_NULL);
  }
  _toneMutex.leaveMutex();

  // for ringing tone..
  _toneMutex.enterMutex();
  _audiofile.stop();
  _toneMutex.leaveMutex();
}

/**
 * Multi Thread
 */
bool
ManagerImpl::playTone()
{
  //return playATone(Tone::TONE_DIALTONE);
  playATone(Tone::TONE_DIALTONE);
}

/**
 * Multi Thread
 */
void
ManagerImpl::congestion () {
  playATone(Tone::TONE_CONGESTION);
}

/**
 * Multi Thread
 */
void
ManagerImpl::ringback () {
  playATone(Tone::TONE_RINGTONE);
}

/**
 * Multi Thread
 */
void
ManagerImpl::ringtone() 
{
  int hasToPlayTone = getConfigInt(SIGNALISATION, PLAY_TONES);
  if (!hasToPlayTone) { return; }

  std::string ringchoice = getConfigString(AUDIO, RING_CHOICE);
  //if there is no / inside the path
  if ( ringchoice.find(DIR_SEPARATOR_CH) == std::string::npos ) {
    // check inside global share directory
    ringchoice = std::string(PROGSHAREDIR) + DIR_SEPARATOR_STR + RINGDIR + DIR_SEPARATOR_STR + ringchoice; 
  }

  AudioLayer* audiolayer = getAudioDriver();
  if (audiolayer==0) { return; }
  int sampleRate  = audiolayer->getSampleRate();

  _toneMutex.enterMutex(); 
  bool loadFile = _audiofile.loadFile(ringchoice, sampleRate);
  _toneMutex.leaveMutex(); 
  if (loadFile) {
    _toneMutex.enterMutex(); 
    _audiofile.start();
    _toneMutex.leaveMutex(); 
    try {
      audiolayer->startStream();
    } catch(...) {
      _debugException("Audio file couldn't start audio stream");
    }
  } else {
    ringback();
  }
}

AudioLoop*
ManagerImpl::getTelephoneTone()
{
  if(_telephoneTone != 0) {
    ost::MutexLock m(_toneMutex);
    return _telephoneTone->getCurrentTone();
  }
  else {
    return 0;
  }
}

AudioLoop*
ManagerImpl::getTelephoneFile()
{
  ost::MutexLock m(_toneMutex);
  if(_audiofile.isStarted()) {
    return &_audiofile;
  } else {
    return 0;
  }
}


/**
 * Use Urgent Buffer
 * By AudioRTP thread
 */
void
ManagerImpl::notificationIncomingCall(void) {

  AudioLayer* audiolayer = getAudioDriver();
  if (audiolayer != 0) {
    unsigned int samplerate = audiolayer->getSampleRate();
    std::ostringstream frequency;
    frequency << "440/" << FRAME_PER_BUFFER;

    Tone tone(frequency.str(), samplerate);
    unsigned int nbSampling = tone.getSize();
    SFLDataFormat buf[nbSampling];
    tone.getNext(buf, tone.getSize());
    audiolayer->putUrgent(buf, sizeof(SFLDataFormat)*nbSampling);
  }
}

/**
 * Multi Thread
 */
bool
ManagerImpl::getStunInfo (StunAddress4& stunSvrAddr, int port) 
{
  StunAddress4 mappedAddr;
  struct in_addr in;
  char* addr;

  //int fd3, fd4;
  // bool ok = stunOpenSocketPair(stunSvrAddr, &mappedAddr, &fd3, &fd4, port);
  int fd1 = stunOpenSocket(stunSvrAddr, &mappedAddr, port);
  bool ok = (fd1 == -1 || fd1 == INVALID_SOCKET) ? false : true;
  if (ok) {
    closesocket(fd1);
    //closesocket(fd3);
    //closesocket(fd4);
    _firewallPort = mappedAddr.port;
    // Convert ipv4 address to host byte ordering
    in.s_addr = ntohl (mappedAddr.addr);
    addr = inet_ntoa(in);
    _firewallAddr = std::string(addr);
    _debug("STUN Firewall: [%s:%d]\n", _firewallAddr.data(), _firewallPort);
    return true;
  } else {
    _debug("Opening a stun socket pair failed\n");
  }
  return false;
}

bool
ManagerImpl::behindNat(const std::string& svr, int port)
{
  StunAddress4 stunSvrAddr;
  stunSvrAddr.addr = 0;
  
  // Convert char* to StunAddress4 structure
  bool ret = stunParseServerName ((char*)svr.data(), stunSvrAddr);
  if (!ret) {
    _debug("SIP: Stun server address (%s) is not valid\n", svr.data());
    return 0;
  }
  
  // Firewall address
  //_debug("STUN server: %s\n", svr.data());
  return getStunInfo(stunSvrAddr, port);
}


///////////////////////////////////////////////////////////////////////////////
// Private functions
///////////////////////////////////////////////////////////////////////////////
/**
 * Initialization: Main Thread
 * @return 1: ok
          -1: error directory
           0: unable to load the setting
           2: file doesn't exist yet
 */
int
ManagerImpl::createSettingsPath (void) {
  _path = std::string(HOMEDIR) + DIR_SEPARATOR_STR + "." + PROGDIR;

  if (mkdir (_path.data(), 0755) != 0) {
    // If directory	creation failed
    if (errno != EEXIST) {
      _debug("Cannot create directory: %s\n", strerror(errno));
      return -1;
    }
  }

  // Load user's configuration
  _path = _path + DIR_SEPARATOR_STR + PROGNAME + "rc";
  return _config.populateFromFile(_path);
}

/**
 * Initialization: Main Thread
 */
void
ManagerImpl::initConfigFile (void) 
{
  std::string type_str("string");
  std::string type_int("int");

  std::string section;
  section = SIGNALISATION;
  fill_config_int(SYMMETRIC, YES_STR);
  fill_config_int(PLAY_DTMF, YES_STR);
  fill_config_int(PLAY_TONES, YES_STR);
  fill_config_int(PULSE_LENGTH, DFT_PULSE_LENGTH_STR);
  fill_config_int(SEND_DTMF_AS, SIP_INFO_STR);

  section = AUDIO;
  fill_config_int(DRIVER_NAME, DFT_DRIVER_STR);
  fill_config_int(DRIVER_NAME_IN, DFT_DRIVER_STR);
  fill_config_int(DRIVER_NAME_OUT, DFT_DRIVER_STR);
  fill_config_int(DRIVER_SAMPLE_RATE, DRIVER_SAMPLE_RATE_DEFAULT);
  fill_config_str(CODEC1, DFT_CODEC);
  fill_config_str(CODEC2, DFT_CODEC);
  fill_config_str(CODEC3, DFT_CODEC);
  fill_config_str(RING_CHOICE, DFT_RINGTONE);
  fill_config_int(VOLUME_SPKR, DFT_VOL_SPKR_STR);
  fill_config_int(VOLUME_MICRO, DFT_VOL_MICRO_STR);

  section = PREFERENCES;
  fill_config_str(SKIN_CHOICE, DFT_SKIN);
  fill_config_int(CONFIRM_QUIT, YES_STR);
  fill_config_str(ZONE_TONE, DFT_ZONE);
  fill_config_int(CHECKED_TRAY, NO_STR);
  fill_config_str(VOICEMAIL_NUM, DFT_VOICEMAIL);
  fill_config_int(CONFIG_ZEROCONF, CONFIG_ZEROCONF_DEFAULT_STR);

  initConfigAccount();
  
  _exist = createSettingsPath();
  _setupLoaded = (_exist == 2 ) ? false : true;
}

/**
 * Initialization: Main Thread
 */
void
ManagerImpl::initAudioCodec (void)
{
  _debugInit("Active Codecs");
  // TODO: need to be more dynamic...
  _codecDescriptorMap.setActive(getConfigString(AUDIO, CODEC1));
  _codecDescriptorMap.setActive(getConfigString(AUDIO, CODEC2));
  _codecDescriptorMap.setActive(getConfigString(AUDIO, CODEC3));
}


/**
 * Initialization: Main Thread
 */
void
ManagerImpl::initAudioDriver(void) 
{
  _debugInit("AudioLayer Creation");
  _audiodriver = new AudioLayer(this);
  if (_audiodriver == 0) {
    _debug("Init audio driver error\n");
  } else {
    std::string error = getAudioDriver()->getErrorMessage();
    if (!error.empty()) {
      _debug("Init audio driver: %s\n", error.c_str());
    }
  } 
}

/**
 * Initialization: Main Thread and gui
 */
void
ManagerImpl::selectAudioDriver (void)
{
  int noDevice  = getConfigInt(AUDIO, DRIVER_NAME);
  int noDeviceIn  = getConfigInt(AUDIO, DRIVER_NAME_IN);
  int noDeviceOut = getConfigInt(AUDIO, DRIVER_NAME_OUT);
  int sampleRate  = getConfigInt(AUDIO, DRIVER_SAMPLE_RATE);
  if (sampleRate <=0 || sampleRate > 48000) {
      sampleRate = 8000;
  }

  // this is when no audio device in/out are set
  // or the audio device in/out are set to 0
  // we take the nodevice instead
  if (noDeviceIn == 0 && noDeviceOut == 0) {
    noDeviceIn = noDeviceOut = noDevice;
  }
  _debugInit(" AudioLayer Opening Device");
  _audiodriver->setErrorMessage("");
  _audiodriver->openDevice(noDeviceIn, noDeviceOut, sampleRate);
}

/**
 * Initialize the Zeroconf scanning services loop
 * Informations will be store inside a map DNSService->_services
 * Initialization: Main Thread
 */
void 
ManagerImpl::initZeroconf(void) 
{
#ifdef USE_ZEROCONF
  _debugInit("Zeroconf Initialization");
  int useZeroconf = getConfigInt(PREFERENCES, CONFIG_ZEROCONF);

  if (useZeroconf) {
    _DNSService->startScanServices();
  }
#endif
}

/**
 * Init the volume for speakers/micro from 0 to 100 value
 * Initialization: Main Thread
 */
void
ManagerImpl::initVolume()
{
  _debugInit("Initiate Volume");
  setSpkrVolume(getConfigInt(AUDIO, VOLUME_SPKR));
  setMicVolume(getConfigInt(AUDIO, VOLUME_MICRO));
}

/**
 * configuration function requests
 * Main Thread
 */
bool 
ManagerImpl::getZeroconf(const std::string& sequenceId)
{
  bool returnValue = false;
#ifdef USE_ZEROCONF
  int useZeroconf = getConfigInt(PREFERENCES, CONFIG_ZEROCONF);
  if (useZeroconf && _gui != NULL) {
    TokenList arg;
    TokenList argTXT;
    std::string newService = "new service";
    std::string newTXT = "new txt record";
    if (!_DNSService->isStart()) { _DNSService->startScanServices(); }
    DNSServiceMap services = _DNSService->getServices();
    DNSServiceMap::iterator iter = services.begin();
    arg.push_back(newService);
    while(iter!=services.end()) {
      arg.push_front(iter->first);
      _gui->sendMessage("100",sequenceId,arg);
      arg.pop_front(); // remove the first, the name

      TXTRecordMap record = iter->second.getTXTRecords();
      TXTRecordMap::iterator iterTXT = record.begin();
      while(iterTXT!=record.end()) {
        argTXT.clear();
        argTXT.push_back(iter->first);
        argTXT.push_back(iterTXT->first);
        argTXT.push_back(iterTXT->second);
        argTXT.push_back(newTXT);
        _gui->sendMessage("101",sequenceId,argTXT);
        iterTXT++;
      }
      iter++;
    }
    returnValue = true;
  }
#else
  (void)sequenceId;
#endif
  return returnValue;
}

/**
 * Main Thread
 */
bool 
ManagerImpl::attachZeroconfEvents(const std::string& sequenceId, Pattern::Observer& observer)
{
  bool returnValue = false;
  // don't need the _gui like getZeroconf function
  // because Observer is here
#ifdef USE_ZEROCONF
  int useZeroconf = getConfigInt(PREFERENCES, CONFIG_ZEROCONF);
  if (useZeroconf) {
    if (!_DNSService->isStart()) { _DNSService->startScanServices(); }
    _DNSService->attach(observer);
    returnValue = true;
  }
#else
  (void)sequenceId;
  (void)observer;
#endif
  return returnValue;
}
bool
ManagerImpl::detachZeroconfEvents(Pattern::Observer& observer)
{
  bool returnValue = false;
#ifdef USE_ZEROCONF
  if (_DNSService) {
    _DNSService->detach(observer);
    returnValue = true;
  }
#else
  (void)observer;
#endif
  return returnValue;
}

/**
 * Main Thread
 */
bool
ManagerImpl::getEvents() {
  initRegisterVoIPLink();
  return true;
}

// TODO: rewrite this
/**
 * Main Thread
 */
bool 
ManagerImpl::getCallStatus(const std::string& sequenceId)
{
  if (!_gui) { return false; }
  ost::MutexLock m(_callAccountMapMutex);
  CallAccountMap::iterator iter = _callAccountMap.begin();
  TokenList tk;
  std::string code;
  std::string status;
  std::string destination;  
  std::string number;

  while (iter != _callAccountMap.end())
  {
    Call* call = getAccountLink(iter->second)->getCall(iter->first);
    Call::ConnectionState state = call->getConnectionState();
    if (state != Call::Connected) {
      switch(state) {
        case Call::Trying:       code="110"; status = "Trying";       break;
        case Call::Ringing:      code="111"; status = "Ringing";      break;
        case Call::Progressing:  code="125"; status = "Progressing";  break;
        case Call::Disconnected: code="125"; status = "Disconnected"; break;
        default: code=""; status= "";
      }
    } else {
      switch (call->getState()) {
        case Call::Active:       code="112"; status = "Established";  break;
        case Call::Hold:         code="114"; status = "Held";         break;
        case Call::Busy:         code="113"; status = "Busy";         break;
        case Call::Refused:      code="125"; status = "Refused";      break;
        case Call::Error:        code="125"; status = "Error";        break;
        case Call::Inactive:     code="125"; status = "Inactive";     break;
      }
    }

    // No Congestion
    // No Wrong Number
    // 116 <CSeq> <call-id> <acc> <destination> Busy
    destination = call->getPeerName();
    number = call->getPeerNumber();
    if (number!="") {
      destination.append(" <");
      destination.append(number);
      destination.append(">");
    }
    tk.push_back(iter->second);
    tk.push_back(destination);
    tk.push_back(status);
    _gui->sendCallMessage(code, sequenceId, iter->first, tk);
    tk.clear();

    iter++;
  }
  
  return true;
}

//THREAD=Main
bool 
ManagerImpl::getConfigAll(const std::string& sequenceId)
{
  bool returnValue = false;
  Conf::ConfigTreeIterator iter = _config.createIterator();
  TokenList tk = iter.begin();
  if (tk.size()) {
    returnValue = true;
  }
  while (tk.size()) {
    _gui->sendMessage("100", sequenceId, tk);
    tk = iter.next();
  }
  return returnValue;
}

//THREAD=Main
bool 
ManagerImpl::getConfig(const std::string& section, const std::string& name, TokenList& arg)
{
  return _config.getConfigTreeItemToken(section, name, arg);
}

//THREAD=Main
// throw an Conf::ConfigTreeItemException if not found
int 
ManagerImpl::getConfigInt(const std::string& section, const std::string& name)
{
  try {
    return _config.getConfigTreeItemIntValue(section, name);
  } catch (Conf::ConfigTreeItemException& e) {
    throw e;
  }
  return 0;
}

//THREAD=Main
std::string 
ManagerImpl::getConfigString(const std::string& section, const std::string&
name)
{
  try {
    return _config.getConfigTreeItemValue(section, name);
  } catch (Conf::ConfigTreeItemException& e) {
    throw e;
  }
  return "";
}

//THREAD=Main
bool 
ManagerImpl::setConfig(const std::string& section, const std::string& name, const std::string& value)
{
  return _config.setConfigTreeItem(section, name, value);
}

//THREAD=Main
bool 
ManagerImpl::setConfig(const std::string& section, const std::string& name, int value)
{
  std::ostringstream valueStream;
  valueStream << value;
  return _config.setConfigTreeItem(section, name, valueStream.str());
}

//THREAD=Main
bool 
ManagerImpl::getConfigList(const std::string& sequenceId, const std::string& name)
{
  bool returnValue = false;
  TokenList tk;
  if (name=="codecdescriptor") {

    CodecMap map = _codecDescriptorMap.getMap();
    CodecMap::iterator iter = map.begin();
    while( iter != map.end() ) {
      tk.clear();
      std::ostringstream strType;
      strType << iter->first;
      tk.push_back(strType.str());
      if (iter->second) {
        tk.push_back(iter->second->getOfficialName());
      } else {
        tk.push_back(strType.str());
      }
      _gui->sendMessage("100", sequenceId, tk);
      iter++;
    }
    returnValue = true;
  } else if (name=="ringtones") {
    // add empty line
    std::ostringstream str;
    str << 1;
    tk.push_back(str.str());
    tk.push_back(""); // filepath
    _gui->sendMessage("100", sequenceId, tk);

    // share directory
    std::string path = std::string(PROGSHAREDIR) + DIR_SEPARATOR_STR + RINGDIR;
    int nbFile = 1;
    returnValue = getDirListing(sequenceId, path, &nbFile);

    // home directory
    path = std::string(HOMEDIR) + DIR_SEPARATOR_STR + "." + PROGDIR + DIR_SEPARATOR_STR + RINGDIR;
    getDirListing(sequenceId, path, &nbFile);
  } else if (name=="audiodevice") {
    returnValue = getAudioDeviceList(sequenceId, AudioLayer::InputDevice | AudioLayer::OutputDevice);
  } else if (name=="audiodevicein") {
    returnValue = getAudioDeviceList(sequenceId, AudioLayer::InputDevice);
  } else if (name=="audiodeviceout") {
    returnValue = getAudioDeviceList(sequenceId, AudioLayer::OutputDevice);
  } else if (name=="countrytones") {
    returnValue = getCountryTones(sequenceId);
  }
  return returnValue;
}

//THREAD=Main
bool 
ManagerImpl::getAudioDeviceList(const std::string& sequenceId, int ioDeviceMask) 
{
  AudioLayer* audiolayer = getAudioDriver();
  if (audiolayer == 0) { return false; }

  bool returnValue = false;
  
  // TODO: test when there is an error on initializing...
  TokenList tk;
  AudioDevice* device = 0;
  int nbDevice = audiolayer->getDeviceCount();
  
  for (int index = 0; index < nbDevice; index++ ) {
    device = audiolayer->getAudioDeviceInfo(index, ioDeviceMask);
    if (device != 0) {
      tk.clear();
      std::ostringstream str; str << index; tk.push_back(str.str());
      tk.push_back(device->getName());
      tk.push_back(device->getApiName());
      std::ostringstream rate; rate << (int)(device->getRate()); tk.push_back(rate.str());
      _gui->sendMessage("100", sequenceId, tk);

      // don't forget to delete it after
      delete device; device = 0;
    }
  }
  returnValue = true;
  
  std::ostringstream rate; 
  rate << "VARIABLE";
  tk.clear();
  tk.push_back(rate.str());
  _gui->sendMessage("101", sequenceId, tk);

  return returnValue;
}

//THREAD=Main
bool
ManagerImpl::getCountryTones(const std::string& sequenceId)
{
  // see ToneGenerator for the list...
  sendCountryTone(sequenceId, 1, "North America");
  sendCountryTone(sequenceId, 2, "France");
  sendCountryTone(sequenceId, 3, "Australia");
  sendCountryTone(sequenceId, 4, "United Kingdom");
  sendCountryTone(sequenceId, 5, "Spain");
  sendCountryTone(sequenceId, 6, "Italy");
  sendCountryTone(sequenceId, 7, "Japan");

  return true;
}

//THREAD=Main
void 
ManagerImpl::sendCountryTone(const std::string& sequenceId, int index, const std::string& name) {
  TokenList tk;
  std::ostringstream str; str << index; tk.push_back(str.str());
  tk.push_back(name);
  _gui->sendMessage("100", sequenceId, tk);
}

//THREAD=Main
bool
ManagerImpl::getDirListing(const std::string& sequenceId, const std::string& path, int *nbFile) {
  TokenList tk;
  try {
    ost::Dir dir(path.c_str());
    const char *cFileName = 0;
    std::string fileName;
    std::string filePathName;
    while ( (cFileName=dir++) != 0 ) {
      fileName = cFileName;
      filePathName = path + DIR_SEPARATOR_STR + cFileName;
      if (fileName.length() && fileName[0]!='.' && !ost::isDir(filePathName.c_str())) {
        tk.clear();
        std::ostringstream str;
        str << (*nbFile);
        tk.push_back(str.str());
        tk.push_back(filePathName);
        _gui->sendMessage("100", sequenceId, tk);
        (*nbFile)++;
      }
    }
    return true;
  } catch (...) {
    // error to open file dir
    return false;
  }
}

bool 
ManagerImpl::getAccountList(const std::string& sequenceId) 
{
  bool oneActive = false;
  TokenList tk;

  AccountMap::iterator iter = _accountMap.begin();
  while ( iter != _accountMap.end() ) {
    if ( iter->second != 0 ) {
      _debug("Account List: %s\n", iter->first.data()); 
      tk.push_back(iter->first);
      // we try to send one active account for default account on start
      // this is not the way it should be... 
      if ( iter->second->isEnabled() || iter->second->shouldInitOnStart()) {
        tk.push_back("Active");
        tk.push_back(getConfigString(iter->first,CONFIG_ACCOUNT_ALIAS));
         _gui->sendMessage("130", sequenceId, tk);
        oneActive = true;
      } else {
        tk.push_back("Inactive");
        tk.push_back(getConfigString(iter->first,CONFIG_ACCOUNT_ALIAS));
         _gui->sendMessage("131", sequenceId, tk);
      }

     tk.clear();
    }
    iter++;
  }

  return oneActive;
}

//THREAD=Main
/*
 * Experimental...
 */
bool
ManagerImpl::setSwitch(const std::string& switchName, std::string& message) {
  AudioLayer* audiolayer = 0;
  if (switchName == "audiodriver" ) {
    // hangup all call here 
    audiolayer = getAudioDriver();

    int oldSampleRate = 0;
    if (audiolayer) { oldSampleRate = audiolayer->getSampleRate(); }

    selectAudioDriver();
    audiolayer = getAudioDriver();

    if (audiolayer) {
      std::string error = audiolayer->getErrorMessage();
      int newSampleRate = audiolayer->getSampleRate();

      if (!error.empty()) {
        message = error;
        return false;
      }

      if (newSampleRate != oldSampleRate) {
        _toneMutex.enterMutex();

        _debug("Unload Telephone Tone\n");
        delete _telephoneTone; _telephoneTone = 0;
        _debug("Unload DTMF Key\n");
        delete _dtmfKey; _dtmfKey = 0;

        _debug("Load Telephone Tone\n");
        std::string country = getConfigString(PREFERENCES, ZONE_TONE);
        _telephoneTone = new TelephoneTone(country, newSampleRate);

        _debugInit("Loading DTMF key");
        _dtmfKey = new DTMF(newSampleRate);

        _toneMutex.leaveMutex();
      }

      message = _("Change with success");
      playDtmf('9');
      getAudioDriver()->sleep(300); // in milliseconds
      playDtmf('1');
      getAudioDriver()->sleep(300); // in milliseconds
      playDtmf('1');
      return true;
    }
  } else if ( switchName == "echo" ) {
    audiolayer = getAudioDriver();
    if (audiolayer) {
      audiolayer->toggleEchoTesting();
      return true;
    }
  }


  return false;
}

// ACCOUNT handling
bool
ManagerImpl::associateCallToAccount(const CallID& callID, const AccountID& accountID)
{
  if (getAccountFromCall(callID) == AccountNULL) { // nothing with the same ID
    if (  accountExists(accountID)  ) { // account id exist in AccountMap
      ost::MutexLock m(_callAccountMapMutex);
      _callAccountMap[callID] = accountID;
      return true;
    } else {
      return false; 
    }
  } else {
    return false;
  }
}

AccountID
ManagerImpl::getAccountFromCall(const CallID& callID)
{
  ost::MutexLock m(_callAccountMapMutex);
  CallAccountMap::iterator iter = _callAccountMap.find(callID);
  if ( iter == _callAccountMap.end()) {
    return AccountNULL;
  } else {
    return iter->second;
  }
}

bool
ManagerImpl::removeCallAccount(const CallID& callID)
{
  ost::MutexLock m(_callAccountMapMutex);
  if ( _callAccountMap.erase(callID) ) {
    return true;
  }
  return false;
}

CallID 
ManagerImpl::getNewCallID() 
{
  std::ostringstream random_id("s");
  random_id << (unsigned)rand();
  
  // when it's not found, it return ""
  // generate, something like s10000s20000s4394040
  while (getAccountFromCall(random_id.str()) != AccountNULL) {
    random_id.clear();
    random_id << "s";
    random_id << (unsigned)rand();
  }
  return random_id.str();
}

short
ManagerImpl::loadAccountMap()
{
  _debugStart("Load account:");
  short nbAccount = 0;
  Account* tmpAccount;
  
  // SIP Loading X account...
  short nbAccountSIP = ACCOUNT_SIP_COUNT_DEFAULT;
  for (short iAccountSIP = 0; iAccountSIP<nbAccountSIP; iAccountSIP++) {
    std::ostringstream accountName;
    accountName << "SIP" << iAccountSIP;
    
    tmpAccount = AccountCreator::createAccount(AccountCreator::SIP_ACCOUNT, accountName.str());
     if (tmpAccount!=0) {
       _debugMid(" %s", accountName.str().data());
       _accountMap[accountName.str()] = tmpAccount;
      nbAccount++;
    }
  }

  // IAX Loading X account...
  short nbAccountIAX = ACCOUNT_IAX_COUNT_DEFAULT;
  for (short iAccountIAX = 0; iAccountIAX<nbAccountIAX; iAccountIAX++) {
    std::ostringstream accountName;
    accountName << "IAX" << iAccountIAX;
    tmpAccount = AccountCreator::createAccount(AccountCreator::IAX_ACCOUNT, accountName.str());
    if (tmpAccount!=0) {
       _debugMid(" %s", accountName.str().data());
       _accountMap[accountName.str()] = tmpAccount;
      nbAccount++;
    }
  }
  _debugEnd("\n");

  return nbAccount;
}

void
ManagerImpl::unloadAccountMap()
{
  _debug("Unloading account map...\n");
  AccountMap::iterator iter = _accountMap.begin();
  while ( iter != _accountMap.end() ) {
    _debug("-> Deleting account %s\n", iter->first.c_str());
    delete iter->second; iter->second = 0;
    iter++;
  }
  _accountMap.clear();
}

bool
ManagerImpl::accountExists(const AccountID& accountID)
{
  AccountMap::iterator iter = _accountMap.find(accountID);
  if ( iter == _accountMap.end() ) {
    return false;
  }
  return true;
}

Account*
ManagerImpl::getAccount(const AccountID& accountID)
{
  AccountMap::iterator iter = _accountMap.find(accountID);
  if ( iter == _accountMap.end() ) {
    return 0;
  }
  return iter->second;
}

VoIPLink* 
ManagerImpl::getAccountLink(const AccountID& accountID)
{
  Account* acc = getAccount(accountID);
  if ( acc ) {
    return acc->getVoIPLink();
  }
  return 0;
}

void
ManagerImpl::initConfigAccount() {
  AccountMap::iterator iter = _accountMap.begin();
  while ( iter != _accountMap.end() ) {
    if (iter->second!=0) {
      iter->second->initConfig(_config);
    }
    iter++;
  }
}

#ifdef TEST
/** 
 * Test accountMap
 */
bool ManagerImpl::testCallAccountMap()
{
  if ( getAccountFromCall(1) != AccountNULL ) {
    _debug("TEST: getAccountFromCall with empty list failed\n");
  }
  if ( removeCallAccount(1) != false ) {
    _debug("TEST: removeCallAccount with empty list failed\n");
  }
  CallID newid = getNewCallID();
  if ( associateCallToAccount(newid, "acc0") == false ) {
    _debug("TEST: associateCallToAccount with new CallID empty list failed\n");
  }
  if ( associateCallToAccount(newid, "acc1") == true ) {
    _debug("TEST: associateCallToAccount with a known CallID failed\n");
  }
  if ( getAccountFromCall( newid ) != "acc0" ) {
    _debug("TEST: getAccountFromCall don't return the good account id\n");
  }
  CallID secondnewid = getNewCallID();
  if ( associateCallToAccount(secondnewid, "xxxx") == true ) {
    _debug("TEST: associateCallToAccount with unknown account id failed\n");
  }
  if ( removeCallAccount( newid ) != true ) {
    _debug("TEST: removeCallAccount don't remove the association\n");
  }

  return true;
}

/**
 * Test AccountMap
 */
bool ManagerImpl::testAccountMap() 
{
  if (loadAccountMap() != 2) {
    _debug("TEST: loadAccountMap didn't load 2 account\n");
  }
  if (accountExists("acc0") == false) {
    _debug("TEST: accountExists didn't find acc0\n");
  }
  if (accountExists("accZ") != false) {
    _debug("TEST: accountExists found an unknown account\n");
  }
  if (getAccount("acc0") == 0) {
    _debug("TEST: getAccount didn't find acc0\n");
  }
  if (getAccount("accZ") != 0) {
    _debug("TEST: getAccount found an unknown account\n");
  }
  unloadAccountMap();
  if ( accountExists("acc0") == true ) {
    _debug("TEST: accountExists found an account after unloadAccount\n");
  }
  return true;
}
#endif