/**@file TransactionTable and related classes. */

/*
* Copyright 2008, 2010 Free Software Foundation, Inc.
* Copyright 2010 Kestrel Signal Process, Inc.
* Copyright 2011, 2012 Range Networks, Inc.
*
* This software is distributed under multiple licenses;
* see the COPYING file in the main directory for licensing
* information for this specific distribuion.
*
* 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 "TransactionTable.h"
#include "ControlCommon.h"

#include <GSMLogicalChannel.h>
#include <GSML3Message.h>
#include <GSML3CCMessages.h>
#include <GSML3RRMessages.h>
#include <GSML3MMMessages.h>
#include <GSMConfig.h>

#include <sqlite3.h>
#include <sqlite3util.h>

#include <SIPEngine.h>
#include <SIPInterface.h>
#include <SIPUtility.h>

#include <CallControl.h>

#include <Reporting.h>
#include <Logger.h>
#undef WARNING


using namespace std;
using namespace GSM;
using namespace Control;
using namespace SIP;



static const char* createTransactionTable = {
	"CREATE TABLE IF NOT EXISTS TRANSACTION_TABLE ("
		"ID INTEGER PRIMARY KEY, "		// internal transaction ID
		"CHANNEL TEXT DEFAULT NULL,"	// channel description string (cross-refs CHANNEL_TABLE)
		"CREATED INTEGER NOT NULL, "	// Unix time of record creation
		"CHANGED INTEGER NOT NULL, "	// time of last state change
		"TYPE TEXT, "					// transaction type
		"SUBSCRIBER TEXT, "				// IMSI, if known
		"L3TI INTEGER, "				// GSM L3 transaction ID, +0x08 if generated by MS
		"SIP_CALLID TEXT, "				// SIP-side call id tag
		"SIP_PROXY TEXT, "				// SIP proxy IP
		"CALLED TEXT, "					// called party number
		"CALLING TEXT, "				// calling party number
		"GSMSTATE TEXT, "				// GSM/Q.931 state
		"SIPSTATE TEXT "				// SIP state
	")"
};




void TransactionEntry::initTimers()
{
	// Call this only once, from the constructor.
	// TODO -- It would be nice if these were all configurable.
	assert(mTimers.size()==0);
	mTimers["301"] = Z100Timer(T301ms);
	mTimers["302"] = Z100Timer(T302ms);
	mTimers["303"] = Z100Timer(T303ms);
	mTimers["304"] = Z100Timer(T304ms);
	mTimers["305"] = Z100Timer(T305ms);
	mTimers["308"] = Z100Timer(T308ms);
	mTimers["310"] = Z100Timer(T310ms);
	mTimers["313"] = Z100Timer(T313ms);
	mTimers["3113"] = Z100Timer(gConfig.getNum("GSM.Timer.T3113"));
	mTimers["TR1M"] = Z100Timer(TR1Mms);
}





// Form for MT transactions.
TransactionEntry::TransactionEntry(
	const char* proxy,
	const L3MobileIdentity& wSubscriber, 
	GSM::LogicalChannel* wChannel,
	const L3CMServiceType& wService,
	const L3CallingPartyBCDNumber& wCalling,
	GSM::CallState wState,
	const char *wMessage,
	bool wFake)
	:mID(gTransactionTable.newID()),
	mSubscriber(wSubscriber),mService(wService),
	mL3TI(gTMSITable.nextL3TI(wSubscriber.digits())),
	mCalling(wCalling),
	mSIP(proxy,mSubscriber.digits()),
	mGSMState(wState),
	mNumSQLTries(gConfig.getNum("Control.NumSQLTries")),
	mChannel(wChannel),
	mTerminationRequested(false),
	mRemoved(false),
	mFake(wFake)
	 
{
	if (wMessage) mMessage.assign(wMessage); //strncpy(mMessage,wMessage,160);
	else mMessage.assign(""); //mMessage[0]='\0';
	initTimers();
}

// Form for MOC transactions.
TransactionEntry::TransactionEntry(
	const char* proxy,
	const L3MobileIdentity& wSubscriber,
	GSM::LogicalChannel* wChannel,
	const L3CMServiceType& wService,
	unsigned wL3TI,
	const L3CalledPartyBCDNumber& wCalled)
	:mID(gTransactionTable.newID()),
	mSubscriber(wSubscriber),mService(wService),
	mL3TI(wL3TI),
	mCalled(wCalled),
	mSIP(proxy,mSubscriber.digits()),
	mGSMState(GSM::MOCInitiated),
	mNumSQLTries(gConfig.getNum("Control.NumSQLTries")),
	mChannel(wChannel),
	mTerminationRequested(false),
	mRemoved(false),
	mFake(false)
{
	assert(mSubscriber.type()==GSM::IMSIType);
	mMessage.assign(""); //mMessage[0]='\0';
	initTimers();
}


// Form for SOS transactions.
TransactionEntry::TransactionEntry(
	const char* proxy,
	const L3MobileIdentity& wSubscriber,
	GSM::LogicalChannel* wChannel,
	const L3CMServiceType& wService,
	unsigned wL3TI)
	:mID(gTransactionTable.newID()),
	mSubscriber(wSubscriber),mService(wService),
	mL3TI(wL3TI),
	mSIP(proxy,mSubscriber.digits()),
	mGSMState(GSM::MOCInitiated),
	mNumSQLTries(2*gConfig.getNum("Control.NumSQLTries")),
	mChannel(wChannel),
	mTerminationRequested(false),
	mRemoved(false),
	mFake(false)
{
	mMessage.assign(""); //mMessage[0]='\0';
	initTimers();
}


// Form for MO-SMS transactions.
TransactionEntry::TransactionEntry(
	const char* proxy,
	const L3MobileIdentity& wSubscriber,
	GSM::LogicalChannel* wChannel,
	const L3CalledPartyBCDNumber& wCalled,
	const char* wMessage)
	:mID(gTransactionTable.newID()),
	mSubscriber(wSubscriber),
	mService(GSM::L3CMServiceType::ShortMessage),
	mL3TI(7),mCalled(wCalled),
	mSIP(proxy,mSubscriber.digits()),
	mGSMState(GSM::SMSSubmitting),
	mNumSQLTries(gConfig.getNum("Control.NumSQLTries")),
	mChannel(wChannel),
	mTerminationRequested(false),
	mRemoved(false),
	mFake(false)
{
	assert(mSubscriber.type()==GSM::IMSIType);
	if (wMessage!=NULL) mMessage.assign(wMessage); //strncpy(mMessage,wMessage,160);
	else mMessage.assign(""); //mMessage[0]='\0';
	initTimers();
}

// Form for MO-SMS transactions with parallel call.
TransactionEntry::TransactionEntry(
	const char* proxy,
	const L3MobileIdentity& wSubscriber,
	GSM::LogicalChannel* wChannel)
	:mID(gTransactionTable.newID()),
	mSubscriber(wSubscriber),
	mService(GSM::L3CMServiceType::ShortMessage),
	mL3TI(7),
	mSIP(proxy,mSubscriber.digits()),
	mGSMState(GSM::SMSSubmitting),
	mNumSQLTries(gConfig.getNum("Control.NumSQLTries")),
	mChannel(wChannel),
	mTerminationRequested(false),
	mRemoved(false),
	mFake(false)
{
	assert(mSubscriber.type()==GSM::IMSIType);
	mMessage[0]='\0';
	initTimers();
}


TransactionEntry::~TransactionEntry()
{
	// This should go out of scope before the object is actually destroyed.
	ScopedLock lock(mLock);

	// Remove the associated SIP message FIFO.
	gSIPInterface.removeCall(mSIP.callID());

	// Delete the SQL table entry.
	char query[100];
	sprintf(query,"DELETE FROM TRANSACTION_TABLE WHERE ID=%u",mID);
	runQuery(query);

}


void TransactionEntry::resetTimer(const char* name)
{
	if (mRemoved) throw RemovedTransaction(mID);
	ScopedLock lock(mLock);
	mTimers[name].reset();
}


void TransactionEntry::setTimer(const char* name)
{
	if (mRemoved) throw RemovedTransaction(mID);
	ScopedLock lock(mLock);
	mTimers[name].set();
}

void TransactionEntry::setTimer(const char* name, long newLimit)
{
	if (mRemoved) throw RemovedTransaction(mID);
	ScopedLock lock(mLock);
	mTimers[name].set(newLimit);
}


bool TransactionEntry::timerExpired(const char* name) const
{
	if (mRemoved) throw RemovedTransaction(mID);
	ScopedLock lock(mLock);
	TimerTable::const_iterator itr = mTimers.find(name);
	assert(itr!=mTimers.end());
	return (itr->second).expired();
}


bool TransactionEntry::anyTimerExpired() const
{
	if (mRemoved) throw RemovedTransaction(mID);
	ScopedLock lock(mLock);
	TimerTable::const_iterator itr = mTimers.begin();
	while (itr!=mTimers.end()) {
		if ((itr->second).expired()) {
			LOG(INFO) << itr->first << " expired in " << *this;
			return true;
		}
		++itr;
	}
	return false;
}


void TransactionEntry::resetTimers()
{
	if (mRemoved) throw RemovedTransaction(mID);
	ScopedLock lock(mLock);
	TimerTable::iterator itr = mTimers.begin();
	while (itr!=mTimers.end()) {
		(itr->second).reset();
		++itr;
	}
}


bool TransactionEntry::clearingGSM() const
{
	if (mRemoved) throw RemovedTransaction(mID);
	ScopedLock lock(mLock);
	return (mGSMState==GSM::ReleaseRequest) || (mGSMState==GSM::DisconnectIndication);
}


bool TransactionEntry::deadOrRemoved() const
{
	if (mRemoved) return true;
	ScopedLock lock(mLock);
	return dead();
}


bool TransactionEntry::dead() const
{
	// Get the state information and release the locks.

	// If it's locked, we assume someone has locked it,
	// so it's not dead.
	// And if someone locked in permanently,
	// the resulting deadlock would spread through the whole system.

	if (!mLock.trylock()) return false;
	SIP::SIPState lSIPState = mSIP.state();
	GSM::CallState lGSMState = mGSMState;
	unsigned age = mStateTimer.elapsed();
	mLock.unlock();

	// Now check states against the timer.


	// 30-second tests
	if (age < 30*1000) return false;
	// Failed?
	if (lSIPState==SIP::Fail) return true;
	// SIP Null state?
	if (lSIPState==SIP::NullState) return true;
	// SIP stuck in proceeding?
	if (lSIPState==SIP::Proceeding) return true;
	// SIP cancelled?
	if (lSIPState==SIP::Canceled) return true;
	// SIP Cleared?
	if (lSIPState==SIP::Cleared) return true;

	// 180-second tests
	if (age < 180*1000) return false;
	// Dead if someone requested removal >3 min ago.
	if (mRemoved) return true;
	// Any GSM state other than Active for >3 min?
	if (lGSMState!=GSM::Active) return true;
	// Any SIP stte other than active for >3 min?
	if (lSIPState !=SIP::Active) return true;
	
	// If we got here, the state-vs-timer relationship
	// appears to be valid.
	return false;
}



ostream& Control::operator<<(ostream& os, const TransactionEntry& entry)
{
	entry.text(os);
	return os;
}



void TransactionEntry::text(ostream& os) const
{
	ScopedLock lock(mLock);
	os << mID;
	if (mRemoved) os << " (removed)";
	else if (dead()) os << " (defunct)";
	if (mChannel) os << " " << *mChannel;
	else os << " no chan";
	os << " " << mSubscriber;
	os << " L3TI=" << mL3TI;
	os << " SIP-call-id=" << mSIP.callID();
	os << " SIP-proxy=" << mSIP.proxyIP() << ":" << mSIP.proxyPort();
	os << " " << mService;
	if (mCalled.digits()[0]) os << " to=" << mCalled.digits();
	if (mCalling.digits()[0]) os << " from=" << mCalling.digits();
	os << " GSMState=" << mGSMState;
	os << " SIPState=" << mSIP.state();
	os << " (" << (stateAge()+500)/1000 << " sec)";
	if (mMessage[0]) os << " message=\"" << mMessage << "\"";
}

void TransactionEntry::message(const char *wMessage, size_t length)
{
	/*if (length>520) {
		LOG(NOTICE) << "truncating long message: " << wMessage;
		length=520;
	}*/
	if (mRemoved) throw RemovedTransaction(mID);
	ScopedLock lock(mLock);
	//memcpy(mMessage,wMessage,length);
	//mMessage[length]='\0';
	mMessage.assign(wMessage, length);
}

void TransactionEntry::messageType(const char *wContentType)
{
	if (mRemoved) throw RemovedTransaction(mID);
	ScopedLock lock(mLock);
	mContentType.assign(wContentType);
}



void TransactionEntry::runQuery(const char* query) const
{
	// Caller should hold mLock and should have already checked mRemoved..
	for (unsigned i=0; i<mNumSQLTries; i++) {
		if (sqlite3_command(gTransactionTable.DB(),query)) return;
	}
	LOG(ALERT) << "transaction table access failed after " << mNumSQLTries << "attempts. query:" << query << " error: " << sqlite3_errmsg(gTransactionTable.DB());
}



void TransactionEntry::insertIntoDatabase()
{
	// This should be called only from gTransactionTable::add.
	// Caller should hold mLock.

	ostringstream serviceTypeSS;
	serviceTypeSS << mService;

	ostringstream sipStateSS;
	sipStateSS << mSIP.state();
	mPrevSIPState = mSIP.state();

	char subscriber[25];
	switch (mSubscriber.type()) {
		case IMSIType: sprintf(subscriber,"IMSI%s",mSubscriber.digits()); break;
		case IMEIType: sprintf(subscriber,"IMEI%s",mSubscriber.digits()); break;
		case TMSIType: sprintf(subscriber,"TMSI%x",mSubscriber.TMSI()); break;
		default:
			sprintf(subscriber,"invalid");
			LOG(ERR) << "non-valid subscriber ID in transaction table: " << mSubscriber;
	}

	const char* stateString = GSM::CallStateString(mGSMState);
	assert(stateString);

	// FIXME -- This should be done in a single SQL transaction.

	char query[500];
	unsigned now = (unsigned)time(NULL);
	sprintf(query,"INSERT INTO TRANSACTION_TABLE "
		        "(ID,CREATED,CHANGED,TYPE,SUBSCRIBER,L3TI,CALLED,CALLING,GSMSTATE,SIPSTATE,SIP_CALLID,SIP_PROXY) "
		"VALUES  (%u,%u,     %u,     '%s','%s',      %u,'%s',  '%s',   '%s',    '%s',      '%s',      '%s')",
		mID,now,now,
		serviceTypeSS.str().c_str(),
		subscriber,
		mL3TI,
		mCalled.digits(),
		mCalling.digits(),
		stateString,
		sipStateSS.str().c_str(),
		mSIP.callID().c_str(),
		mSIP.proxyIP().c_str()
	);

	runQuery(query);

	if (!mChannel) return;
	sprintf(query,"UPDATE TRANSACTION_TABLE SET CHANNEL='%s' WHERE ID=%u",
			mChannel->descriptiveString(), mID);
	runQuery(query);
}



void TransactionEntry::channel(GSM::LogicalChannel* wChannel)
{
	if (mRemoved) throw RemovedTransaction(mID);
	ScopedLock lock(mLock);
	mChannel = wChannel;

	char query[500];
	if (mChannel) {
		sprintf(query,"UPDATE TRANSACTION_TABLE SET CHANGED=%u,CHANNEL='%s' WHERE ID=%u",
				(unsigned)time(NULL), mChannel->descriptiveString(), mID);
	} else {
		sprintf(query,"UPDATE TRANSACTION_TABLE SET CHANGED=%u,CHANNEL=NULL WHERE ID=%u",
				(unsigned)time(NULL), mID);
	}

	runQuery(query);
}


GSM::LogicalChannel* TransactionEntry::channel()
{
	if (mRemoved) throw RemovedTransaction(mID);
	return mChannel;
}

const GSM::LogicalChannel* TransactionEntry::channel() const
{
	if (mRemoved) throw RemovedTransaction(mID);
	return mChannel;
}



unsigned TransactionEntry::L3TI() const
{
	if (mRemoved) throw RemovedTransaction(mID);
	return mL3TI;
}

GSM::CallState TransactionEntry::GSMState() const
{
	if (mRemoved) throw RemovedTransaction(mID);
	ScopedLock lock(mLock);
	return mGSMState;
}


void TransactionEntry::GSMState(GSM::CallState wState)
{
	if (mRemoved) throw RemovedTransaction(mID);
	ScopedLock lock(mLock);
	mStateTimer.now();
	unsigned now = mStateTimer.sec();

	mGSMState = wState;
	const char* stateString = GSM::CallStateString(wState);
	assert(stateString);

	char query[150];
	sprintf(query,
		"UPDATE TRANSACTION_TABLE SET GSMSTATE='%s',CHANGED=%u WHERE ID=%u",
		stateString,now, mID);
	runQuery(query);
}


SIP::SIPState TransactionEntry::echoSIPState(SIP::SIPState state) const
{
	// Caller should hold mLock.
	if (mPrevSIPState==state) return state;
	mPrevSIPState = state;

	const char* stateString = SIP::SIPStateString(state);
	assert(stateString);

	unsigned now = time(NULL);

	char query[150];
	sprintf(query,
		"UPDATE TRANSACTION_TABLE SET SIPSTATE='%s',CHANGED=%u WHERE ID=%u",
		stateString,now,mID);
	runQuery(query);

	return state;
}




SIP::SIPState TransactionEntry::MOCSendINVITE(const char* calledUser, const char* calledDomain, short rtpPort, unsigned codec)
{
	if (mRemoved) throw RemovedTransaction(mID);
	ScopedLock lock(mLock);
	SIP::SIPState state = mSIP.MOCSendINVITE(calledUser,calledDomain,rtpPort,codec,channel());
	echoSIPState(state);
	return state;
}

SIP::SIPState TransactionEntry::MOCResendINVITE()
{
	if (mRemoved) throw RemovedTransaction(mID);
	ScopedLock lock(mLock);
	SIP::SIPState state = mSIP.MOCResendINVITE();
	echoSIPState(state);
	return state;
}

SIP::SIPState TransactionEntry::MOCCheckForOK()
{
	if (mRemoved) throw RemovedTransaction(mID);
	ScopedLock lock(mLock);
	SIP::SIPState state = mSIP.MOCCheckForOK(&mLock);
	echoSIPState(state);
	return state;
}

SIP::SIPState TransactionEntry::MOCSendACK()
{
	if (mRemoved) throw RemovedTransaction(mID);
	ScopedLock lock(mLock);
	SIP::SIPState state = mSIP.MOCSendACK();
	echoSIPState(state);
	return state;
}

SIP::SIPState TransactionEntry::SOSSendINVITE(short rtpPort, unsigned codec)
{
	if (mRemoved) throw RemovedTransaction(mID);
	ScopedLock lock(mLock);
	SIP::SIPState state = mSIP.SOSSendINVITE(rtpPort,codec,channel());
	echoSIPState(state);
	return state;
}


SIP::SIPState TransactionEntry::MTCSendTrying()
{
	if (mRemoved) throw RemovedTransaction(mID);
	ScopedLock lock(mLock);
	SIP::SIPState state = mSIP.MTCSendTrying();
	echoSIPState(state);
	return state;
}

SIP::SIPState TransactionEntry::MTCSendRinging()
{
	if (mRemoved) throw RemovedTransaction(mID);
	ScopedLock lock(mLock);
	SIP::SIPState state = mSIP.MTCSendRinging();
	echoSIPState(state);
	return state;
}

SIP::SIPState TransactionEntry::MTCCheckForACK()
{
	if (mRemoved) throw RemovedTransaction(mID);
	ScopedLock lock(mLock);
	SIP::SIPState state = mSIP.MTCCheckForACK(&mLock);
	echoSIPState(state);
	return state;
}

SIP::SIPState TransactionEntry::MTCCheckForCancel()
{
	if (mRemoved) throw RemovedTransaction(mID);
	ScopedLock lock(mLock);
	SIP::SIPState state = mSIP.MTCCheckForCancel();
	echoSIPState(state);
	return state;
}


SIP::SIPState TransactionEntry::MTCSendOK(short rtpPort, unsigned codec)
{
	if (mRemoved) throw RemovedTransaction(mID);
	ScopedLock lock(mLock);
	SIP::SIPState state = mSIP.MTCSendOK(rtpPort,codec,channel());
	echoSIPState(state);
	return state;
}

SIP::SIPState TransactionEntry::MODSendBYE()
{
	if (mRemoved) throw RemovedTransaction(mID);
	ScopedLock lock(mLock);
	SIP::SIPState state = mSIP.MODSendBYE();
	echoSIPState(state);
	return state;
}

SIP::SIPState TransactionEntry::MODSendERROR(osip_message_t * cause, int code, const char * reason, bool cancel)
{
	if (mRemoved) throw RemovedTransaction(mID);
	ScopedLock lock(mLock);
	SIP::SIPState state = mSIP.MODSendERROR(cause, code, reason, cancel);
	echoSIPState(state);
	return state;
}

SIP::SIPState TransactionEntry::MODSendCANCEL()
{
	if (mRemoved) throw RemovedTransaction(mID);
	ScopedLock lock(mLock);
	SIP::SIPState state = mSIP.MODSendCANCEL();
	echoSIPState(state);
	return state;
}

SIP::SIPState TransactionEntry::MODResendBYE()
{
	if (mRemoved) throw RemovedTransaction(mID);
	ScopedLock lock(mLock);
	SIP::SIPState state = mSIP.MODResendBYE();
	echoSIPState(state);
	return state;
}

SIP::SIPState TransactionEntry::MODResendCANCEL()
{
	if (mRemoved) throw RemovedTransaction(mID);
	ScopedLock lock(mLock);
	SIP::SIPState state = mSIP.MODResendCANCEL();
	echoSIPState(state);
	return state;
}

SIP::SIPState TransactionEntry::MODResendERROR(bool cancel)
{
	if (mRemoved) throw RemovedTransaction(mID);
	ScopedLock lock(mLock);
	SIP::SIPState state = mSIP.MODResendERROR(cancel);
	echoSIPState(state);
	return state;
}

SIP::SIPState TransactionEntry::MODWaitForBYEOK()
{
	if (mRemoved) throw RemovedTransaction(mID);
	ScopedLock lock(mLock);
	SIP::SIPState state = mSIP.MODWaitForBYEOK(&mLock);
	echoSIPState(state);
	return state;
}

SIP::SIPState TransactionEntry::MODWaitForCANCELOK()
{
	if (mRemoved) throw RemovedTransaction(mID);
	ScopedLock lock(mLock);
	SIP::SIPState state = mSIP.MODWaitForCANCELOK(&mLock);
	echoSIPState(state);
	return state;
}

SIP::SIPState TransactionEntry::MODWaitForERRORACK(bool cancel)
{
	if (mRemoved) throw RemovedTransaction(mID);
	ScopedLock lock(mLock);
	SIP::SIPState state = mSIP.MODWaitForERRORACK(cancel,&mLock);
	echoSIPState(state);
	return state;
}

SIP::SIPState TransactionEntry::MODWaitFor487()
{
	if (mRemoved) throw RemovedTransaction(mID);
	ScopedLock lock(mLock);
	SIP::SIPState state = mSIP.MODWaitFor487(&mLock);
	echoSIPState(state);
	return state;
}

SIP::SIPState TransactionEntry::MODWaitForResponse(vector<unsigned> *validResponses)
{
	if (mRemoved) throw RemovedTransaction(mID);
	ScopedLock lock(mLock);
	SIP::SIPState state = mSIP.MODWaitForResponse(validResponses, &mLock);
	echoSIPState(state);
	return state;
}

SIP::SIPState TransactionEntry::MTDCheckBYE()
{
	if (mRemoved) throw RemovedTransaction(mID);
	ScopedLock lock(mLock);
	SIP::SIPState state = mSIP.MTDCheckBYE();
	echoSIPState(state);
	return state;
}

SIP::SIPState TransactionEntry::MTDSendBYEOK()
{
	if (mRemoved) throw RemovedTransaction(mID);
	ScopedLock lock(mLock);
	SIP::SIPState state = mSIP.MTDSendBYEOK();
	echoSIPState(state);
	return state;
}

SIP::SIPState TransactionEntry::MTDSendCANCELOK()
{
	if (mRemoved) throw RemovedTransaction(mID);
	ScopedLock lock(mLock);
	SIP::SIPState state = mSIP.MTDSendCANCELOK();
	echoSIPState(state);
	return state;
}

SIP::SIPState TransactionEntry::MOSMSSendMESSAGE(const char* calledUser, const char* calledDomain, const char* contentType)
{
	if (mRemoved) throw RemovedTransaction(mID);
	ScopedLock lock(mLock);
	SIP::SIPState state = mSIP.MOSMSSendMESSAGE(calledUser,calledDomain,mMessage.c_str(),contentType,channel());
	echoSIPState(state);
	return state;
}

SIP::SIPState TransactionEntry::MOSMSWaitForSubmit()
{
	if (mRemoved) throw RemovedTransaction(mID);
	ScopedLock lock(mLock);
	SIP::SIPState state = mSIP.MOSMSWaitForSubmit(&mLock);
	echoSIPState(state);
	return state;
}

SIP::SIPState TransactionEntry::MTSMSSendOK()
{
	if (mRemoved) throw RemovedTransaction(mID);
	ScopedLock lock(mLock);
	SIP::SIPState state = mSIP.MTSMSSendOK(channel());
	echoSIPState(state);
	return state;
}

bool TransactionEntry::sendINFOAndWaitForOK(unsigned info)
{
	if (mRemoved) throw RemovedTransaction(mID);
	ScopedLock lock(mLock);
	return mSIP.sendINFOAndWaitForOK(info,&mLock);
}

void TransactionEntry::SIPUser(const char* IMSI)
{
	if (mRemoved) throw RemovedTransaction(mID);
	ScopedLock lock(mLock);
	mSIP.user(IMSI);
}

void TransactionEntry::SIPUser(const char* callID, const char *IMSI , const char *origID, const char *origHost)
{
	if (mRemoved) throw RemovedTransaction(mID);
	ScopedLock lock(mLock);
	mSIP.user(callID,IMSI,origID,origHost);
}

void TransactionEntry::called(const L3CalledPartyBCDNumber& wCalled)
{
	if (mRemoved) throw RemovedTransaction(mID);
	ScopedLock lock(mLock);
	mCalled = wCalled;

	char query[151];
	snprintf(query,150,
		"UPDATE TRANSACTION_TABLE SET CALLED='%s' WHERE ID=%u",
		mCalled.digits(), mID);
	runQuery(query);
}


void TransactionEntry::L3TI(unsigned wL3TI)
{
	if (mRemoved) throw RemovedTransaction(mID);
	ScopedLock lock(mLock);
	mL3TI = wL3TI;

	char query[151];
	snprintf(query,150,
		"UPDATE TRANSACTION_TABLE SET L3TI=%u WHERE ID=%u",
		mL3TI, mID);
	runQuery(query);
}


bool TransactionEntry::terminationRequested()
{
	if (mRemoved) throw RemovedTransaction(mID);
	ScopedLock lock(mLock);
	bool retVal = mTerminationRequested;
	mTerminationRequested = false;
	return retVal;
}

void TransactionTable::init(const char* path)
{
	// This assumes the main application uses sdevrandom.
	mIDCounter = random();
	// Connect to the database.
	int rc = sqlite3_open(path,&mDB);
	if (rc) {
		LOG(ALERT) << "Cannot open Transaction Table database at " << path << ": " << sqlite3_errmsg(mDB);
		sqlite3_close(mDB);
		mDB = NULL;
		return;
	}
	// Create a new table, if needed.
	if (!sqlite3_command(mDB,createTransactionTable)) {
		LOG(ALERT) << "Cannot create Transaction Table";
	}
	// Clear any previous entires.
	if (!sqlite3_command(gTransactionTable.DB(),"DELETE FROM TRANSACTION_TABLE"))
		LOG(WARNING) << "cannot clear previous transaction table";
}

TransactionTable::~TransactionTable()
{
	// Don't bother disposing of the memory,
	// since this is only invoked when the application exits.
	if (mDB) sqlite3_close(mDB);
}




unsigned TransactionTable::newID()
{
	ScopedLock lock(mLock);
	return mIDCounter++;
}


void TransactionTable::add(TransactionEntry* value)
{
	LOG(INFO) << "new transaction " << *value;
	ScopedLock lock(mLock);
	mTable[value->ID()]=value;
	value->insertIntoDatabase();
}



TransactionEntry* TransactionTable::find(unsigned key)
{
	// Since this is a log-time operation, we don't screw that up by calling clearDeadEntries.

	// ID==0 is a non-valid special case.
	LOG(DEBUG) << "by key: " << key;
	assert(key);
	ScopedLock lock(mLock);
	TransactionMap::iterator itr = mTable.find(key);
	if (itr==mTable.end()) return NULL;
	if (itr->second->deadOrRemoved()) return NULL;
	return (itr->second);
}


void TransactionTable::innerRemove(TransactionMap::iterator itr)
{
	// This should not be called anywhere but from clearDeadEntries.
	LOG(DEBUG) << "removing transaction: " << *(itr->second);
	TransactionEntry *t = itr->second;
	mTable.erase(itr);
	delete t;
}


bool TransactionTable::remove(unsigned key)
{
	// ID==0 is a non-valid special case, and it shouldn't be passed here.
	if (key==0) {
		LOG(ERR) << "called with key==0";
		return false;
	}

	ScopedLock lock(mLock);
	TransactionMap::iterator itr = mTable.find(key);
	if (itr==mTable.end()) return false;
	itr->second->remove();
	return true;
}

bool TransactionTable::removePaging(unsigned key)
{
	// ID==0 is a non-valid special case and should not be passed here.
	assert(key);
	ScopedLock lock(mLock);
	TransactionMap::iterator itr = mTable.find(key);
	if (itr==mTable.end()) return false;
	if (itr->second->removed()) return true;
	if (itr->second->GSMState()!=GSM::Paging) return false;
	//no one to respond to if we're fake
	if (!itr->second->fake()){
		itr->second->MODSendERROR(NULL, 480, "Temporarily Unavailable", true);
	}
	itr->second->remove();
	return true;
}




void TransactionTable::clearDeadEntries()
{
	// Caller should hold mLock.
	TransactionMap::iterator itr = mTable.begin();
	while (itr!=mTable.end()) {
		if (!itr->second->dead()) ++itr;
		else {
			LOG(DEBUG) << "erasing " << itr->first;
			TransactionMap::iterator old = itr;
			itr++;
			innerRemove(old);
		}
	}
}




TransactionEntry* TransactionTable::find(const GSM::LogicalChannel *chan)
{
	LOG(DEBUG) << "by channel: " << *chan << " (" << chan << ")";

	ScopedLock lock(mLock);

	// Yes, it's linear time.
	// Since clearDeadEntries is also linear, do that here, too.
	clearDeadEntries();

	// Brute force search.
	// This search assumes in order by transaction ID.
	TransactionEntry *retVal = NULL;
	for (TransactionMap::iterator itr = mTable.begin(); itr!=mTable.end(); ++itr) {
		if (itr->second->deadOrRemoved()) continue;
		const GSM::LogicalChannel* thisChan = itr->second->channel();
		if ((void*)thisChan != (void*)chan) continue;
		retVal = itr->second;
	}
	//LOG(DEBUG) << "no match for " << *chan << " (" << chan << ")";
	return retVal;
}


TransactionEntry* TransactionTable::findBySACCH(const GSM::SACCHLogicalChannel *chan)
{
	LOG(DEBUG) << "by SACCH: " << *chan << " (" << chan << ")";

	ScopedLock lock(mLock);

	// Yes, it's linear time.
	// Since clearDeadEntries is also linear, do that here, too.
	clearDeadEntries();

	// Brute force search.
	TransactionEntry *retVal = NULL;
	for (TransactionMap::iterator itr = mTable.begin(); itr!=mTable.end(); ++itr) {
		if (itr->second->deadOrRemoved()) continue;
		const GSM::LogicalChannel* thisChan = itr->second->channel();
		if (thisChan->SACCH() != chan) continue;
		retVal = itr->second;
	}
	return retVal;
}


TransactionEntry* TransactionTable::find(GSM::TypeAndOffset desc)
{
	LOG(DEBUG) << "by type and offset: " << desc;

	ScopedLock lock(mLock);

	// Yes, it's linear time.
	// Since clearDeadEntries is also linear, do that here, too.
	clearDeadEntries();

	// Brute force search.
	for (TransactionMap::iterator itr = mTable.begin(); itr!=mTable.end(); ++itr) {
		if (itr->second->deadOrRemoved()) continue;
		const GSM::LogicalChannel* thisChan = itr->second->channel();
		if (thisChan->typeAndOffset()!=desc) continue;
		return itr->second;
	}
	//LOG(DEBUG) << "no match for " << *chan << " (" << chan << ")";
	return NULL;
}


TransactionEntry* TransactionTable::find(const L3MobileIdentity& mobileID, GSM::CallState state)
{
	LOG(DEBUG) << "by ID and state: " << mobileID << " in " << state;

	ScopedLock lock(mLock);

	// Yes, it's linear time.
	// Since clearDeadEntries is also linear, do that here, too.
	clearDeadEntries();

	// Brute force search.
	for (TransactionMap::iterator itr = mTable.begin(); itr!=mTable.end(); ++itr) {
		if (itr->second->deadOrRemoved()) continue;
		if (itr->second->GSMState() != state) continue;
		if (itr->second->subscriber() != mobileID) continue;
		return itr->second;
	}
	return NULL;
}

bool TransactionTable::isBusy(const L3MobileIdentity& mobileID)
{
	LOG(DEBUG) << "id: " << mobileID << "?";

	ScopedLock lock(mLock);

	// Yes, it's linear time.
	// Since clearDeadEntries is also linear, do that here, too.
	clearDeadEntries();

	// Brute force search.
	for (TransactionMap::iterator itr = mTable.begin(); itr!=mTable.end(); ++itr) {
		if (itr->second->deadOrRemoved()) continue;
		if (itr->second->subscriber() != mobileID) continue;
		GSM::L3CMServiceType service = itr->second->service();
		bool speech =
			service==GSM::L3CMServiceType::EmergencyCall ||
			service==GSM::L3CMServiceType::MobileOriginatedCall ||
			service==GSM::L3CMServiceType::MobileTerminatedCall;
		if (!speech) continue;
		// OK, so we found a transaction for this call.
		bool inCall =
			itr->second->GSMState() == GSM::Paging ||
			itr->second->GSMState() == GSM::AnsweredPaging ||
			itr->second->GSMState() == GSM::MOCInitiated ||
			itr->second->GSMState() == GSM::MOCProceeding ||
			itr->second->GSMState() == GSM::MTCConfirmed ||
			itr->second->GSMState() == GSM::CallReceived ||
			itr->second->GSMState() == GSM::CallPresent ||
			itr->second->GSMState() == GSM::ConnectIndication ||
			itr->second->GSMState() == GSM::HandoverInbound ||
			itr->second->GSMState() == GSM::HandoverProgress ||
			itr->second->GSMState() == GSM::HandoverOutbound ||
			itr->second->GSMState() == GSM::Active;
		if (inCall) return true;
	}
	return false;
}




TransactionEntry* TransactionTable::find(const L3MobileIdentity& mobileID, const char* callID)
{
	assert(callID);
	LOG(DEBUG) << "by ID and call-ID: " << mobileID << ", call " << callID;

	string callIDString = string(callID);
	// Yes, it's linear time.
	// Since clearDeadEntries is also linear, do that here, too.
	clearDeadEntries();

	// Brute force search.
	ScopedLock lock(mLock);
	for (TransactionMap::iterator itr = mTable.begin(); itr!=mTable.end(); ++itr) {
		if (itr->second->deadOrRemoved()) continue;
		if (itr->second->mSIP.callID() != callIDString) continue;
		if (itr->second->subscriber() != mobileID) continue;
		return itr->second;
	}
	return NULL;
}


TransactionEntry* TransactionTable::find(const L3MobileIdentity& mobileID, unsigned transactionID)
{
	LOG(DEBUG) << "by ID and transaction-ID: " << mobileID << ", transaction " << transactionID;

	// Yes, it's linear time.
	// Since clearDeadEntries is also linear, do that here, too.
	clearDeadEntries();

	// Brute force search.
	ScopedLock lock(mLock);
	for (TransactionMap::iterator itr = mTable.begin(); itr!=mTable.end(); ++itr) {
		if (itr->second->deadOrRemoved()) continue;
		if (itr->second->subscriber() != mobileID) continue;
		return itr->second;
	}
	return NULL;
}


TransactionEntry* TransactionTable::answeredPaging(const L3MobileIdentity& mobileID)
{
	// Yes, it's linear time.
	// Even in a 6-ARFCN system, it should rarely be more than a dozen entries.

	ScopedLock lock(mLock);

	// Since clearDeadEntries is also linear, do that here, too.
	clearDeadEntries();

	// Brute force search.
	for (TransactionMap::iterator itr = mTable.begin(); itr!=mTable.end(); ++itr) {
		if (itr->second->deadOrRemoved()) continue;
		if (itr->second->GSMState() != GSM::Paging) continue;
		if (itr->second->subscriber() == mobileID) {
			// Stop T3113 and change the state.
			itr->second->GSMState(AnsweredPaging);
			itr->second->resetTimer("3113");
			return itr->second;
		}
	}
	return NULL;
}


GSM::LogicalChannel* TransactionTable::findChannel(const L3MobileIdentity& mobileID)
{
	// Yes, it's linear time.
	// Even in a 6-ARFCN system, it should rarely be more than a dozen entries.

	ScopedLock lock(mLock);

	// Since clearDeadEntries is also linear, do that here, too.
	clearDeadEntries();

	// Brute force search.
	for (TransactionMap::iterator itr = mTable.begin(); itr!=mTable.end(); ++itr) {
		if (itr->second->deadOrRemoved()) continue;
		if (itr->second->subscriber() != mobileID) continue;
		GSM::LogicalChannel* chan = itr->second->channel();
		if (!chan) continue;
		if (chan->type() == FACCHType) return chan;
		if (chan->type() == SDCCHType) return chan;
	}
	return NULL;
}


unsigned TransactionTable::countChan(const GSM::LogicalChannel* chan)
{
	ScopedLock lock(mLock);
	clearDeadEntries();
	unsigned count = 0;
	for (TransactionMap::iterator itr = mTable.begin(); itr!=mTable.end(); ++itr) {
		if (itr->second->deadOrRemoved()) continue;
		if (itr->second->channel() == chan) count++;
	}
	return count;
}



size_t TransactionTable::dump(ostream& os, bool showAll) const
{
	ScopedLock lock(mLock);
	size_t sz = 0;
	for (TransactionMap::const_iterator itr = mTable.begin(); itr!=mTable.end(); ++itr) {
		if ((!showAll) && itr->second->deadOrRemoved()) continue;
		sz++;
		os << *(itr->second) << endl;
	}
	return sz;
}


TransactionEntry* TransactionTable::findLongestCall()
{
	ScopedLock lock(mLock);
	clearDeadEntries();
	long longTime = 0;
	TransactionMap::iterator longCall = mTable.end();
	for (TransactionMap::iterator itr = mTable.begin(); itr!=mTable.end(); ++itr) {
		if (itr->second->deadOrRemoved()) continue;
		if (!(itr->second->channel())) continue;
		if (itr->second->service() == GSM::L3CMServiceType::EmergencyCall) continue;
		if (itr->second->GSMState() != GSM::Active) continue;
		long runTime = itr->second->stateAge();
		if (runTime > longTime) {
			runTime = longTime;
			longCall = itr;
		}
	}
	if (longCall == mTable.end()) return NULL;
	return longCall->second;
}

/* linear, we should move the actual search into this structure */
bool TransactionTable::RTPAvailable(short rtpPort)
{
	ScopedLock lock(mLock);
	clearDeadEntries();
	bool avail = true;
	for (TransactionMap::iterator itr = mTable.begin(); itr!=mTable.end(); ++itr) {
		if (itr->second->deadOrRemoved()) continue;
		if (itr->second->mSIP.RTPPort() == rtpPort){
			avail = false;
			break;
		}
	}
	return avail;
}

bool TransactionTable::duplicateMessage(const GSM::L3MobileIdentity& mobileID, const std::string& wMessage)
{

	ScopedLock lock(mLock);

	// Since clearDeadEntries is also linear, do that here, too.
	clearDeadEntries();

	// Brute force search.
	for (TransactionMap::const_iterator itr = mTable.begin(); itr!=mTable.end(); ++itr) {
		if (itr->second->deadOrRemoved()) continue;
		if (itr->second->subscriber() != mobileID) continue;
		if (itr->second->message() == wMessage) return true;
	}
	return false;

}

// vim: ts=4 sw=4
