/**
 * engine.cpp
 * This file is part of the YATE Project http://YATE.null.ro
 *
 * Yet Another Signalling Stack - implements the support for SS7, ISDN and PSTN
 *
 * Yet Another Telephony Engine - a fully featured software PBX and IVR
 * Copyright (C) 2004-2014 Null Team
 *
 * This software is distributed under multiple licenses;
 * see the COPYING file in the main directory for licensing
 * information for this specific distribution.
 *
 * This use of this software may be subject to additional restrictions.
 * See the LEGAL file in the main directory for details.
 *
 * 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.
 */

#include "yatesig.h"
#include <yateversn.h>

#include <string.h>

// Maximum wait for a non-critical mutex acquisition
#ifndef MAX_LOCK_WAIT
#define MAX_LOCK_WAIT 10000
#endif

#define MIN_TICK_SLEEP 500
#define DEF_TICK_SLEEP 5000
#define MAX_TICK_SLEEP 50000

namespace TelEngine {

class SignallingThreadPrivate : public Thread
{
public:
    inline SignallingThreadPrivate(SignallingEngine* engine, const char* name, Priority prio)
	: Thread(name,prio), m_engine(engine)
	{ }
    virtual ~SignallingThreadPrivate();
    virtual void run();

private:
    SignallingEngine* m_engine;
};

};


using namespace TelEngine;

static ObjList s_factories;
static Mutex s_mutex(true,"SignallingFactory");

// Retrieve a value from a list
// Shift it if upper bits are set and mask is not set
// Mask it with a given mask
static inline unsigned char fixValue(const NamedList& list, const String& param,
    const TokenDict* dict, unsigned char mask, unsigned char upperMask, unsigned char shift)
{
    unsigned char val = (unsigned char)list.getIntValue(param,dict,0);
    if (0 != (val & upperMask) && 0 == (val & mask))
	val >>= shift;
    return val & mask;
}

SignallingFactory::SignallingFactory(bool fallback)
{
    s_mutex.lock();
    if (!s_factories.find(this)) {
	ObjList* l = fallback ? s_factories.append(this) : s_factories.insert(this);
	l->setDelete(false);
    }
    s_mutex.unlock();
}

SignallingFactory::~SignallingFactory()
{
    s_mutex.lock();
    s_factories.remove(this,false);
    s_mutex.unlock();
}

SignallingComponent* SignallingFactory::build(const String& type, NamedList* name)
{
    if (type.null())
	return 0;
    NamedList dummy(type);
    if (!name)
	name = &dummy;
    Lock lock(s_mutex);
    for (ObjList* l = &s_factories; l; l = l->next()) {
	SignallingFactory* f = static_cast<SignallingFactory*>(l->get());
	if (!f)
	    continue;
	DDebug(DebugAll,"Attempting to create a '%s' %s using factory %p",
	    name->c_str(),type.c_str(),f);
	SignallingComponent* obj = f->create(type,*name);
	if (obj)
	    return obj;
    }
    lock.drop();
    DDebug(DebugInfo,"Factory creating default '%s' named '%s'",type.c_str(),name->c_str());
    // now build some objects we know about
    if (type == YSTRING("SS7MTP2"))
	return new SS7MTP2(*name);
    else if (type == YSTRING("SS7M2PA"))
	return new SS7M2PA(*name);
    else if (type == YSTRING("SS7MTP3"))
	return new SS7MTP3(*name);
    else if (type == YSTRING("SS7Router"))
	return new SS7Router(*name);
    else if (type == YSTRING("SS7Management"))
	return new SS7Management(*name);
    else if (type == YSTRING("ISDNQ921"))
	return new ISDNQ921(*name,*name);
    else if (type == YSTRING("ISDNQ931"))
	return new ISDNQ931(*name,*name);
    else if (type == YSTRING("ISDNQ931Monitor"))
	return new ISDNQ931Monitor(*name,*name);
    Debug(DebugMild,"Factory could not create '%s' named '%s'",type.c_str(),name->c_str());
    return 0;
}

void* SignallingFactory::buildInternal(const String& type, NamedList* name)
{
    SignallingComponent* c = build(type,name);
    if (!c)
	return 0;
    void* raw = c->getObject(type);
    if (!raw)
	Debug(DebugFail,"Built component %p could not be casted back to type '%s'",c,type.c_str());
#ifdef DEBUG
    else
	Debug(DebugAll,"Built component %p type '%s' interface at %p",c,type.c_str(),raw);
#endif
    return raw;
}


SignallingComponent::SignallingComponent(const char* name, const NamedList* params, const char* type)
    : m_engine(0), m_compType(type)
{
    if (params) {
	name = params->getValue(YSTRING("debugname"),name);
	m_compType = params->getValue(YSTRING("type"),m_compType);
	debugLevel(params->getIntValue(YSTRING("debuglevel"),-1));
    }
    DDebug(engine(),DebugAll,"Component '%s' created [%p]",name,this);
    setName(name);
}

void SignallingComponent::setName(const char* name)
{
    debugName(0);
    m_name = name;
    debugName(m_name);
}

SignallingComponent::~SignallingComponent()
{
    DDebug(engine(),DebugAll,"Component '%s' deleted [%p]",toString().c_str(),this);
}

void SignallingComponent::destroyed()
{
    detach();
}

const String& SignallingComponent::toString() const
{
    return m_name;
}

bool SignallingComponent::initialize(const NamedList* config)
{
    return true;
}

bool SignallingComponent::resolveConfig(const String& cmpName, NamedList& params, const NamedList* config)
{
    if (!config)
	return false;
    String name = config->getValue(cmpName,params);
    if (!(name && !name.toBoolean(false)))
	return false;
    static_cast<String&>(params) = name;
    NamedString* param = config->getParam(params);
    NamedPointer* ptr = YOBJECT(NamedPointer,param);
    NamedList* ifConfig = ptr ? YOBJECT(NamedList,ptr->userData()) : 0;
    if (ifConfig)
	params.copyParams(*ifConfig);
    else {
	if (config->hasSubParams(params + "."))
	    params.copySubParams(*config,params + ".");
	else
	    params.addParam("local-config","true");
    }
    return true;
}

bool SignallingComponent::control(NamedList& params)
{
    return false;
}

NamedList* SignallingComponent::controlCreate(const char* oper)
{
    if (m_name.null())
	return 0;
    NamedList* params = new NamedList("chan.control");
    params->addParam("component",m_name);
    if (!TelEngine::null(oper))
	params->addParam("operation",oper);
    return params;
}

bool SignallingComponent::controlExecute(NamedList* params)
{
    bool ok = false;
    if (params) {
	ok = control(*params);
	TelEngine::destruct(params);
    }
    return ok;
}


void SignallingComponent::engine(SignallingEngine* eng)
{
    if (eng == m_engine)
	return;
    if (eng)
	eng->insert(this);
    else
	detach();
}

void SignallingComponent::insert(SignallingComponent* component)
{
    if (!component)
	return;
    if (m_engine) {
	// we have an engine - force the other component in the same
	m_engine->insert(component);
	return;
    }
    if (component->engine())
	// insert ourselves in the other's engine
	component->engine()->insert(this);
}

void SignallingComponent::detach()
{
    debugChain();
    if (m_engine) {
	m_engine->remove(this);
	m_engine = 0;
    }
}

void SignallingComponent::timerTick(const Time& when)
{
    XDebug(engine(),DebugAll,"Timer ticked for component '%s' [%p]",
	toString().c_str(),this);
}

unsigned long SignallingComponent::tickSleep(unsigned long usec) const
{
    return m_engine ? m_engine->tickSleep(usec) : 0;
}

void SignallingNotifier::notify(NamedList& notifs)
{
    DDebug(DebugInfo,"SignallingNotifier::notify() [%p] stub",this);
}

void SignallingNotifier::cleanup()
{
    DDebug(DebugInfo,"SignallingNotifier::cleanup() [%p] stub",this);
}


static SignallingEngine* s_self = 0;
long SignallingEngine::s_maxLockWait = MAX_LOCK_WAIT;

SignallingEngine::SignallingEngine(const char* name)
    : Mutex(true,"SignallingEngine"),
      m_thread(0),
      m_usecSleep(DEF_TICK_SLEEP), m_tickSleep(0)
{
    debugName(name);
}

SignallingEngine::~SignallingEngine()
{
    if (m_thread) {
	Debug(this,DebugGoOn,
	    "Engine destroyed with worker thread still running [%p]",this);
	stop();
    }
    lock();
    if (s_self == this)
	s_self = 0;
    unsigned int n = m_components.count();
    if (n)
	Debug(this,DebugNote,"Cleaning up %u components [%p]",n,this);
    m_components.clear();
    unlock();
}

SignallingEngine* SignallingEngine::self(bool create)
{
    if (create && !s_self) {
	// if mutex debugging is in force don't limit the lock time
	if (Lockable::wait())
	    s_maxLockWait = -1;
	s_self = new SignallingEngine;
    }
    return s_self;
}

SignallingComponent* SignallingEngine::find(const String& name)
{
    Lock mylock(this);
    return static_cast<SignallingComponent*>(m_components[name]);
}

SignallingComponent* SignallingEngine::find(const String& name, const String& type, const SignallingComponent* start)
{
    XDebug(this,DebugAll,"Engine finding '%s' of type %s from %p [%p]",
	name.c_str(),type.c_str(),start,this);
    Lock mylock(this);
    ObjList* l = m_components.skipNull();
    if (start) {
	l = m_components.find(start);
	if (!l)
	    return 0;
	l = l->skipNext();
    }
    for (; l; l = l->skipNext()) {
	SignallingComponent* c = static_cast<SignallingComponent*>(l->get());
	if ((name.null() || (c->toString() == name)) &&
	    (type.null() || c->getObject(type)))
	    return c;
    }
    return 0;
}

bool SignallingEngine::find(const SignallingComponent* component)
{
    if (!component)
	return false;
    Lock mylock(this);
    DDebug(this,DebugAll,"Engine finding component @%p [%p]",component,this);
    return m_components.find(component) != 0;
}

SignallingComponent* SignallingEngine::build(const String& type, NamedList& params, bool init, bool ref)
{
    XDebug(this,DebugAll,"Engine building '%s' of type %s [%p]",
	params.c_str(),type.c_str(),this);
    Lock mylock(this);
    SignallingComponent* c = find(params,type);
    if (c && (ref ? c->ref() : c->alive())) {
	DDebug(this,DebugAll,"Engine returning existing component '%s' @%p (%d) [%p]",
	    c->toString().c_str(),c,c->refcount(),this);
	return c;
    }
    c = SignallingFactory::build(type,&params);
    DDebug(this,DebugAll,"Created new component '%s' @%p [%p]",
	c->toString().c_str(),c,this);
    insert(c);
    if (init && c)
	c->initialize(&params);
    return c;
}

void SignallingEngine::insert(SignallingComponent* component)
{
    if (!component)
	return;
    Lock mylock(this);
    if (component->engine() == this)
	return;
#ifdef DEBUG
    const char* dupl = m_components.find(component->toString()) ? " (duplicate)" : "";
    Debug(this,DebugAll,"Engine inserting component '%s'%s @%p [%p]",
	component->toString().c_str(),dupl,component,this);
#endif
    component->detach();
    component->m_engine = this;
    component->debugChain(this);
    m_components.append(component);
}

void SignallingEngine::remove(SignallingComponent* component)
{
    if (!component)
	return;
    Lock mylock(this);
    if (component->engine() != this)
	return;
    DDebug(this,DebugAll,"Engine removing component @%p '%s' [%p]",
	component,component->toString().c_str(),this);
    m_components.remove(component,false);
    component->m_engine = 0;
    component->detach();
}

bool SignallingEngine::remove(const String& name)
{
    if (name.null())
	return false;
    Lock mylock(this);
    SignallingComponent* component = find(name);
    if (!component)
	return false;
    DDebug(this,DebugAll,"Engine removing component '%s' @%p [%p]",
	component->toString().c_str(),component,this);
    component->m_engine = 0;
    component->detach();
    m_components.remove(component);
    return true;
}

void SignallingEngine::notify(SignallingComponent* component, NamedList notifs)
{
    if (!(m_notifier && component))
	return;
    Debug(this,DebugAll,"Engine [%p] sending notify from '%s' [%p]",this,component->toString().c_str(),component);
    m_notifier->notify(notifs);
}

bool SignallingEngine::control(NamedList& params)
{
    bool ok = false;
    Lock mylock(this);
    for (ObjList* l = m_components.skipNull(); l; l = l->skipNext())
	ok = static_cast<SignallingComponent*>(l->get())->control(params) || ok;
    // Do not add operation-status here !!
    // The handler should return false because the message wasn't processed
    // by any component
    return ok;
}

bool SignallingEngine::start(const char* name, Thread::Priority prio, unsigned long usec)
{
    Lock mylock(this);
    if (m_thread)
	return m_thread->running();
    // defaults and sanity checks
    if (usec == 0)
	usec = DEF_TICK_SLEEP;
    else if (usec < MIN_TICK_SLEEP)
	usec = MIN_TICK_SLEEP;
    else if (usec > MAX_TICK_SLEEP)
	usec = MAX_TICK_SLEEP;

    SignallingThreadPrivate* tmp = new SignallingThreadPrivate(this,name,prio);
    if (tmp->startup()) {
	m_usecSleep = usec;
	m_thread = tmp;
	DDebug(this,DebugInfo,"Engine started worker thread [%p]",this);
	return true;
    }
    delete tmp;
    Debug(this,DebugGoOn,"Engine failed to start worker thread [%p]",this);
    return false;
}

void SignallingEngine::stop()
{
    // TODO: experimental: remove commented if it's working
    if (!m_thread)
	return;
    m_thread->cancel(false);
    while (m_thread)
	Thread::yield(true);
    Debug(this,DebugAll,"Engine stopped worker thread [%p]",this);
#if 0
    lock();
    SignallingThreadPrivate* tmp = m_thread;
    m_thread = 0;
    if (tmp) {
	delete tmp;
	DDebug(this,DebugInfo,"Engine stopped worker thread [%p]",this);
    }
    unlock();
#endif
}

Thread* SignallingEngine::thread() const
{
    return m_thread;
}

unsigned long SignallingEngine::tickSleep(unsigned long usec)
{
    if (m_tickSleep > usec)
	m_tickSleep = usec;
    return m_tickSleep;
}

unsigned long SignallingEngine::timerTick(const Time& when)
{
    RefPointer<SignallingComponent> c;
    lock();
    m_tickSleep = m_usecSleep;
    ListIterator iter(m_components);
    while (c = static_cast<SignallingComponent*>(iter.get())) {
	unlock();
	c->timerTick(when);
	c = 0;
	lock();
    }
    unsigned long rval = m_tickSleep;
    m_tickSleep = m_usecSleep;
    unlock();
    return rval;
}

void SignallingEngine::maxLockWait(long maxWait)
{
    if (maxWait < 0)
	maxWait = -1;
    else if (maxWait < MIN_TICK_SLEEP)
	maxWait = MIN_TICK_SLEEP;
    s_maxLockWait = maxWait;
}

SignallingThreadPrivate::~SignallingThreadPrivate()
{
    if (m_engine)
	m_engine->m_thread = 0;
}

void SignallingThreadPrivate::run()
{
    for (;;) {
	if (m_engine) {
	    Time t;
	    unsigned long sleepTime = m_engine->timerTick(t);
	    if (sleepTime) {
		usleep(sleepTime,true);
		continue;
	    }
	}
	yield(true);
    }
}


/*
 * SignallingTimer
 */
// Retrieve a timer interval from a list of parameters
unsigned int SignallingTimer::getInterval(const NamedList& params, const char* param,
    unsigned int minVal, unsigned int defVal, unsigned int maxVal, bool allowDisable)
{
    unsigned int val = (unsigned int)params.getIntValue(param,defVal);
    if (!val)
        return allowDisable ? 0 : minVal;
    if (val < minVal)
	return minVal;
    if (maxVal && val > maxVal)
	return maxVal;
    return val;
}


/**
 * SignallingUtils
 */

// Coding standard as defined in Q.931/Q.850
static const TokenDict s_dict_codingStandard[] = {
	{"CCITT",            0x00},
	{"ISO/IEC",          0x01},
	{"national",         0x02},
	{"network specific", 0x03},
	{0,0}
	};

// Locations as defined in Q.850
static const TokenDict s_dict_location[] = {
	{"U",    0x00},                  // User
	{"LPN",  0x01},                  // Private network serving the local user
	{"LN",   0x02},                  // Public network serving the local user
	{"TN",   0x03},                  // Transit network
	{"RLN",  0x04},                  // Public network serving the remote user
	{"RPN",  0x05},                  // Private network serving the remote user
	{"INTL", 0x07},                  // International network
	{"BI",   0x0a},                  // Network beyond the interworking point
	{0,0}
	};

// Q.850 2.2.5. Cause class: Bits 4-6
// Q.850 Table 1. Cause value: Bits 0-6
// Defined for CCITT coding standard
static const TokenDict s_dict_causeCCITT[] = {
	// normal-event class
	{"normal-event",                   0x00},
	{"unallocated",                    0x01}, // Unallocated (unassigned) number
	{"noroute-to-network",             0x02}, // No route to specified transit network
	{"noroute",                        0x03}, // No route to destination
	{"send-info-tone",                 0x04}, // Send special information tone
	{"misdialed-trunk-prefix",         0x05}, // Misdialed trunk prefix
	{"channel-unacceptable",           0x06}, // Channel unacceptable
	{"call-delivered",                 0x07}, // Call awarded and being delivered in an established channel
	{"preemption",                     0x08}, // Preemption
	{"preemption-circuit-reserved",    0x09}, // Preemption circuit reserved for re-use
	{"ported-number",                  0x0e}, // QoR: ported number Q.850 Addendum 1 (06/2000)
	{"excess-digits",                  0x0e}, // Excess digits received, call is proceeding
	{"normal-clearing",                0x10}, // Normal Clearing
	{"busy",                           0x11}, // User busy
	{"noresponse",                     0x12}, // No user responding
	{"noanswer",                       0x13}, // No answer from user (user alerted)
	{"offline",                        0x14}, // Subscriber absent
	{"rejected",                       0x15}, // Call Rejected
	{"moved",                          0x16}, // Number changed
	{"redirection",                    0x17}, // Redirection to new destination Q.850 05/98
	{"rejected-by-feature",            0x18}, // Call rejected due to feature at the destination Q.850 Amendment 1 (07/2001)
	{"looping",                        0x19}, // Exchange routing error (hop counter) Q.850 05/98
	{"answered",                       0x1a}, // Non-selected user clearing (answered elsewhere)
	{"out-of-order",                   0x1b}, // Destination out of order
	{"invalid-number",                 0x1c}, // Invalid number format
	{"facility-rejected",              0x1d}, // Facility rejected
	{"status-enquiry-rsp",             0x1e}, // Response to STATUS ENQUIRY
	{"normal",                         0x1f}, // Normal, unspecified
	// resource-unavailable class
	{"resource-unavailable",           0x20}, // Resource unavailable
	{"congestion",                     0x22}, // No circuit/channel available
	{"channel-congestion",             0x22},
	{"net-out-of-order",               0x26}, // Network out of order
	{"frame-mode-conn-down",           0x27}, // Permanent frame mode connection out of service
	{"frame-mode-conn-up",             0x28}, // Permanent frame mode connection operational
	{"noconn",                         0x29},
	{"temporary-failure",              0x29}, // Temporary failure
	{"congestion",                     0x2a}, // Switching equipment congestion
	{"switch-congestion",              0x2a},
	{"access-info-discarded",          0x2b}, // Access information discarded
	{"channel-unavailable",            0x2c}, // Requested channel not available
	{"preemption-congestion",          0x2e}, // Precedence call blocked
	{"noresource",                     0x2f}, // Resource unavailable, unspecified
	{"service-unavailable",            0x30}, // Service or option not available
	{"qos-unavailable",                0x31}, // Quality of service unavailable
	{"facility-not-subscribed",        0x32}, // Requested facility not subscribed
	{"forbidden-out",                  0x35}, // Outgoing call barred within CUG
	{"forbidden-in",                   0x37}, // Incoming call barred within CUG
	{"bearer-cap-not-auth",            0x39}, // Bearer capability not authorized
	{"bearer-cap-not-available",       0x3a}, // Bearer capability not presently available
	{"nomedia",                        0x3a},
	{"invalid-access-info-out",        0x3e}, // Inconsistency in designated outgoing access information and subscriber class
	{"service-unavailable",            0x3f}, // Service or option not available
	// service-not-implemented class
	{"bearer-cap-not-implemented",     0x41}, // Bearer capability not implemented
	{"channel-type-not-implemented",   0x42}, // Channel type not implemented
	{"facility-not-implemented",       0x45}, // Requested facility not implemented
	{"restrict-bearer-cap-avail",      0x46}, // Only restricted digital information bearer capability is available
	{"service-not-implemented",        0x4f}, // Service or option not implemented, unspecified
	// invalid-message class
	{"invalid-callref",                0x51}, // Invalid call reference value
	{"unknown-channel",                0x52}, // Identified channel does not exist
	{"unknown-callid",                 0x53}, // A suspended call exists, but this call identity does not
	{"duplicate-callid",               0x54}, // Call identity in use
	{"no-call-suspended",              0x55}, // No call suspended
	{"suspended-call-cleared",         0x56}, // Call having the requested call identity has been cleared
	{"not-subscribed",                 0x57}, // User not member of CUG
	{"incompatible-dest",              0x58}, // Incompatible destination
	{"unknown-group",                  0x5a}, // Non-existent CUG
	{"invalid-transit-net",            0x5b}, // Invalid transit network selection
	{"invalid-message",                0x5f}, // Invalid message, unspecified
	// protocol-error class
	{"missing-mandatory-ie",           0x60}, // Mandatory information element is missing
	{"unknown-message",                0x61}, // Message type non-existent or not implemented
	{"wrong-message",                  0x62}, // Message not compatible with call state, non-existent or not implemented
	{"unknown-ie",                     0x63}, // Information element non-existent or not implemented
	{"invalid-ie",                     0x64}, // Invalid information element contents
	{"wrong-state-message",            0x65}, // Message not compatible with call state
	{"timeout",                        0x66}, // Recovery on timer expiry
	{"unknown-param-passed-on",        0x67}, // Parameter non-existent or not implemented, passed on
	{"unknown-param-message-droppped", 0x6e}, // Message with unrecognized parameter, discarded
	{"protocol-error",                 0x6f}, // Protocol error, unspecified
	// interworking class
	{"interworking",                   0x7f}, // Interworking, unspecified
	{0,0}
	};

// Q.931 4.5.5. Information transfer capability: Bits 0-4
// Defined for CCITT coding standard
static const TokenDict s_dict_transferCapCCITT[] = {
	{"speech",       0x00},          // Speech
	{"udi",          0x08},          // Unrestricted digital information
	{"rdi",          0x09},          // Restricted digital information
	{"3.1khz-audio", 0x10},          // 3.1 khz audio
	{"udi-ta",       0x11},          // Unrestricted digital information with tone/announcements
	{"video",        0x18},          // Video
	{0,0}
	};

// Q.931 4.5.5. Transfer mode: Bits 5,6
// Defined for CCITT coding standard
static const TokenDict s_dict_transferModeCCITT[] = {
	{"circuit",      0x00},          // Circuit switch mode
	{"packet",       0x02},          // Packet mode
	{0,0}
	};

// Q.931 4.5.5. Transfer rate: Bits 0-4
// Defined for CCITT coding standard
static const TokenDict s_dict_transferRateCCITT[] = {
	{"packet",        0x00},         // Packet mode only
	{"64kbit",        0x10},         // 64 kbit/s
	{"2x64kbit",      0x11},         // 2x64 kbit/s
	{"384kbit",       0x13},         // 384 kbit/s
	{"1536kbit",      0x15},         // 1536 kbit/s
	{"1920kbit",      0x17},         // 1920 kbit/s
	{"multirate",     0x18},         // Multirate (64 kbit/s base rate)
	{0,0}
	};

// Q.931 4.5.5. User information Layer 1 protocol: Bits 0-4
// Defined for CCITT coding standard
static const TokenDict s_dict_formatCCITT[] = {
	{"v110",          0x01},         // Recomendation V.110 and X.30
	{"mulaw",         0x02},         // Recomendation G.711 mu-law
	{"alaw",          0x03},         // Recomendation G.711 A-law
	{"g721",          0x04},         // Recomendation G.721 32kbit/s ADPCM and I.460
	{"h221",          0x05},         // Recomendation H.221 and H.242
	{"h223",          0x06},         // Recomendation H.223 and H.245 videoconference
	{"non-CCITT",     0x07},         // Non CCITT standardized rate adaption
	{"v120",          0x08},         // Recomendation V.120
	{"x31",           0x09},         // Recomendation X.31 HDLC flag stuffing
	{0,0}
	};

const TokenDict* SignallingUtils::s_dictCCITT[5] = {
	s_dict_causeCCITT,
	s_dict_formatCCITT,
	s_dict_transferCapCCITT,
	s_dict_transferModeCCITT,
	s_dict_transferRateCCITT
	};

// Check if a comma separated list of flags has a given flag
bool SignallingUtils::hasFlag(const String& flags, const char* flag)
{
    ObjList* obj = flags.split(',',false);
    bool found = (obj->find(flag) != 0);
    TelEngine::destruct(obj);
    return found;
}

// Append a flag to a comma separated list of flags
bool SignallingUtils::appendFlag(String& flags, const char* flag)
{
    if (TelEngine::null(flag) || hasFlag(flags,flag))
	return false;
    flags.append(flag,",");
    return true;
}

// Remove a flag from a comma separated list of flags
bool SignallingUtils::removeFlag(String& flags, const char* flag)
{
    ObjList* obj = flags.split(',',false);
    ObjList* found = obj->find(flag);
    if (found) {
	obj->remove(found,true);
	flags = "";
	for (ObjList* o = obj->skipNull(); o; o = o->skipNext())
	    flags.append(*static_cast<String*>(o->get()),",");
    }
    TelEngine::destruct(obj);
    return (found != 0);
}

// Check if a list's parameter (comma separated list of flags) has a given flag
bool SignallingUtils::hasFlag(const NamedList& list, const char* param, const char* flag)
{
    const String* s = list.getParam(param);
    return s && hasFlag(*s,flag);
}

// Append a flag to a list parameter, create parameter if missing
bool SignallingUtils::appendFlag(NamedList& list, const char* param, const char* flag)
{
    String* s = list.getParam(param);
    if (s)
	return appendFlag(*s,flag);
    list.addParam(param,flag);
    return true;
}

// Add string (keyword) if found or integer parameter to a named list
void SignallingUtils::addKeyword(NamedList& list, const char* param, const TokenDict* tokens, unsigned int val)
{
    const char* value = lookup(val,tokens);
    if (value)
	list.addParam(param,value);
    else
	list.addParam(param,String(val));
}

// Dump a buffer to a list of parameters
void SignallingUtils::dumpData(const SignallingComponent* comp, NamedList& list, const char* param,
	const unsigned char* buf, unsigned int len, char sep)
{
    String raw;
    raw.hexify((void*)buf,len,sep);
    list.addParam(param,raw);
    DDebug(comp,DebugAll,"Utils::dumpData dumped %s='%s'",param,raw.safe());
}

// Dump data from a buffer to a list of parameters. The buffer is parsed until (and including)
//  the first byte with the extension bit (the most significant one) set
unsigned int SignallingUtils::dumpDataExt(const SignallingComponent* comp, NamedList& list, const char* param,
	const unsigned char* buf, unsigned int len, char sep)
{
    if (!(buf && len))
	return 0;
    unsigned int count = 0;
    for (; count < len && !(buf[count] & 0x80); count++) ;
    if (count == len) {
	Debug(comp,DebugMild,"Utils::dumpDataExt invalid ext bits for %s (len=%u)",param,len);
	return 0;
    }
    count++;
    dumpData(comp,list,param,buf,count,sep);
    return count;
}

// Decode a received buffer to a comma separated list of flags
bool SignallingUtils::decodeFlags(const SignallingComponent* comp, NamedList& list, const char* param,
	const SignallingFlags* flags, const unsigned char* buf, unsigned int len)
{
    if (!(flags && buf && len <= sizeof(unsigned int)))
	return false;
    unsigned int val = 0;
    int shift = 0;
    while (len--) {
	val |= ((unsigned int)*buf++) << shift;
	shift += 8;
    }
    String tmp;
    for (; flags->mask; flags++)
	if ((val & flags->mask) == flags->value)
	    tmp.append(flags->name,",");
    DDebug(comp,DebugAll,"Utils::decodeFlags. Decoded %s='%s' from %u",param,tmp.safe(),val);
    list.addParam(param,tmp);
    return true;
}

const TokenDict* SignallingUtils::codings()
{
    return s_dict_codingStandard;
}

const TokenDict* SignallingUtils::locations()
{
    return s_dict_location;
}

#define Q850_MAX_CAUSE 32

// Q.850 2.1
bool SignallingUtils::decodeCause(const SignallingComponent* comp, NamedList& list,
	const unsigned char* buf, unsigned int len, const char* prefix, bool isup)
{
    if (!buf)
	return false;
    if (len < 2) {
	Debug(comp,DebugNote,"Utils::decodeCause. Invalid length %u",len);
	return false;
    }
    String causeName = prefix;
    // Byte 0: Coding standard (bit 5,6), location (bit 0-3)
    unsigned char coding = (buf[0] & 0x60) >> 5;
    addKeyword(list,causeName + ".coding",codings(),coding);
    addKeyword(list,causeName + ".location",locations(),buf[0] & 0x0f);
    unsigned int crt = 1;
    // If bit 7 is 0, the next byte should contain the recomendation
    unsigned char rec = 0;
    if (!(buf[0] & 0x80)) {
	rec = buf[1] & 0x7f;
	// For ISUP there shouldn't be a recomendation byte
	if (isup)
	    Debug(comp,DebugMild,"Utils::decodeCause. Found recomendation %u for ISUP cause",rec);
	crt = 2;
    }
    if (rec)
	list.addParam(causeName + ".rec",String(rec));
    if (crt >= len) {
	Debug(comp,DebugMild,"Utils::decodeCause. Invalid length %u. Cause value is missing",len);
	list.addParam(causeName,"");
	return false;
    }
    // Current byte: bits 0..6: cause, bits 5,6: cause class
    addKeyword(list,causeName,dict(0,coding),buf[crt] & 0x7f);
    // Rest of data: diagnostic
    crt++;
    if (crt < len)
	dumpData(comp,list,causeName + ".diagnostic",buf + crt,len - crt);
    return true;
}

// Decode bearer capabilities as defined in Q.931 (Bearer Capabilities) and Q.763 (User Service Information)
// Q.931 - 4.5.5 / Q.763 - 3.57
// The given sections in comments are from Q.931
bool SignallingUtils::decodeCaps(const SignallingComponent* comp, NamedList& list, const unsigned char* buf,
	unsigned int len, const char* prefix, bool isup)
{
    if (!buf)
	return false;
    if (len < 2) {
	Debug(comp,DebugMild,"Utils::decodeCaps. Invalid length %u",len);
	return false;
    }
    String capsName = prefix;
    // Byte 0: Coding standard (bit 5,6), Information transfer capability (bit 0-4)
    // Byte 1: Transfer mode (bit 5,6), Transfer rate (bit 0-4)
    unsigned char coding = (buf[0] & 0x60) >> 5;
    addKeyword(list,capsName + ".coding",codings(),coding);
    addKeyword(list,capsName + ".transfercap",dict(2,coding),buf[0] & 0x1f);
    addKeyword(list,capsName + ".transfermode",dict(3,coding),(buf[1] & 0x60) >> 5);
    u_int8_t rate = buf[1] & 0x1f;
    addKeyword(list,capsName + ".transferrate",dict(4,coding),rate);
    // Figure 4.11 Note 1: Next byte is the rate multiplier if the transfer rate is 'multirate' (0x18)
    u_int8_t crt = 2;
    if (rate == 0x18) {
	if (len < 3) {
	    Debug(comp,DebugMild,"Utils::decodeCaps. Invalid length %u. No rate multiplier",len);
	    return false;
	}
	addKeyword(list,capsName + ".multiplier",0,buf[2] & 0x7f);
	crt = 3;
    }
    // Get optional extra information
    // Layer 1 data
    if (len <= crt)
	return true;
    u_int8_t ident = (buf[crt] & 0x60) >> 5;
    if (ident != 1) {
	Debug(comp,DebugNote,"Utils::decodeCaps. Invalid layer 1 ident %u",ident);
	return true;
    }
    addKeyword(list,capsName,dict(1,coding),buf[crt] & 0x1f);
    //TODO: Decode the rest of Layer 1, Layer 2 and Layer 3 data
    return true;
}

// Encode a comma separated list of flags. Flags can be prefixed with the '-'
//  character to be reset if previously set
void SignallingUtils::encodeFlags(const SignallingComponent* comp,
	int& dest, const String& flags, const TokenDict* dict)
{
    if (flags.null() || !dict)
	return;
    ObjList* list = flags.split(',',false);
    DDebug(comp,DebugAll,"Utils::encodeFlags '%s' dest=0x%x",flags.c_str(),dest);
    for (ObjList* o = list->skipNull(); o; o = o->skipNext()) {
	String* s = static_cast<String*>(o->get());
	bool set = !s->startSkip("-",false);
	const TokenDict* p = dict;
	for (; p->token && *s != p->token; p++) ;
	if (!p->token) {
	    DDebug(comp,DebugAll,"Utils::encodeFlags '%s' not found",s->c_str());
	    continue;
	}
	DDebug(comp,DebugAll,"Utils::encodeFlags %sset %s=0x%x",
	    set?"":"re",p->token,p->value);
	if (set)
	    dest |= p->value;
	else
	    dest &= ~p->value;
    }
    TelEngine::destruct(list);
}

// Encode a comma separated list of signalling flags
unsigned int SignallingUtils::encodeFlags(const SignallingComponent* comp, const String& flags,
    const SignallingFlags* dict, const char* paramName)
{
    if (!dict)
	return 0;
    unsigned int v = 0;
    ObjList* l = flags.split(',',false);
    for (ObjList* o = l->skipNull(); o; o = o->skipNext()) {
	const String* s = static_cast<const String*>(o->get());
	for (const SignallingFlags* d = dict; d->mask; d++) {
	    if (*s == d->name) {
		if (v & d->mask) {
		    Debug(comp,DebugMild,"Flag %s. %s overwriting bits 0x%x",
			paramName,d->name,v & d->mask);
		    v &= d->mask;
		}
		v |= d->value;
	    }
	}
    }
    TelEngine::destruct(l);
    return v;
}

// Q.850 2.1
bool SignallingUtils::encodeCause(const SignallingComponent* comp, DataBlock& buf,
	const NamedList& params, const char* prefix, bool isup, bool fail)
{
    u_int8_t data[4] = {2,0x80,0x80,0x80};
    String causeName = prefix;
    // Coding standard (0: CCITT) + location. If no location, set it to 0x0a: "BI"
    unsigned char coding = fixValue(params,causeName + ".coding",codings(),0x03,0x60,5);
    unsigned char loc = (unsigned char)params.getIntValue(causeName + ".location",locations(),0x0a);
    data[1] |= (coding << 5) | (loc & 0x0f);
    // Recommendation (only for Q.931)
    if (!isup) {
	unsigned char rec = (unsigned char)params.getIntValue(causeName + ".rec",0,0);
	// Add recommendation. Clear bit 7 of the first byte
	data[1] &= 0x7f;
	data[2] |= (rec & 0x7f);
	data[0] = 3;
    }
    // Value. Set to normal-clearing if missing for CCITT encoding or
    //  to 0 for other encoding standards
    unsigned char val = (unsigned char)params.getIntValue(causeName,dict(0,coding),!coding ? 0x10 : 0);
    data[data[0]] |= (val & 0x7f);
    // Diagnostic
    DataBlock diagnostic;
    const char* tmp = params.getValue(causeName + ".diagnostic");
    if (tmp)
	diagnostic.unHexify(tmp,strlen(tmp),' ');
    // Set data
    if (!isup && diagnostic.length() + data[0] + 1 > 32) {
	Debug(comp,fail?DebugNote:DebugMild,
	    "Utils::encodeCause. Cause length %u > 32. %s",
	    diagnostic.length() + data[0] + 1,fail?"Fail":"Skipping diagnostic");
	if (fail)
	    return false;
	diagnostic.clear();
    }
    u_int8_t len = data[0] + 1;
    data[0] += diagnostic.length();
    buf.assign(data,len);
    buf += diagnostic;
    return true;
}

bool SignallingUtils::encodeCaps(const SignallingComponent* comp, DataBlock& buf, const NamedList& params,
	const char* prefix, bool isup)
{
    u_int8_t data[5] = {2,0x80,0x80,0x80,0x80};
    String capsName = prefix;
    // Byte 1: Coding standard (bit 5,6), Information transfer capability (bit 0-4)
    // Byte 2: Transfer mode (bit 5,6), Transfer rate (bit 0-4)
    unsigned char coding = fixValue(params,capsName + ".coding",codings(),0x03,0x60,5);
    unsigned char cap = (unsigned char)params.getIntValue(capsName + ".transfercap",dict(2,coding),0);
    unsigned char mode = fixValue(params,capsName + ".transfermode",dict(3,coding),0x03,0x60,5);
    unsigned char rate = (unsigned char)params.getIntValue(capsName + ".transferrate",dict(4,coding),0x10);
    data[1] |= (coding << 5) | (cap & 0x1f);
    data[2] |= (mode << 5) | (rate & 0x1f);
    if (rate == 0x18) {
	data[0] = 3;
	rate = (unsigned char)params.getIntValue(capsName + ".multiplier",0,0);
	data[3] |= rate & 0x7f;
    }
    // User information layer data
    // Bit 7 = 1, Bits 5,6 = layer (1), Bits 0-4: the value
    int format = params.getIntValue(capsName,dict(1,coding),-1);
    if (format != -1) {
	data[data[0] + 1] |= 0x20 | (((unsigned char)format) & 0x1f);
	data[0]++;
    }
    buf.assign(data,data[0] + 1);
    return true;
}

// Parse a list of integers or integer intervals. Source elements must be separated by a
//   '.' or ',' character. Integer intervals must be separated by a '-' character.
// Empty elements are silently discarded
unsigned int* SignallingUtils::parseUIntArray(const String& source,
	unsigned int min, unsigned int max,
	unsigned int& count, bool discardDup)
{
    count = 0;
    ObjList* list = source.split(((-1!=source.find(','))?',':'.'),false);
    if (!list->count()) {
	TelEngine::destruct(list);
	return 0;
    }

    unsigned int maxArray = 0;
    unsigned int* array = 0;
    bool ok = true;
    int first, last;

    for (ObjList* o = list->skipNull(); o; o = o->skipNext()) {
	String* s = static_cast<String*>(o->get());
	// Get the interval (may be a single value)
	int sep = s->find('-');
	if (sep == -1)
	    first = last = s->toInteger(-1);
	else {
	    first = s->substr(0,sep).toInteger(-1);
	    last = s->substr(sep + 1).toInteger(-2);
	}
	if (first < 0 || last < 0 || last < first) {
	    ok = false;
	    break;
	}
	// Resize and copy array if not enough room
	unsigned int len = (unsigned int)(last - first + 1);
	if (count + len > maxArray) {
	    maxArray = count + len;
	    unsigned int* tmp = new unsigned int[maxArray];
	    if (array) {
		::memcpy(tmp,array,sizeof(unsigned int) * count);
		delete[] array;
	    }
	    array = tmp;
	}
	// Add to array code list
	for (; first <= last; first++) {
	    // Check interval
	    if ((unsigned int)first < min || max < (unsigned int)first) {
		ok = false;
		break;
	    }
	    // Check duplicates
	    if (discardDup) {
		bool dup = false;
		for (unsigned int i = 0; i < count; i++)
		    if (array[i] == (unsigned int)first) {
			dup = true;
			break;
		    }
		if (dup)
		    continue;
	    }
	    array[count++] = first;
	}
	if (!ok)
	    break;
    }
    TelEngine::destruct(list);

    if (ok && count)
	return array;
    count = 0;
    if (array)
	delete[] array;
    return 0;
}


/*
 * SignallingMessageTimerList
 */
// Add a pending operation to the list. Start its timer
SignallingMessageTimer* SignallingMessageTimerList::add(SignallingMessageTimer* m,
    const Time& when)
{
    if (!m)
	return 0;
    m->stop();
    m->start(when.msec());
    if (m->global().interval() && !m->global().started())
	m->global().start(when.msec());
    ObjList* ins = skipNull();
    for (; ins; ins = ins->skipNext()) {
	SignallingMessageTimer* crt = static_cast<SignallingMessageTimer*>(ins->get());
	if (m->fireTime() < crt->fireTime())
	    break;
    }
    if (!ins)
	append(m);
    else
	ins->insert(m);
    return m;
}

// Check if the first operation timed out
SignallingMessageTimer* SignallingMessageTimerList::timeout(const Time& when)
{
    ObjList* o = skipNull();
    if (!o)
	return 0;
    SignallingMessageTimer* m = static_cast<SignallingMessageTimer*>(o->get());
    if (!(m->timeout(when.msec()) || m->global().timeout(when.msec())))
	return 0;
    o->remove(false);
    return m;
}

/* vi: set ts=8 sw=4 sts=4 noet: */
