//////////////////////////////////////////////////////////////////////////////
//File Name: LSSessionManager.cxx
//Description: Manages the peer to peer connection between two LSs.
//This class can run either as a thread or a standalone application.
//OPEN messages are listened for on port 6069.
//////////////////////////////////////////////////////////////////////////////

#include <unistd.h>
#include <iostream>
#include <signal.h>
#include <algorithm>
#include <stdlib.h>
#include <stdio.h> 
#include <strstream.h>
#include "LSSessionManager.hxx"
#include "ProvisionInterface.hxx"
#include "LSSendOnlySession.hxx"
#include "LSReceiveOnlySession.hxx"
#include "LSSendReceiveSession.hxx"
#include "TcpServerSocket.hxx"
#include "TRIPOpenMsg.hxx"
#include "TRIPUpdateMsg.hxx"
#include "TRIPAuthAttr.hxx"
#include "TRIPAuthData.hxx"
#include "TRIPTransceiver.hxx"
#include "TRIPTransCapability.hxx"
#include "TRIBAgent.hxx"
#include "OutgoingPolicyAgent.hxx"
#include "IncomingPolicyAgent.hxx"
#include "DecisionAgent.hxx"
#include "DecisionProxyAgent.hxx"
#include "LSApiServer.hxx"
#include "LSRouteInterface.hxx"
#include "LSUtils.hxx"
#include "cpLog.h"

///////////////////////////////////////////////////////////////////////////////
//METHOD DEFINITIONS
///////////////////////////////////////////////////////////////////////////////
static const int LS_PORT = 6069;
static const int LS_API_DEF_PORT = 8000;
LSSessionManager* LSSessionManager::_instance = 0;
u_int32_t LSSessionManager::MinSeqNumber = 1;
u_int32_t LSSessionManager::MaxSeqNumber = UINT_MAX;
//-----------------------------------------------------------------------------
extern "C" {
  void sigAlrm(int sig){   
  }
  void LSSessionManager_destroy(void){
    LSSessionManager::destroy();
  }
};
//-----------------------------------------------------------------------------
LSSessionManager& LSSessionManager::instance(){
  if (!_instance){
    _instance = new LSSessionManager();
  }
  return (*_instance);
}
//-----------------------------------------------------------------------------
void LSSessionManager::destroy(void){
  //Clean-up rest of the Singeltons also.
  ProvisionInterface::destroy();
  DecisionAgent::destroy();
  OutgoingPolicyAgent::destroy();
  DecisionProxyAgent::destroy();
  TRIBAgent::destroy();

  delete LSSessionManager::_instance;
  LSSessionManager::_instance = 0;
}
//-----------------------------------------------------------------------------
LSSessionManager::LSSessionManager(){
}
//-----------------------------------------------------------------------------
void LSSessionManager::processOpenRequest(Connection& conn) throw (VNetworkException&, TRIPBadDataException&){
  string destIp = conn.getIp();
  cpLog(LOG_DEBUG, "processOpenRequest from IP:%s", destIp.c_str());
  Sptr<LSPeerSession> pSession = locateExistingPeerSession(destIp);
  if(pSession.getPtr() != 0){ 
    cpLog(LOG_DEBUG, "Found existing PeerSession for (%s)", destIp.c_str());
    pSession->handleIncomingConnection(conn);
    pSession->advertiseRoutes();
  }
  else{
    cpLog(LOG_DEBUG, "Creating new PeerSession for (%s)", destIp.c_str());
    NetworkAddress addr(conn.getIp(), LS_PORT);
    pSession = createSession(addr);
    lockSessionList();
    _sessionList.push_back(pSession);
    unlockSessionList();
    pSession->handleIncomingConnection(conn);
    pSession->start();
    pSession->advertiseRoutes();
  }
  ostrstream oStr;
  oStr << *pSession << ends;
  cpLog(LOG_DEBUG, "Session initiation (%s)", oStr.str());
  oStr.freeze(false);
}
//-----------------------------------------------------------------------------
Sptr<LSPeerSession>
LSSessionManager::createSession(const NetworkAddress& addr){
  Sptr<LSPeerSession> retSession=0;
  switch(_transMode){
  case TRIP_TRANS_SND:
    retSession  = new LSSendOnlySession(_tripItadKey.getItad(), 
					_tripItadKey.getTripId(), 
					addr);
    break;
        case TRIP_TRANS_RCV:
            retSession  = new LSReceiveOnlySession(_tripItadKey.getItad(), 
						   _tripItadKey.getTripId(), 
						   addr);
	    break;
  default:
    retSession  = new LSSendReceiveSession(_tripItadKey.getItad(), 
					   _tripItadKey.getTripId(), 
					   addr);
    break;
  }
  return retSession;
}
//-----------------------------------------------------------------------------
void LSSessionManager::setAuthMech(string am){
   if(am==""){
    //DSA is default.
    _authMech = '1';
  }
  else{
    if(am=="DSA"){
      _authMech = '1';
    }
    else{
      _authMech = '2';
    }
  }
  cout << "Authentication mechanism for this LS is: " << _authMech << endl;

}
//-----------------------------------------------------------------------------
void LSSessionManager::init(string thePS){
  cpLog(LOG_DEBUG, "Initialising LSSessionManager...");

  //Because you have not yet injected any local routes.
  _injected = false;
  
  //Set the name of the ProvisionServer.
  _ps = thePS;
  cout << "LS has PS: " << _ps << endl;
  
  //Instantiate the instance of Provision Interface to extract all of the 
  //information the the PS has to offer.
  cout << "Extracting information from PS..." << endl;
  ProvisionInterface& provisionInterface = ProvisionInterface::instance();
  cpLog(LOG_DEBUG, "Parsing all configuartion files from PS...");
  provisionInterface.parseConfig(_ps);
  cpLog(LOG_DEBUG, "Getting capability...");
  string tMode = provisionInterface.getCapability();
  if(tMode=="send"){
    _transMode = TRIP_TRANS_SND;  
  }
  else if(tMode=="rec"){
    _transMode = TRIP_TRANS_RCV;  
  }
  else{
    _transMode = TRIP_TRANS_SND_RCV;  
  }
  cout << "Capability: " << _transMode << endl;
  char buf[256];
  gethostname(buf, 256);
  NetworkAddress na(buf);
  cpLog(LOG_DEBUG, "Getting ITAD...");
  _itad = provisionInterface.getITAD();
  _localIp = na.getIpName();
  printf("naip4: %x\n", na.getIp4Address());
  _tripItadKey = TRIPItadKey(_itad, TRIPId(na.getIp4Address ()), _localIp); 
  cout << "ITAD: " << _itad << endl;
  cpLog(LOG_DEBUG, "Getting local IP address...");
  cout << "Local IP: " << _localIp << endl;

  //Initialising the TRIB database.
  Sptr <TRIPItadKey > itadkey = new TRIPItadKey(_itad, TRIPId(na.getIp4Address ()));
  TRIBAgent& tribagent = TRIBAgent::instance();
  tribagent.createAdjTribIn(*itadkey, TC_INTERNAL);
  tribagent.createAdjTribOut(*itadkey, TC_EXTERNAL);
  
  //Set the topology for the LS.
  cpLog(LOG_DEBUG, "Creating LSTopology...");
  _topology = new LSTopology(getTripId());
  
  //Set the port of the server socket to listen for messages.
  cpLog(LOG_DEBUG, "Opening server socket for listening...");
  _serverSocket = new TcpServerSocket(LS_PORT);
  
  //Set the signal alarm.
  signal(SIGALRM, sigAlrm);
  
  //Initialise the sequence number and transaction ID.
  _seqNum = LSSessionManager::MinSeqNumber;
  _transactionId = 0;

  
  //Start the LSApiServer.
  cpLog(LOG_DEBUG, "Starting LSApiServer...");
  _localPort = provisionInterface.getPort();
  LSApiServer::instance(_localPort);
  cout << "LSApiServer running on port: " << _localPort << endl;

  //Register for callBack.
  IncomingPolicyAgent::instance().registerForFlooding(LSSessionManager::floodRoutesCB);
  DecisionAgent::instance().registerForFlooding(LSSessionManager::floodRoutesCB);
  //Initialising the address family and the protocols supported by
  //the LS.
  cpLog(LOG_DEBUG, "Getting list of address family and protocols supported...");
  vector<string> _theProtocols = provisionInterface.getProtocolList();
  vector<string> _theAddrFamily = provisionInterface.getAddrFamilyList();
  
  int numP = provisionInterface.getTotalProtocols();
  int numA = provisionInterface.getTotalAddrFamily();
  
  cout << "Total number of protocols supported: " << numP << endl;
  for(int i=0; i<numP; i++){
    if(_theProtocols[i]=="SIP"){
      cout << "Protocol: " << _theProtocols[i] <<endl;
      _protoCapabilityList.push_back(TRIP_APP_PROTO_SIP);
    }//End if.
    else if(_theProtocols[i]=="H.323-Q921"){
      cout << "Protocol: " << _theProtocols[i] <<endl;
      _protoCapabilityList.push_back(TRIP_APP_PROTO_H323_Q921);
    }//End else.
    else if(_theProtocols[i]=="H.323-ANNEX-G"){
      cout << "Protocol: " << _theProtocols[i] <<endl;
      _protoCapabilityList.push_back(TRIP_APP_PROTO_H323_ANNEX_G);
    }//End else.
    else if(_theProtocols[i]=="H.323-RAS"){
      cout << "Protocol: " << _theProtocols[i] <<endl;
      _protoCapabilityList.push_back(TRIP_APP_PROTO_H323_RAS);
    }//End else.
  }//End of for.

  cout << "Total number of address families supported: " << numA << endl;  
  for(int i=0; i<numA; i++){
    if(_theAddrFamily[i]=="POTS"){
      cout << "Address Family: " << _theAddrFamily[i] <<endl;
      _addrFamilyList.push_back(TRIP_ADDR_POTS);
    }
    else if(_theAddrFamily[i]=="ROUTING NUM"){
      cout << "Address Family: " << _theAddrFamily[i] <<endl;
      _addrFamilyList.push_back(TRIP_ADDR_ROUTING_NUM);
    }
  }//End of for
  
  //Find out all of the LSs out there and initiate a session.
  cpLog(LOG_DEBUG, "Creating peering sessions will all of the LSs out there...");
  _lsLocator.init();
  const LSLocator::LSList& lsList = _lsLocator.getLSList();
  for(LSLocator::LSList::const_iterator itr = lsList.begin();
      itr != lsList.end(); itr++){
    NetworkAddress addr((*itr));
    Sptr<LSPeerSession> sSession = createSession(addr);
    lockSessionList();
    _sessionList.push_back(sSession);
    unlockSessionList();
    sSession->start();
    sSession->advertiseRoutes();
  }//End of for loop.

   IncomingPolicyAgent::instance().init();

   cpLog(LOG_DEBUG, "Initialisation of LSSessionManager complete.");
}
//-----------------------------------------------------------------------------
LSSessionManager::~LSSessionManager(){
  if(_thread.getPtr() != 0){
    _thread->exit();
  }
}
//-----------------------------------------------------------------------------
void LSSessionManager::run(){
  ///If threadRun is already running, call to run
  //should be discarded.
  if(_thread.getPtr() == 0){
    processMsg();
  }
  else{
    cpLog(LOG_ALERT, "Already processing messages in separate thread");
    assert(0);
  }
}
//-----------------------------------------------------------------------------
void LSSessionManager::threadRun(){
  _thread = new VThread(); 
  _thread->spawn(LSSessionManager::threadRunImpl, this);
} 
//-----------------------------------------------------------------------------
void* LSSessionManager::threadRunImpl(void* args){
  LSSessionManager* self = static_cast<LSSessionManager*>(args);
  self->processMsg();
  return (NULL);
} 
//-----------------------------------------------------------------------------
void LSSessionManager::processMsg(){
  for (; ; ){
    cout << "Accepting connections" << endl;
    Connection conn;
    _serverSocket->accept(conn);
    cout << "serverSocket connection accepted." << endl;

    try{
      if(!_lsLocator.isTrustedLs(conn.getIp())){
	cpLog(LOG_DEBUG, "LS is not trusted, ignoring the connection");
	conn.close();
	continue;
      }

      processOpenRequest(conn);

      //If there are any local routes to inject, inject them.
      ProvisionInterface& pi = ProvisionInterface::instance();
      bool gotRoutes = pi.hasLocRoutes();

      if(gotRoutes){
        cpLog(LOG_DEBUG, "There are routes that must be injected.");
        int numR = pi.getTotalRoutes();
        vector<string> lrRouteAddr = pi.getLocRouteAddr();
        vector<string> lrProto = pi.getLocRouteProtocols();
        vector<string> lrPrefix = pi.getLocRoutePrefixes();
        vector<string> lrNext = pi.getLocRouteNext();
        vector<string> lrNextPort = pi.getLocRouteNextPort();
        vector<string> lrNextITAD = pi.getLocRouteNextITAD();
        DecisionProxyAgent& dpa = DecisionProxyAgent::instance(); 
        
        cout << "Total number of local routes to inject: " << numR << endl;
          
        //Create the routes for UPDATE.
        for(int i=0;i<numR; i++){
          string s1 = lrRouteAddr[i];
          string s2 = lrProto[i];
          string s3 = lrPrefix[i];
          string s4 = lrNext[i];
          string s5 = lrNextPort[i];
          string s6 = lrNextITAD[i];
          cout << "Address Family: " << s1 <<endl; 
          cout << "Protocol: " << s2 <<endl; 
          cout << "Prefix: " << s3 <<endl; 
          cout << "Next Hop: " << s4 <<endl; 
          cout << "Next Port: " << s5 <<endl; 
          int itadValue = atoi(s6.c_str());
          int prefixInt = atoi(s3.c_str());
          cout << "ITAD: " << itadValue <<endl; 
          TRIPData nhData(s4);
          cout << "Local IP: " << _localIp << endl;
			  	    
          if(s1=="POTS"){
            if(s2=="SIP"){
                Sptr<TRIPWithdrawnRoute> withdrawnRoute = new TRIPWithdrawnRoute(TRIP_FLG_LINK_STATE, LSSessionManager::instance().getTripId(), LSSessionManager::instance().getSeqNumber());
                Sptr<TRIPReachableRoute> reachableRoute = 
                    new TRIPReachableRoute(TRIP_FLG_LINK_STATE, LSSessionManager::instance().getTripId(), LSSessionManager::instance().getSeqNumber());
                TRIPRoutes tripRoutes(TRIP_ADDR_POTS, 
                                    TRIP_APP_PROTO_SIP,
                                    TRIPData(s3));	
              *reachableRoute << tripRoutes;
              Sptr<TRIPUpdateMsg> uMsg = new TRIPUpdateMsg();

		  char buf[256];
	      sprintf(buf, "%d", prefixInt);
		  TRIPRoutes tRoute6(TRIP_ADDR_POTS, 
						   TRIP_APP_PROTO_SIP,
						   TRIPData(buf));
		*reachableRoute << tRoute6;
              
              uMsg->addWithdrawnRoute(withdrawnRoute);
              uMsg->addReachableRoute(reachableRoute);


	      TRIPItadPathSegment seg1(TRIP_SEG_AP_SET, itadValue);
	      Sptr<TRIPAdvertisePath> aPath = new TRIPAdvertisePath();
	      *aPath << seg1;
	      
	      TRIPItadPathSegment seg2(TRIP_SEG_AP_SET, itadValue);
	      Sptr<TRIPRoutedPath> aPath2 = new TRIPRoutedPath();
	      *aPath2 << seg2;
	      
	      uMsg->addAttribute(aPath); 
	      uMsg->addAttribute(aPath2);  

              int q = 1;


              Sptr<TRIPNextHopServer> nServer = 
                new TRIPNextHopServer(q, itadValue, 
                                      TRIPData(_localIp.c_str()));
	      string nhStr = nhData.str();

              q = pi.getPref();
              
              Sptr<TRIPLocalPref> lp = new TRIPLocalPref(q);

              uMsg->addAttribute(nServer);
              uMsg->addAttribute(lp);

              Sptr<TRIPUpdateEvent> uEvent = new TRIPUpdateEvent(uMsg, LSSessionManager::instance().getTripItadKey(),TRIP_UPDATE_MSG);
              cpLog(LOG_DEBUG, "Injecting local routes...");
              dpa.addToQueue(uEvent);

            }
          }//End if for POTS and SIP.
          else if(s1=="ROUTING NUM"){
            if(s2=="SIP"){
              Sptr<TRIPReachableRoute> reachableRoute = 
                new TRIPReachableRoute(TRIP_FLG_LINK_STATE, 
                                       LSSessionManager::instance().getTripId(), 
                                       LSSessionManager::instance().getSeqNumber()); 
              TRIPRoutes tripRoutes(TRIP_ADDR_ROUTING_NUM, 
                                    TRIP_APP_PROTO_SIP,
				    TRIPData(s3));	
              *reachableRoute << tripRoutes;
              Sptr<TRIPUpdateMsg> uMsg = new TRIPUpdateMsg();
              
              uMsg->addReachableRoute(reachableRoute);
              Sptr<TRIPNextHopServer> nServer = 
                new TRIPNextHopServer(itadValue, 
                                      TRIPData(_localIp.c_str()));
              uMsg -> addAttribute(nServer);

              
              Sptr<TRIPUpdateEvent> uEvent = 
                new TRIPUpdateEvent(uMsg, 
                                    LSSessionManager::instance().getTripItadKey(),
                                    TRIP_UPDATE_MSG);
              dpa.addToQueue(uEvent);
              cpLog(LOG_DEBUG, "Injecting local routes...");
            }
          }//End if for ROUTING NUM and SIP.
        }//End of for.

      }//End if for whether or not there are routes to inject.
    
    }//End of try.
    catch(TRIPBadDataException& e){
      cpLog(LOG_ALERT, "Bad open message, reason:%s",
	    e.getDescription().c_str());
    }//End of catch.
    catch(VException& e){
      cpLog(LOG_ALERT, "Caught exception, reason:%s",
	    e.getDescription().c_str());
    }//End of catch.

  }//End of continuous for-loop.

}//End of method.
//-----------------------------------------------------------------------------
ostream&
operator<<(ostream& os, const LSSessionManager& sessionManager){
  os << "LS Session list:" << endl;
  sessionManager.lockSessionList();
  for(LSPeerList::const_iterator itr = sessionManager._sessionList.begin(); 
      itr != sessionManager._sessionList.end(); itr++){
    os << *(*itr) << endl;
  }
  sessionManager.unlockSessionList();
  return os;
}
//-----------------------------------------------------------------------------
Sptr<LSPeerSession> 
LSSessionManager::locateExistingPeerSession(const string& destIp){
  cpLog(LOG_DEBUG, "LSSessionManager::locateExistingPeerSession");
  Sptr<LSPeerSession> retVal;
  lockSessionList();
  for(LSPeerList::const_iterator itr = _sessionList.begin(); 
              itr != _sessionList.end(); itr++){
    if((*itr)->peerExists(destIp)){
      retVal = (*itr);
      break;
    }
  }
  unlockSessionList();
  return retVal;
}
//-----------------------------------------------------------------------------
bool 
LSSessionManager::locateExistingPeerSession(u_int16_t itad, const TRIPId& tripId){
  bool retVal = false;
  lockSessionList();
  for(LSPeerList::const_iterator itr = _sessionList.begin(); 
      itr != _sessionList.end(); itr++){
    if((*itr)->peerExists(itad, tripId)){
      retVal = true;
      break;
    }
  }
  unlockSessionList();
  return retVal;
}
//-----------------------------------------------------------------------------
bool
LSSessionManager::externalSessionExists() const{
  bool retVal = false;
  lockSessionList();
  for(LSPeerList::const_iterator itr = _sessionList.begin();
      itr != _sessionList.end(); itr++){
    if ((*itr)->isEstablished() && (!((*itr)->isPeerInternal()))){
      retVal = true;
      break;
    }
  }
  unlockSessionList();
  return retVal;
}
//-----------------------------------------------------------------------------
void 
LSSessionManager::floodRoutesCB(Sptr <TRIPWorkSet> workSet,
				TRIPCallbackReason reason, 
				const TRIPItadKey& changedSrc){
  LSSessionManager::instance().floodRoutes(workSet, reason, changedSrc);
}
//-----------------------------------------------------------------------------
void LSSessionManager::floodRoutes(Sptr <TRIPWorkSet> workSet, 
				   TRIPCallbackReason reason, 
				   const TRIPItadKey& changedSrc){
  bool isExternal = false;
  if(reason == TRIP_CB_EXT_TRIB_UPDATE){
    isExternal = true;
    cpLog(LOG_DEBUG, "Flooding routes as (Ext-trib) changed");
  }
  else{
    cpLog(LOG_DEBUG, "Flooding routes as (%s) changed", 
	  changedSrc.getDescription().c_str());
  }
  
  if (!(workSet.getPtr())){
    assert(0);
  }
  const RouteSetMap& workSetMap = workSet->getRouteSet();
  if (workSetMap.size() == 0){
    return;
  }
  try{
    TRIPRouteSet routeSet;
    for (RouteSetMap::const_iterator wItr = workSetMap.begin();
	 wItr != workSetMap.end(); wItr++){
      routeSet = (*wItr).second;
      Sptr <UpdateMsgList> msgList = getFloodMsgList(routeSet, false,
						     isExternal);
      assert(msgList.getPtr());
      
      for( LSPeerList::iterator itr = _sessionList.begin();
	   itr != _sessionList.end(); itr++){
	Sptr<LSPeerSession> session = (*itr);
	
	//If session is not in the establised state, ignore
	if(!session->isEstablished()) continue;
	
	//Only send update to peers that has not caused the flooding
	//if external then send to all internal peers.
	if(session->isPeerInternal()){
	  session->sendUpdateMsgList(msgList);
	  session->advertiseRoutes();
          
	}
      }
    }
  }
  catch(VMissingDataException& e){
    cpLog(LOG_DEBUG, "(%s)", e.getDescription().c_str());
  }
}
//-----------------------------------------------------------------------------
Sptr <UpdateMsgList>
LSSessionManager::getFloodMsgList(const TRIPRouteSet& floodSet, 
				  bool initialFlood, 
				  bool isExternal){
  if (floodSet.size() == 0){
    return 0;
  }
  u_int8_t flg = 0;
  flg |= TRIP_FLG_LINK_STATE;
  TRIPId id;
  TRIPId localTripId = LSSessionManager::instance().getTripId();
  TRIPItadKey itadKey = LSSessionManager::instance().getTripItadKey();
  u_int32_t seq = 0;
  u_int32_t prevTranId = 0;
  bool msgToSend = false;
  bool withdrawn = false;
  int trip_id = id.getId();
  stringstream ss;
  ss << trip_id;
  string s_id = "" + ss.str();
  Sptr <UpdateMsgList> retList = new UpdateMsgList();
  Sptr<TRIPUpdateMsg> uMsg;
  Sptr<TRIPWithdrawnRoute> wRoute;
  Sptr<TRIPReachableRoute> rRoute;
  Sptr<TRIPRouteElement> route;

  
  if ( cpLogGetPriority() >= LOG_DEBUG ){
    for(TRIPRouteSet::iterator iitr = floodSet.begin();
	iitr != floodSet.end(); iitr++){
      (*iitr)->prettyDump(cerr);
    }
  }
  for(TRIPRouteSet::iterator itr = floodSet.begin();
      itr != floodSet.end(); itr++){
    withdrawn = false;
    if ((*itr)->isWithdrawn()){
      withdrawn = true;
    }
    if (initialFlood){
      if (withdrawn) continue;
    }
    if (((*itr)->getTransactionId() == prevTranId) && (msgToSend)){
      TRIPRoutes tRoute((*itr)->getAddrFamily(), 
			(*itr)->getAppProtocol(),                             
			TRIPData((*itr)->getPrefix()));
      if (withdrawn){
	*wRoute << tRoute;
      }
      else{
	*rRoute << tRoute;
      }
    }
    else{
      prevTranId = (*itr)->getTransactionId();
      if (msgToSend){
	uMsg = new TRIPUpdateMsg(wRoute, rRoute, route->getAttributes());
	retList->push_back(uMsg);
      }
      
      if (initialFlood){
	id = (*itr)->getOriginator().getTripId();
	seq = (*itr)->getSeqNumber();
      }
      else{
	if (isExternal){
	  id = localTripId;
	}
	else{
	  id = (*itr)->getOriginator().getTripId();
	}
	seq = getNextSeqNumber(isExternal, (*itr));
      }
      wRoute = new TRIPWithdrawnRoute(flg, id, seq);
      rRoute = new TRIPReachableRoute(flg, id, seq);
      route = (*itr);
      msgToSend = true;
      TRIPRoutes tRoute((*itr)->getAddrFamily(), 
			(*itr)->getAppProtocol(),         
			TRIPData((*itr)->getPrefix()));
      if (withdrawn){
	*wRoute << tRoute;
      }
      else{
	*rRoute << tRoute;
      }
    }
    if (!(initialFlood)){
      (*itr)->unsetChanged(T_FLOOD_BIT);
      (*itr)->setSeqNumber(seq);
      if(isExternal) (*itr)->setOriginator(itadKey);
    }
  }
  if (route.getPtr()){
    uMsg = new TRIPUpdateMsg(wRoute, rRoute, route->getAttributes());
    retList->push_back(uMsg);
  }
  cpLog(LOG_DEBUG, "getFloodMsgList - Number of flood msgs is (%d) ",
	retList->size());
  return retList;
}
//-----------------------------------------------------------------------------
Sptr<TRIPUpdateMsg>
LSSessionManager::getNextMsgToFlood(const TRIPTrieCache& cache, 
				    bool startFlooding){
    Sptr<TRIPUpdateMsg> retUMsg = 0;
    Sptr<TRIPRouteElement> element = 0;
    Sptr<TRIPRouteSet> wSet = 0;
    Sptr<TRIPRouteSet> rSet = 0;
    
    TRIPId id = LSSessionManager::instance().getTripId();
    TRIPItadKey itadKey = LSSessionManager::instance().getTripItadKey();
    u_int8_t flg =  TRIP_FLG_LINK_STATE;
    u_int32_t seqNum = 0;
    int trip_id = id.getId();
    stringstream ss;
    ss << trip_id;
    string s_id = "" + ss.str();
    
    retUMsg = new TRIPUpdateMsg;
    TRIPProtoKey key(TRIP_APP_PROTO_SIP, TRIP_ADDR_POTS);
    wSet = cache.getWithdrawnRoutesChangedNeedsFlooding(key); 
    if((wSet.getPtr() != 0) && wSet->size()){
      
      //Withdrawn routes found,get the first route element and query
      //for reachable routes for the same transactionId.
      element = *(wSet->begin());
      u_int32_t trId = element->getTransactionId();
      seqNum = getNextSeqNumber(startFlooding, element);
      rSet = cache.getReachableRoutesChangedNeedsFlooding(trId, key);
    }
    else{
      //No withdrawn routes found, however check if there are any reachable
      //routes that needs to be put in the UPDATE message.
      rSet = cache.getReachableRoutesChangedNeedsFlooding(key) ;
      if((rSet.getPtr() != 0) && rSet->size()){
	element = *(rSet->begin());
	seqNum = getNextSeqNumber(startFlooding, element);
      }
    }
    Sptr<TRIPWithdrawnRoute> wRoute = new TRIPWithdrawnRoute(flg, id, seqNum);
    Sptr<TRIPReachableRoute> rRoute = new TRIPReachableRoute(flg, id, seqNum);
    processRoutes(wRoute, wSet, itadKey, seqNum, startFlooding); 
    processRoutes(rRoute, rSet, itadKey, seqNum, startFlooding);
    retUMsg->addWithdrawnRoute(wRoute);
    retUMsg->addReachableRoute(rRoute);
       
    //If we have created the UPDATE message add the rest of the 
    //attribute to the UPDATE message.
    assert(element.getPtr());
    const AttributeSet& aSet = element->getAttributes();
    for(AttributeSet::const_iterator itr = aSet.begin();
	itr != aSet.end(); itr++){
      retUMsg->addAttribute((*itr));
    }


    return retUMsg;
}
//----------------------------------------------------------------------------
void
LSSessionManager::processRoutes(Sptr<TRIPRouteContainer> routeContainer,
				Sptr<TRIPRouteSet> rSet, 
				const TRIPItadKey& itadKey, 
				u_int32_t seqNum, bool startFlooding){
  if((rSet.getPtr() == 0 ) || (rSet->size() == 0)) 
   return;

  //Iterate throgh each route element and make it part of the withdrawn route.
  for(TRIPRouteSet::iterator itr = rSet->begin(); 
      itr != rSet->end(); itr++){
    TRIPRoutes tRoute((*itr)->getAddrFamily(), (*itr)->getAppProtocol(),
		      TRIPData((*itr)->getPrefix()));
    *routeContainer << tRoute;
    (*itr)->unsetChanged(T_FLOOD_BIT);
    (*itr)->setSeqNumber(seqNum);
    if(startFlooding) (*itr)->setOriginator(itadKey);
  }
}
//----------------------------------------------------------------------------
u_int32_t
LSSessionManager::getNextSeqNumber(bool startFlooding, 
				   Sptr<TRIPRouteElement> data){
  //If flooding is being startFlooding.
  //If the Originator of given route element is the same same this
  //LS, increment the seqNumber.
  u_int32_t seqNum;
  if(startFlooding){
    //Originating a new flooding, get a new sequence number
    seqNum = LSSessionManager::instance().getSeqNumber();
  }
  else{
    //If it is previous originator of the route then increment the
    //seq. number 
    seqNum = data->getSeqNumber();
    if(data->getOriginator() == getTripItadKey()){
      seqNum++;
    }
  }
  return seqNum;
}
//----------------------------------------------------------------------------
void LSSessionManager::sendTopologyUpdateOmit(const TRIPItadKey& itadKey, 
					      Sptr <TRIPUpdateMsg> uMsg ){
  if (uMsg.getPtr()){
    lockSessionList();
    for(LSPeerList::const_iterator itr = _sessionList.begin(); 
	itr != _sessionList.end(); itr++){
      if(itadKey == (*itr)->getPeerItadKey()) continue;
      if((*itr)->isEstablished() && (*itr)->isPeerInternal()){
	(*itr)->sendUpdate(*uMsg);
      }
    }
    unlockSessionList();
  }
}
//----------------------------------------------------------------------------
void LSSessionManager::clearAdjTribIn(const TRIPItadKey& itadKey){
  cpLog(LOG_DEBUG, "LSSessionManager::clearAdjTribIn");
  //If Peer is an internal peer need to send topology updates to all 
  //internal peers.
  if(itadKey.isInternal()){
    //Remove it from the topology set
    LSConnectionList removeList;
    _topology->removeConnection(itadKey.getTripId(), removeList);
    //Dump the _topology.
    ostrstream oStr;
    _topology->dump(oStr);
    oStr << ends;
    cpLog(LOG_INFO, "TOPOLOGY: %s", oStr.str());
    oStr.freeze(false);
    
    if(removeList.size()){
      //Remove the Adj-TRIB-Ins.
      for(LSConnectionList::iterator itr = removeList.begin();
	  itr != removeList.end(); itr++){
	TRIPItadKey key(getItad(), (*itr));
	cpLog(LOG_DEBUG, "Destroying Adj-trib-in for (%s)",
	      key.getDescription().c_str());
	TRIBAgent::instance().destroyAdjTribIn(key);
      }
    }
    Sptr <LSConnectionList> cList = _topology->getConnectionList(getTripId());
    if (cList.getPtr()){
      Sptr<TRIPUpdateMsg> uMsg = createTopologyUpdateMsg();
      sendTopologyUpdateOmit(itadKey, uMsg);
    }
  }
  else{
    Sptr<TRIPUpdateEvent> event = new TRIPUpdateEvent(0, 
						      itadKey, 
						      TRIP_CONNECTION_LOST);
    DecisionProxyAgent::instance().addToQueue(event);
    TRIBAgent::instance().destroyAdjTribOut(itadKey);
  }
}
//----------------------------------------------------------------------------
Sptr<TRIPUpdateMsg>
LSSessionManager::createTopologyUpdateMsg(Sptr<TRIPItadTopology> topologyAttr){
  Sptr<TRIPUpdateMsg> uMsg = 0;
  Sptr<LSConnectionList> cList;
  if (!(topologyAttr.getPtr())){
    cList = _topology->getConnectionList(getTripId());
    if (!(cList.getPtr())){
      return uMsg;
    }
  }

  //Create an UPDATE message with Topology attribute to send to
  //rest of the internal peer sessions.
  uMsg = new TRIPUpdateMsg();
  TRIPId id = getTripId();
  int trip_id = id.getId();
  stringstream ss;
  ss << trip_id;
  string s_id = "" + ss.str();
  Sptr<TRIPWithdrawnRoute> wRoute =
    new TRIPWithdrawnRoute(TRIP_FLG_LINK_STATE, id, 0);
  Sptr<TRIPReachableRoute> rRoute =
      new TRIPReachableRoute(TRIP_FLG_LINK_STATE, id, 0);
  uMsg->addWithdrawnRoute(wRoute);
  uMsg->addReachableRoute(rRoute);
  
  //Add all the mandatory attributes.
    
  Sptr<TRIPNextHopServer> nServer =
    new TRIPNextHopServer(getItad(),TRIPData(""));
  uMsg->addAttribute(nServer);
  Sptr<TRIPAdvertisePath> advPath = new TRIPAdvertisePath();
  Sptr<TRIPRoutedPath> routePath = new TRIPRoutedPath();
  uMsg->addAttribute(advPath);
  uMsg->addAttribute(routePath);

  if(topologyAttr != 0){
    uMsg->addAttribute(topologyAttr);
  }
  else{
    u_int32_t seqNum = getNextSeqNumber(true, 0);
    Sptr<TRIPItadTopology> itadTopology =
      new TRIPItadTopology(id, seqNum, TRIP_FLG_LINK_STATE);
    //Get the list of TRIPIds and form the ItadTopology Attribute.
    for(LSConnectionList::iterator itr = cList->begin();
	itr != cList->end(); itr++){
      itadTopology->addTripId((*itr));
    }
    uMsg->addAttribute(itadTopology);
  }
  
  ostrstream ostr;
  ostr << "Topology Update Msg:" << endl;
  ostr << *uMsg << endl;
  ostr << ends;
  cpLog(LOG_DEBUG, "%s", ostr.str());
  ostr.freeze(false);
  return (uMsg);
}
//----------------------------------------------------------------------------
void LSSessionManager::handleTopologyChanges(const TRIPItadKey& srcItadKey,
					     Sptr<TRIPItadTopology> itadTopology){
  cpLog(LOG_DEBUG, "LSSessionManager::handleTopologyChanges");
  TRIPItadTopologySet recSet;
  LSConnectionList addList, removeList;
  bool flood = _topology->updateTopology(itadTopology, addList, removeList);
  if(removeList.size()){
        //Remove the Adj-TRIB-Ins
    for(LSConnectionList::iterator itr = removeList.begin(); 
	itr != removeList.end(); itr++){
      TRIPItadKey key(getItad(), (*itr));
      cpLog(LOG_DEBUG, "Destroying Adj-trib-in for (%s)",
	    key.getDescription().c_str());
      TRIBAgent::instance().destroyAdjTribIn(key);
    }
  }
  if(flood){
    Sptr<TRIPUpdateMsg> uMsg = createTopologyUpdateMsg(itadTopology);
    //Since we need to send to every internal peer session.
    TRIPItadKey none;
    sendTopologyUpdateOmit(none, uMsg);
  }

  ostrstream oStr;
  _topology->dump(oStr);
  oStr << ends;
  cpLog(LOG_INFO, "TOPOLOGY: %s", oStr.str());
  oStr.freeze(false);
}
//----------------------------------------------------------------------------
void LSSessionManager::addTopology(const TRIPItadKey& lKey, 
				   const TRIPItadKey& peerKey){
  cpLog(LOG_DEBUG, "LSSessionManager::addTopology");
  _topology->addConnection(lKey.getTripId(), peerKey.getTripId());
  Sptr<TRIPUpdateMsg> uMsg = createTopologyUpdateMsg(); 
  TRIPItadKey none;
  sendTopologyUpdateOmit(none, uMsg);                                    
  
  ostrstream oStr;
  _topology->dump(oStr);
  oStr << ends;
  cpLog(LOG_INFO, "TOPOLOGY: %s", oStr.str());
  oStr.freeze(false);
}
//----------------------------------------------------------------------------
bool PrefixMatch(string number, string prefix){
  const char *number_c = number.c_str();
  const char *prefix_c = prefix.c_str();
  if (strncmp(number_c, prefix_c, strlen(prefix_c)) == 0) {
    return true;
  }
  else {
    return false;
  }
}
//----------------------------------------------------------------------------
string LSSessionManager::findSIPGateway(string pre){
  
  //Setting the prefix that must be looked up.
  int strLen = pre.length()-1;
  cout << "Prefix length is: " << strLen << endl;

  string prefix = pre.substr(1, strLen); 
  cout << "In LSSession Manager, prefix to look for: " << prefix << endl;
  
  //Setting the key for SIP and POTS.
  TRIPProtoKey key(TRIP_APP_PROTO_SIP, TRIP_ADDR_POTS);
  
  //Look for the prefix.
  string gatewayFound = "";

  //First check to see whether the prefix can be found within the
  //set of preferred local routes.
  ProvisionInterface& pi = ProvisionInterface::instance();
  bool gotRoutes = pi.hasLocRoutes();
  if(gotRoutes){    
    cout << "Checking preferred local routes." << endl;
    int numR = pi.getTotalRoutes();
    vector<string> lrRouteAddr = pi.getLocRouteAddr();
    vector<string> lrProto = pi.getLocRouteProtocols();
    vector<string> lrPrefix = pi.getLocRoutePrefixes();
    vector<string> lrNext = pi.getLocRouteNext();
    vector<string> lrNextPort = pi.getLocRouteNextPort();
    vector<string> lrNextITAD = pi.getLocRouteNextITAD();
    
    for (int i=0; i<numR; i++) {
      if ( PrefixMatch(prefix, lrPrefix[i]) ) {
        gatewayFound = lrNext[i];
        gatewayFound += ":";
        gatewayFound += lrNextPort[i];
        cout << "Local provision table has gateway as: " << gatewayFound << endl;
        break;
      }//End if.
        
    }//End for.

  }//End if.

  //If it is not within the set of preferred local routes, check the tables of
  //this LS.
  //Search the Loc-TRIB table of the LS.
  if (gatewayFound == "") {
    cout << "SEARCHING FOR PREFIX IN LOCTRIB OF LS" << endl;

    Sptr<TRIPTrieCache> locTRIB = TRIBAgent::instance().getLocTrib();
    
    char *number_c = strdup(prefix.c_str());
    
    for (int i=strlen(number_c); i>=1; i--) {
      gatewayFound = locTRIB->getNextHopServer(number_c, key);
      if (gatewayFound != "") {
        cout << "In LSSessionManager, the locTRIB gateway is: " << gatewayFound << endl;
        break;
      }
      number_c[i - 1] = '\0';
    }
    free(number_c);
    
  }//End if.
  //Search the Ext-TRIB table of the LS.
  if (gatewayFound == "") {
    cout << "SEARCHING FOR PREFIX IN EXTTRIB OF LS" << endl;

    Sptr<TRIPTrieCache> extTRIB = TRIBAgent::instance().getExtTrib();
    
    char *number_c = strdup(prefix.c_str());
    
    for (int i=strlen(number_c); i>=1; i--) {
      gatewayFound = extTRIB->getNextHopServer(number_c, key);
      if (gatewayFound != "") {
        cout << "In LSSessionManager, the extTRIB gateway is: " << gatewayFound << endl;
        break;
          }
          number_c[i - 1] = '\0';
      }
    free(number_c);

  }//End if.

  //If not found in tables of this LS, check tables of peers.
  //Searching TRIB-In of peers.
  if (gatewayFound == "") {
    cout << "SEARCHING FOR PREFIX IN TRIBIN OF LS PEERS" << endl;
    //The list of peers for which an connection exists.
    LSConnectionList peerList;

    if(peerList.size()){
      for(LSConnectionList::iterator itr = peerList.begin();
          itr != peerList.end(); itr++){
        //Create the key for the peer.
        TRIPItadKey peerKey(getItad(), (*itr));
        
        Sptr<TRIPTrieCache> adjTRIB = TRIBAgent::instance().getTribIn(peerKey);
        char *number_c = strdup(prefix.c_str());
        
        for (int i=strlen(number_c); i>=1; i--) {
          gatewayFound = adjTRIB->getNextHopServer(number_c, key);
          if (gatewayFound != "") {
            cout << "In LSSessionManager, the abjTRIB gateway is: " << gatewayFound << endl;
            break;
          }//End if.
          number_c[i - 1] = '\0';

        }//End for.
        
        free(number_c);

      }//End for.
      
    }//End if.
    
  }//End if.  
  //Searching TRIB-Out of peers.
  if (gatewayFound == "") {
    cout << "SEARCHING FOR PREFIX IN TRIBOUT OF LS PEERS" << endl;
    //The list of peers for which an connection exists.
    LSConnectionList peerList;

    if(peerList.size()){
      for(LSConnectionList::iterator itr = peerList.begin();
          itr != peerList.end(); itr++){
        //Create the key for the peer.
        TRIPItadKey peerKey(getItad(), (*itr));
        
        Sptr<TRIPTrieCache> adjTRIB = TRIBAgent::instance().getTribOut(peerKey);
        char *number_c = strdup(prefix.c_str());
        
        for (int i=strlen(number_c); i>=1; i--) {
          gatewayFound = adjTRIB->getNextHopServer(number_c, key);
          if (gatewayFound != "") {
            cout << "In LSSessionManager, the abjTRIB gateway is: " << gatewayFound << endl;
            break;
          }//End if.
          number_c[i - 1] = '\0';

        }//End for.
        
        free(number_c);

      }//End for.
      
    }//End if.
    
  }//End if.  

  //Search TRIB-In of LS.
  if (gatewayFound == "") {
    cout << "SEARCHING FOR PREFIX IN TRIBIN OF LS" << endl;

      Sptr<TRIPTrieCache> adjTRIB = TRIBAgent::instance().getTribIn(_tripItadKey);

      char *number_c = strdup(prefix.c_str());

      for (int i=strlen(number_c); i>=1; i--) {
          gatewayFound = adjTRIB->getNextHopServer(number_c, key);
          if (gatewayFound != "") {
              cout << "In LSSessionManager, the abjTRIB gateway is: " << gatewayFound << endl;
              break;
          }
          number_c[i - 1] = '\0';
      }

      free(number_c);

  }//End if.
  //Search TRIB-Out of LS.
  if (gatewayFound == "") {
    cout << "SEARCHING FOR PREFIX IN TRIBOUT OF LS" << endl;

      Sptr<TRIPTrieCache> adjTRIB = TRIBAgent::instance().getTribOut(_tripItadKey);

      char *number_c = strdup(prefix.c_str());

      for (int i=strlen(number_c); i>=1; i--) {
          gatewayFound = adjTRIB->getNextHopServer(number_c, key);
          if (gatewayFound != "") {
              cout << "In LSSessionManager, the abjTRIB gateway is: " << gatewayFound << endl;
              break;
          }
          number_c[i - 1] = '\0';
      }

      free(number_c);

  }//End if.
    
  return gatewayFound;
}
//////////////////////////////////////////////////////////////////////////////
