/* Copyright(C) 2007-2017 VoIPobjects (voipobjects.com)
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

#include "EP_Engine.h"
#include "../helper/HL_Log.h"
#include "../helper/HL_Exception.h"
#include <resip/stack/ExtensionHeader.hxx>
#include <resip/stack/Pidf.hxx>
#include <resip/stack/PlainContents.hxx>

#define LOG_SUBSYSTEM "engine"

#define CONFIG(X) mConfig->at(X)
#define CONFIG_EXISTS(X) mConfig->exists(X)

//#define MODIFY_VIA_BEHIND_NAT

// NAT decorator
class NATDecorator: public resip::MessageDecorator
{
protected:
    UserAgent&      mUserAgent;
    resip::SipMessage mMessage;

    resip::Data     mViaHost;
    unsigned short  mViaPort;

    resip::Data     mContactsHost;
    resip::Data     mContactsScheme;
    unsigned short  mContactsPort;

public:
    NATDecorator(UserAgent& endpoint);
    virtual ~NATDecorator();

    virtual void    decorateMessage(resip::SipMessage &msg, const resip::Tuple &source, const resip::Tuple &destination, const resip::Data& sigcompId);
    virtual void    rollbackMessage(resip::SipMessage& msg);
    virtual MessageDecorator* clone() const;
};


NATDecorator::NATDecorator(UserAgent& ua)
    :mUserAgent(ua), mViaPort(0), mContactsPort(0)
{
}

NATDecorator::~NATDecorator()
{
}

void NATDecorator::decorateMessage(resip::SipMessage &msg, const resip::Tuple &source, const resip::Tuple &destination, const resip::Data& sigcompId)
{
    // Make a copy to allow rollback
    mMessage = msg;

    std::stringstream dump;
    mMessage.encode(dump);
    //ICELogDebug(<< "Decorating message: \n" << dump.str());

    // Check From: header and find the account
    resip::NameAddr from;
    if (msg.isRequest())
        from = msg.header(resip::h_From);
    else
        from = msg.header(resip::h_To);

    PAccount account = mUserAgent.getAccount(from);
    if (!account)
    {
        ICELogDebug(<< "Bad from header " << from.uri().getAor().c_str() << ". Will skip it");
        return;
    }

    if (!account->mConfig->at(CONFIG_EXTERNALIP).asBool())
        return;

    if (!account->mExternalAddress.isEmpty())
    {
#ifdef MODIFY_VIA_BEHIND_NAT
        if (msg.header(resip::h_Vias).size() > 0)
        {
            resip::Via& via = msg.header(resip::h_Vias).front();
            mViaHost = via.sentHost();
            mViaPort = via.sentPort();

            via.sentHost() = resip::Data(account->mExternalAddress.ip());
            via.sentPort() = account->mExternalAddress.port();
        }
#endif

        if (msg.header(resip::h_Contacts).size() > 0)
        {
            resip::Uri& uri = msg.header(resip::h_Contacts).front().uri();
            mContactsHost = uri.host();
            mContactsPort = uri.port();
            mContactsScheme = uri.scheme();

            uri.host() = resip::Data(account->mExternalAddress.ip());
            uri.port() = account->mExternalAddress.port();
            if (account->mConfig->at(CONFIG_SIPS).asBool())
            {
                //uri.scheme() = "sips";
                //uri.param(resip::p_transport) = "tls";
            }

            //uri.scheme() = account->mConfig->at(CONFIG_SIPS).asBool() ? "sips" : "sip";
        }
    }
}

void NATDecorator::rollbackMessage(resip::SipMessage& msg)
{
    // Check From: header and find the account
    resip::NameAddr from = msg.header(resip::h_From);
    PAccount account = mUserAgent.getAccount(from);
    if (!account)
        return;

    if (!account->mExternalAddress.isEmpty())
    {
#ifdef MODIFY_VIA_BEHIND_NAT
        if (msg.header(resip::h_Vias).size() > 0)
        {
            resip::Via& via = msg.header(resip::h_Vias).front();
            if ((via.sentHost() == resip::Data(account->mExternalAddress.ip())) &&
                (via.sentPort() == account->mExternalAddress.port()))
            {
                via.sentHost() = mViaHost;
                via.sentPort() = mViaPort;
            }
        }
#endif

        if (msg.header(resip::h_Contacts).size() > 0)
        {
            resip::Uri& uri = msg.header(resip::h_Contacts).front().uri();
            if ((uri.host() == resip::Data(account->mExternalAddress.ip())) &&
                (uri.port() == account->mExternalAddress.port()))
            {
                uri.host() = mContactsHost;
                uri.port() = mContactsPort;
                //uri.scheme() = mContactsScheme;
            }
        }
    }
}

resip::MessageDecorator* NATDecorator::clone() const
{
    return new NATDecorator(mUserAgent);
}

Account::Account(PVariantMap config, UserAgent& agent)
    :mAgent(agent), mId(0), mConfig(config), mRegistrationState(RegistrationState::None),
    mRegistration(NULL)
{
    mProfile = std::make_shared<resip::UserProfile>(agent.mProfile);
    mId = Account::generateId();
    setup(*config);
}

Account::~Account()
{
}

void Account::setup(VariantMap &config)
{
    // Credentials

    if (!config.exists(CONFIG_USERNAME) || !config.exists(CONFIG_PASSWORD) || !config.exists(CONFIG_DOMAIN))
        throw Exception(ERR_NO_CREDENTIALS);

    mProfile->clearDigestCredentials();
    mProfile->setDigestCredential(resip::Data(config[CONFIG_DOMAIN].asStdString()),
                                  resip::Data(config[CONFIG_USERNAME].asStdString()),
                                  resip::Data(config[CONFIG_PASSWORD].asStdString()));
    ICELogInfo( << "Credentials are set to domain " << config[CONFIG_DOMAIN].asStdString() <<
               ", username to " << config[CONFIG_USERNAME].asStdString());

    // Proxy
    mProfile->unsetOutboundProxy();
    if (config.exists(CONFIG_PROXY))
    {
        if (!config[CONFIG_PROXY].asStdString().empty())
        {
            resip::Uri proxyAddr;
            proxyAddr.host() = resip::Data(config[CONFIG_PROXY].asStdString());
            proxyAddr.port() = 5060;
            if (config.exists(CONFIG_PROXYPORT))
            {
                if (config[CONFIG_PROXYPORT].asInt())
                    proxyAddr.port() = config[CONFIG_PROXYPORT].asInt();
            }
            if (config[CONFIG_SIPS].asBool())
                proxyAddr.param(resip::p_transport) = "tls";

            mProfile->setOutboundProxy(proxyAddr);
        }
    }

    // NAT decorator
    mProfile->setOutboundDecorator(std::make_shared<NATDecorator>(mAgent));

    // Rinstance
    if (config.exists(CONFIG_INSTANCE_ID))
    {
        if (!config[CONFIG_INSTANCE_ID].asStdString().empty())
            mProfile->setInstanceId(config[CONFIG_INSTANCE_ID].asStdString().c_str());
    }
    else
        mProfile->setInstanceId(resip::Data::Empty);

    if (config.exists(CONFIG_REGID))
        mProfile->setRegId(config[CONFIG_REGID].asInt());

    if (config.exists(CONFIG_RINSTANCE))
        mProfile->setRinstanceEnabled(config[CONFIG_RINSTANCE].asBool());
    else
        mProfile->setRinstanceEnabled(true);

    if (config.exists(CONFIG_RPORT))
        mProfile->setRportEnabled(config[CONFIG_RPORT].asBool());
    else
        mProfile->setRportEnabled(true);

    // From header
    resip::NameAddr from;
    if (config.exists(CONFIG_DISPLAYNAME))
        from.displayName() = resip::Data(config[CONFIG_DISPLAYNAME].asStdString().c_str());

    from.uri().scheme() = config[CONFIG_SIPS].asBool() ? "sips" : "sip";
    if (config[CONFIG_DOMAINPORT].asInt() != 0)
        from.uri().port() = config[CONFIG_DOMAINPORT].asInt();
    else
        from.uri().port();// = 5060;

    from.uri().user() = resip::Data(config[CONFIG_USERNAME].asStdString());
    from.uri().host() = resip::Data(config[CONFIG_DOMAIN].asStdString());

    mProfile->setDefaultFrom(from);

    if (!CONFIG_EXISTS(CONFIG_REGISTERDURATION))
        CONFIG(CONFIG_REGISTERDURATION) = UA_REGISTRATION_TIME;
}

int Account::id() const
{
    return mId;
}

void Account::start()
{
    ICELogInfo(<< "Starting account " << this->name());
    if (mRegistrationState != RegistrationState::None)
    {
        ICELogInfo(<< "Registration is active or in progress already.");
        return;
    }
    if (!mAgent.mDum)
    {
        ICELogInfo(<< "DUM is not started yet.");
        return;
    }

    // Create registration
    mRegistration = new ResipSession(*mAgent.mDum);
    auto regmessage = mAgent.mDum->makeRegistration(mProfile->getDefaultFrom(), mProfile, mConfig->at(CONFIG_REGISTERDURATION).asInt(), mRegistration);

    for (UserInfo::const_iterator iter = mUserInfo.begin(); iter != mUserInfo.end(); iter++)
        regmessage->header(resip::ExtensionHeader(iter->first.c_str())).push_back(resip::StringCategory(iter->second.c_str()));

    mRegistrationState = RegistrationState::Registering;

    // Send packet here
    mAgent.mDum->send(regmessage);

    // Check if STUN IP is required
    bool noStunServerIp = !CONFIG_EXISTS(CONFIG_STUNSERVER_IP);
    //bool hasStunServerName = !CONFIG(CONFIG_STUNSERVER_NAME).asStdString().empty();
    if (noStunServerIp)
    {
        ICELogInfo(<<"No STUN server name or IP is not specified. Has to resolve/discover STUN server IP.");
        mRefreshStunServerIpTimer.start(CONFIG(CONFIG_DNS_CACHE_TIME).asInt() * 1000);
        mRefreshStunServerIpTimer.isTimeToSend();
        queryStunServerIp();
    }
}

void Account::stop()
{
    // Close presence publication
    if (mPublication.isValid())
    {
        mPublication->end();
        mPublication = resip::ClientPublicationHandle();
    }

    // Close client subscriptions

    // Close registration
    if (mRegistrationHandle.isValid())
    {
        mRegistrationHandle->removeAll();
        mRegistrationHandle = resip::ClientRegistrationHandle();
    }
    else
        if (mRegistration)
        {
            mRegistration->end();
        }
    mRegistration = NULL;
    mRegistrationState = RegistrationState::None;
}

void Account::refresh()
{
    if (mRegistrationHandle.isValid())
    {
        mRegistrationState = RegistrationState::Registering;
        mRegistrationHandle->requestRefresh();
    }

    if (mPublication.isValid())
    {
        mPublication->refresh();
    }
}

bool Account::active()
{
    return mRegistrationState == RegistrationState::Registered ||
           mRegistrationState == RegistrationState::Registering ||
           mRegistrationState == RegistrationState::Reregistering;
}

std::string Account::name()
{
    return contact(SecureScheme::Nothing).uri().toString().c_str();
}

Account::RegistrationState Account::registrationState()
{
    return mRegistrationState;
}

void Account::publishPresence(bool online, const std::string& content, int seconds)
{
    if (online == mPresenceOnline && content == mPresenceContent)
        return;

    mPresenceOnline = online;
    mPresenceContent = content;

    resip::Pidf p;
    p.setEntity(contact(SecureScheme::Nothing).uri());
    p.setSimpleId(resip::Data(CONFIG(CONFIG_PRESENCE_ID).asStdString()));
    p.setSimpleStatus(online, resip::Data(content));

    if (mPublication.isValid())
        mPublication->update(&p);
    else
        mAgent.mDum->send(mAgent.mDum->makePublication(contact(SecureScheme::TlsOnly), mProfile, p, resip::Symbols::Presence, seconds));
}

void Account::stopPublish()
{
    if (mPublication.isValid())
        mPublication->end();
}

PClientObserver Account::observe(const std::string& target, const std::string& package, void* tag)
{
    // Add subscription functionality
    PClientObserver observer(new ClientObserver());
    observer->mSession = new ResipSession(*mAgent.mDum);
    observer->mSession->setRemoteAddress(target);
    observer->mSession->setTag(tag);
    observer->mSessionId = observer->mSession->sessionId();
    observer->mPeer = target;

    std::shared_ptr<resip::SipMessage> msg;
    int expires = DEFAULT_SUBSCRIPTION_TIME, refresh = DEFAULT_SUBSCRIPTION_REFRESHTIME;
    if (mConfig->exists(CONFIG_SUBSCRIPTION_TIME))
        expires = CONFIG(CONFIG_SUBSCRIPTION_TIME).asInt();
    if (mConfig->exists(CONFIG_SUBSCRIPTION_REFRESHTIME))
        refresh = CONFIG(CONFIG_SUBSCRIPTION_REFRESHTIME).asInt();

    msg = mAgent.mDum->makeSubscription(resip::NameAddr(resip::Data(target)), mProfile,
                                        resip::Data(package), expires, refresh, observer->mSession);
    msg->header(resip::h_Accepts) = mAgent.mDum->getMasterProfile()->getSupportedMimeTypes(resip::NOTIFY);

    mAgent.mClientObserverMap[observer->mSessionId] = observer;
    mAgent.mDum->send(msg);

    return observer;
}

/* Queues message to peer with specified mime type. Returns ID of message. */
int Account::sendMsg(const std::string& peer, const void* ptr, unsigned length, const std::string& mime, void* tag)
{
    ResipSession* s = new ResipSession(*mAgent.mDum);
    s->setUa(&mAgent);
    s->setTag(tag);
    s->setRemoteAddress(peer);

    // Find MIME type
    resip::Mime type;
    std::string::size_type p = mime.find('/');
    if (p != std::string::npos)
        type = resip::Mime(resip::Data(mime.substr(0, p)), resip::Data(mime.substr(p+1)));
    else
        type = resip::Mime(resip::Data(mime), resip::Data());

    resip::ClientPagerMessageHandle msgHandle = mAgent.mDum->makePagerMessage(resip::NameAddr(resip::Data(peer)), mProfile, s);
    unique_ptr<resip::Contents> contentPtr(new resip::PlainContents(resip::Data(std::string((const char*)ptr, length)),type));
    int result = s->sessionId();
    msgHandle->page(std::move(contentPtr));

    return result;
}

resip::NameAddr Account::contact(SecureScheme ss)
{
    resip::NameAddr result;
    switch (ss)
    {
    case SecureScheme::Nothing:
        break;
    case SecureScheme::SipsOnly:
        result.uri().scheme() = mConfig->at(CONFIG_SIPS).asBool() ? "sips" : "sip";
        break;
    case SecureScheme::SipsAndTls:
        result.uri().scheme() = mConfig->at(CONFIG_SIPS).asBool() ? "sips" : "sip";
        if (mConfig->at(CONFIG_SIPS).asBool())
            result.uri().param(resip::p_transport) = "tls";
        break;
    case SecureScheme::TlsOnly:
        if (mConfig->at(CONFIG_SIPS).asBool())
            result.uri().param(resip::p_transport) = "tls";
        break;
    }

    result.uri().user() = resip::Data(mConfig->at(CONFIG_USERNAME).asStdString());
    result.uri().host() = resip::Data(mConfig->at(CONFIG_DOMAIN).asStdString());
    result.uri().port() = mConfig->at(CONFIG_DOMAINPORT).asInt();

    return result;
}

void Account::queryStunServerIp()
{
    ICELogInfo(<<"Looking for STUN/TURN server IP");

    if (!mConfig->exists(CONFIG_STUNSERVER_NAME))
    {
        // Send request to find STUN or TURN service
        std::string target = std::string(mConfig->at(CONFIG_RELAY).asBool() ? "_turn" : "_stun") + "._udp." + mConfig->at(CONFIG_DOMAIN).asStdString();

        // Start lookup
        mAgent.mStack->getDnsStub().lookup<resip::RR_SRV>(resip::Data(target), this);
    }
    else
    {
        // Check if host name is ip already
        std::string server = mConfig->at(CONFIG_STUNSERVER_NAME).asStdString();
        if (ice::NetworkAddress::isIp(server))
        {
            mConfig->at(CONFIG_STUNSERVER_IP) = server;
            mRefreshStunServerIpTimer.stop();
        }
        else
            mAgent.mStack->getDnsStub().lookup<resip::RR_A>(resip::Data(server), this);
    }
}

void Account::prepareIceStack(Session *session, ice::AgentRole icerole)
{
    ice::ServerConfig config;
    ice::NetworkAddress addr;
    addr.setIp(mConfig->at(CONFIG_STUNSERVER_IP).asStdString());
    if (mConfig->at(CONFIG_STUNSERVER_PORT).asInt())
        addr.setPort(mConfig->at(CONFIG_STUNSERVER_PORT).asInt());
    else
        addr.setPort(3478);

    config.mServerList4.push_back(addr);
    config.mRelay = mConfig->at(CONFIG_RELAY).asBool();
    if (mConfig->exists(CONFIG_ICETIMEOUT))
        config.mTimeout = mConfig->at(CONFIG_ICETIMEOUT).asInt();

    config.mUsername = mConfig->at(CONFIG_ICEUSERNAME).asStdString();
    config.mPassword = mConfig->at(CONFIG_ICEPASSWORD).asStdString();

    config.mUseIPv4 = mAgent.config()[CONFIG_IPV4].asBool();
    config.mUseIPv6 = mAgent.config()[CONFIG_IPV6].asBool();
    //config.mDetectNetworkChange = true;
    //config.mNetworkCheckInterval = 5000;

    session->mIceStack = std::shared_ptr<ice::Stack>(ice::Stack::makeICEBox(config));
    session->mIceStack->setEventHandler(session, this);
    session->mIceStack->setRole(icerole);
}

void Account::process()
{
    if (mRefreshStunServerIpTimer.isTimeToSend())
        queryStunServerIp();
}

void Account::onSuccess(resip::ClientRegistrationHandle h, const resip::SipMessage &response)
{
    // Save registration handle
    mRegistrationHandle = h;

    // Copy user info to registration handle
    for (UserInfo::iterator iter = mUserInfo.begin(); iter != mUserInfo.end(); iter++)
        mRegistrationHandle->setCustomHeader(resip::Data(iter->first.c_str()), resip::Data(iter->second.c_str()));

    // Get the Via
    const resip::Via& via = response.header(resip::h_Vias).front();

    // Get the sent host
    const resip::Data& sentHost = via.sentHost();//response.header(h_Contacts).front().uri().host();

    // Get the sentPort
    int sentPort = via.sentPort();

    const resip::Data& sourceHost = response.getSource().toData(resip::UDP);
    int rport = 0;
    if (via.exists(resip::p_rport))
        rport = via.param(resip::p_rport).port();

    resip::Data received = "";
    if (via.exists(resip::p_received))
        received = via.param(resip::p_received);

    bool hostChanged = sentHost != received && received.size() > 0;
    bool portChanged = sentPort != rport && rport != 0;

    // Save external port and IP address
    if (received.size() > 0 /*&& mConfig->at(CONFIG_EXTERNALIP).asBool()*/)
    {
        mExternalAddress.setIp(received.c_str());
        mExternalAddress.setPort(rport ? rport : sentPort);

        // Add new external address to domain list
        if (mAgent.mStack)
            mAgent.mStack->addAlias(resip::Data(mExternalAddress.ip()), mExternalAddress.port());
        if (mAgent.mDum)
            mAgent.mDum->addDomain(resip::Data(mExternalAddress.ip()));
    }

    mUsedTransport = response.getReceivedTransportTuple().getType();
    //bool streamTransport = mUsedTransport == resip::TCP || mUsedTransport == resip::TLS;

    // Retry registration for stream based transport too
    if  ( (hostChanged || portChanged) && mRegistrationState == RegistrationState::Registering /*&& !streamTransport*/ && mConfig->at(CONFIG_EXTERNALIP).asBool())
    {
        //mRegistrationHandle->requestRefresh();
        // Unregister at first
        mRegistrationHandle->removeAll();
        mRegistrationState = RegistrationState::Reregistering;
        return;
    }

    // So here we registered ok
    mRegistrationState = RegistrationState::Registered;
    mAgent.onAccountStart(mAgent.getAccount(this));
}

void Account::onRemoved(resip::ClientRegistrationHandle h, const resip::SipMessage &response)
{
    // Check if this unregistering is a part of rport pr
    if (mRegistrationState == RegistrationState::Reregistering)
    {
        //if (/*this->mUseExternalIP && */response.getSource().getType() == resip::UDP)
        {
            resip::Uri hostport(contact(SecureScheme::TlsOnly).uri());
            hostport.host() = resip::Data(mExternalAddress.ip());
            hostport.port() = mExternalAddress.port();
            if (mUsedTransport != resip::UDP)
            {
                const char* transportName = nullptr;
                switch (mUsedTransport)
                {
                case resip::TCP: transportName = "tcp"; break;
                case resip::TLS: transportName = "tls"; break;
                }

                hostport.param(resip::p_transport) = resip::Data(transportName);
            }
            mProfile->setOverrideHostAndPort(hostport);
            //mProfile->setDefaultFrom(from);
        }
        mProfile->setRegId(mConfig->at(CONFIG_REGID).asInt());
        auto regmessage = mAgent.mDum->makeRegistration(mProfile->getDefaultFrom(), mProfile, UA_REGISTRATION_TIME);
        for (UserInfo::const_iterator iter = mUserInfo.begin(); iter != mUserInfo.end(); iter++)
            regmessage->header(resip::ExtensionHeader(iter->first.c_str())).push_back(resip::StringCategory(iter->second.c_str()));

        mAgent.mDum->send(regmessage);
        return;
    }
    else
    {
        mRegistration = NULL;
        mRegistrationState = RegistrationState::None;
        mRegistrationHandle = resip::ClientRegistrationHandle();
        mAgent.onAccountStop(mAgent.getAccount(this), response.header(resip::h_StatusLine).statusCode());
    }
}

void Account::onFailure(resip::ClientRegistrationHandle h, const resip::SipMessage& response)
{
    // Reset registration handle
    mRegistrationHandle = resip::ClientRegistrationHandle();
    mRegistrationState = RegistrationState::None;
    mRegistration = NULL;
    mAgent.onAccountStop(mAgent.getAccount(this), response.header(resip::h_StatusLine).statusCode());
}

void Account::onDnsResult(const resip::DNSResult<resip::DnsHostRecord>& result)
{
    if (result.status == 0)
    {
        resip::Data foundAddress = result.records.front().host();
        ICELogInfo( << "Success to resolve STUN/TURN address to " << foundAddress.c_str());
        mConfig->at(CONFIG_STUNSERVER_IP) = std::string(foundAddress.c_str());

        // Here the IP address of STUN/TURN server is found. If account is registered already - it means account is ready.
        if (mRegistrationState == RegistrationState::Registered)
            mAgent.onAccountStart(mAgent.getAccount(this));
    }
    else
    {
        ICELogError( << "Failed to resolve STUN or TURN server IP address.");
        if (mRegistrationState == RegistrationState::Registered)
        {
            int startCode = mConfig->at(CONFIG_STUNSERVER_NAME).asStdString().empty() ? 0 : 503;
            mAgent.onAccountStop(mAgent.getAccount(this), startCode);
        }
    }
}

void Account::onDnsResult(const resip::DNSResult<resip::DnsAAAARecord>&)
{

}

void Account::onDnsResult(const resip::DNSResult<resip::DnsSrvRecord>& result)
{
    if (result.status == 0)
    {
        // Find lowest priority
        int priority = 0x7FFFFFFF;
        for (size_t i=0; i<result.records.size(); i++)
            if (result.records[i].priority() < priority)
                priority = result.records[i].priority();

        size_t index = 0;
        int weight = 0;

        for (size_t i=0; i<result.records.size(); i++)
        {
            if (result.records[i].priority() == priority && result.records[i].weight() >= weight)
            {
                index = i;
                weight = result.records[i].weight();
            }
        }

        mConfig->at(CONFIG_STUNSERVER_PORT) = result.records[index].port();

        const char* host = result.records[index].target().c_str();

        ICELogInfo( << "Success to find STUN/TURN server on " << result.records[index].target().c_str() <<
                   ":" << (int)result.records[index].port());


        if (inet_addr(host) == INADDR_NONE)
        {
            // Try to resolve domain name now
            mAgent.mStack->getDnsStub().lookup<resip::RR_A>(result.records[index].target(), this);
            //mStack->getDnsStub().lookup<resip::RR_AAAA>(result.records[index].target(), this);
        }
        else
        {
            mConfig->at(CONFIG_STUNSERVER_IP) = std::string(host);
        }
    }
    else
    {
        ICELogError( << "Failed to find STUN or TURN service for specified domain.");
        //mAgent::shutdown();
    }

}

void Account::onDnsResult(const resip::DNSResult<resip::DnsNaptrRecord>&)
{

}

void Account::onDnsResult(const resip::DNSResult<resip::DnsCnameRecord>&)
{

}

bool Account::isResponsibleFor(const resip::NameAddr &addr)
{
    std::string user = addr.uri().user().c_str();
    std::string domain = addr.uri().host().c_str();
    int p = addr.uri().port();
    if (mConfig->at(CONFIG_USERNAME).asStdString() == user && mConfig->at(CONFIG_DOMAIN).asStdString() == domain)
    {
        // Check if ports are the same or port is not specified at all
        if (mConfig->exists(CONFIG_DOMAINPORT))
            return mConfig->at(CONFIG_DOMAINPORT).asInt() == p || !p;
        else
            return true;
    }
    else
        return false;
}

void Account::setUserInfo(const UserInfo &info)
{
    mUserInfo = info;
    if (mRegistrationHandle.isValid())
    {
        for (UserInfo::iterator iter = mUserInfo.begin(); iter != mUserInfo.end(); iter++)
            mRegistrationHandle->setCustomHeader(resip::Data(iter->first.c_str()), resip::Data(iter->second.c_str()));
    }
}

Account::UserInfo Account::getUserInfo() const
{
    return mUserInfo;
}

std::atomic_int Account::IdGenerator;
int Account::generateId()
{
    return ++IdGenerator;
}
