// Copyright (c) 2000 by the University of Southern California
// All rights reserved.
//
// Permission to use, copy, modify, and distribute this software and its
// documentation in source and binary forms for non-commercial purposes
// and without fee is hereby granted, provided that the above copyright
// notice appear in all copies and that both the copyright notice and
// this permission notice appear in supporting documentation. and that
// any documentation, advertising materials, and other materials related
// to such distribution and use acknowledge that the software was
// developed by the University of Southern California, Information
// Sciences Institute.  The name of the University may not be used to
// endorse or promote products derived from this software without
// specific prior written permission.
//
// THE UNIVERSITY OF SOUTHERN CALIFORNIA makes no representations about
// the suitability of this software for any purpose.  THIS SOFTWARE IS
// PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES,
// INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
//
// Other copyrights might apply to parts of this software and are so
// noted when applicable.
//
//  Copyright (C) 1998 by Mingzhou Sun. All rights reserved.
//  This software is developed at Rensselaer Polytechnic Institute under 
//  DARPA grant No. F30602-97-C-0274
//  Redistribution and use in source and binary forms are permitted
//  provided that the above copyright notice and this paragraph are
//  duplicated in all such forms and that any documentation, advertising
//  materials, and other materials related to such distribution and use
//  acknowledge that the software was developed by Mingzhou Sun at the
//  Rensselaer  Polytechnic Institute.  The name of the University may not 
//  be used to endorse or promote products derived from this software 
//  without specific prior written permission.
//
// $Header: /nfs/jade/vint/CVSROOT/ns-2/linkstate/ls.h,v 1.5 2002/10/09 03:47:00 difa Exp $

#ifndef ns_ls_h
#define ns_ls_h

#include <sys/types.h> 
#include <list>
#include <map>
#include <utility>

#include "timer-handler.h"

const int LS_INVALID_COUNT = -1;
const int LS_INIT_ACCESS_COUNT = 3;
const int LS_INVALID_NODE_ID = 65535;
const int LS_INVALID_COST = 65535;
const int LS_MIN_COST = 0;
const int LS_MAX_COST = 65534;
const int LS_MESSAGE_CENTER_SIZE_FACTOR = 4; // times the number of nodes 
const int LS_DEFAULT_MESSAGE_SIZE = 100; // in bytes
const int LS_LSA_MESSAGE_SIZE = 100; // in bytes
const double LS_RTX_TIMEOUT = 0.002;  // default to 2ms to begin with
const int LS_TIMEOUT_FACTOR = 3;   // x times of one-way total delay
// Topo message is not too long due to incremental update
const int LS_TOPO_MESSAGE_SIZE = 200; // in bytes
const int LS_ACK_MESSAGE_SIZE = 20; // in bytes
const unsigned int LS_INVALID_MESSAGE_ID = 0;
const unsigned int LS_BIG_NUMBER = 1048576;
const unsigned int LS_WRAPAROUND_THRESHOLD = 1073741824; // 2^30
const unsigned int LS_MESSAGE_TYPES = 6;

enum ls_status_t { 
	LS_STATUS_DOWN = 0, 
	LS_STATUS_UP = 1
};

enum ls_message_type_t { 
	LS_MSG_INVALID = 0, 
	LS_MSG_LSA = 1,		// Link state advertisement
	LS_MSG_TPM = 2,		// Topology map message
	LS_MSG_LSAACK = 3,	// Link state advertisement ACK
	LS_MSG_TPMACK = 4, 
	LS_MSG_LSM = 5, 
};

template <class _Tp>
class LsList : public list<_Tp> {
public:
	typedef list<_Tp> baseList;
	LsList() : baseList() {}
	LsList(const _Tp& x) : baseList(1, x) {}
	void eraseAll() { 
		baseList::erase(begin(), end()); 
	}
	LsList<_Tp>& operator= (const LsList<_Tp> & x) {
		return (LsList<_Tp> &)baseList::operator= (x);
	}
};

template<class Key, class T>
class LsMap : public map<Key, T, less<Key> > {
public:
	typedef less<Key> less_key;
	typedef map<Key, T, less_key> baseMap;
	LsMap() : baseMap() {}

	// this next typedef of iterator seems extraneous but is required by gcc-2.96
	typedef typename map<Key, T, less<Key> >::iterator iterator;
	typedef pair<iterator, bool> pair_iterator_bool;
	iterator insert(const Key & key, const T & item) {
		typename baseMap::value_type v(key, item);
		pair_iterator_bool ib = baseMap::insert(v);
		return ib.second ? ib.first : baseMap::end();
	}

	void eraseAll() { erase(begin(), end()); }
	T* findPtr(Key key) {
		iterator it = baseMap::find(key);
		return (it == baseMap::end()) ? (T *)NULL : &((*it).second);
	}
};

/*
  LsNodeIdList -- A list of int 's. It manages its own memory
*/
class LsNodeIdList : public LsList<int> {
public:
	int appendUnique (const LsNodeIdList& x);
};  

/* -------------------------------------------------------------------*/
/* 
   LsLinkState -- representing a link, contains neighborId, cost and status
*/
struct LsLinkState {
	// public data
	int neighborId_;  
	ls_status_t status_;
	int cost_;
	u_int32_t sequenceNumber_;  

	// public methods
	LsLinkState() : neighborId_(LS_INVALID_NODE_ID),
		status_(LS_STATUS_DOWN), cost_(LS_INVALID_COST) {}
	LsLinkState(int id, ls_status_t s, int c) : neighborId_(id), 
		status_(s), cost_(c) {}

	void init (int nbId, ls_status_t s, int c){
		neighborId_ = nbId;
		status_ = s;
		cost_ =c;
	}
} ;

/* 
   LsLinkStateList 
*/
typedef LsList<LsLinkState> LsLinkStateList;

/* -------------------------------------------------------------------*/
/*
  LsTopoMap
  the Link State Database, the representation of the
  topology within the protocol 
*/
typedef LsMap<int, LsLinkStateList> LsLinkStateListMap;

class LsTopoMap : public LsLinkStateListMap {
public:
	// constructor / destructor 
	// the default ones 
	LsTopoMap() : LsLinkStateListMap() {}
  
	//   // map oeration
	//   iterator begin() { return LsLinkStateListMap::begin();}
	//   iterator end() { return LsLinkStateListMap::end();}
	//  iterator begin() { return baseMap::begin();}
	//   const_iterator begin() const { return  baseMap::begin();}
	//   iterator end() { return baseMap::end();}
	//   const_iterator end() const { return baseMap::end();}

	// insert one link state each time 
	LsLinkStateList* insertLinkState(int nodeId, 
					 const LsLinkState& linkState);
	// update returns true if there's change
	bool update(int nodeId, const LsLinkStateList& linkStateList);
	//   friend ostream & operator << ( ostream & os, LsTopoMap & x) ;
	void setNodeId(int id) { myNodeId_ = id ;}
private:
	int myNodeId_; // for update()
};

typedef LsTopoMap LsTopology;
typedef LsTopoMap* LsTopoMapPtr;

/*
  LsPath - A struct with destination, cost, nextHop
*/
struct LsPath {
	LsPath() : destination (LS_INVALID_NODE_ID) {}
	LsPath(int dest, int c, int nh)
		: destination (dest), cost(c), nextHop(nh) {}
	// methods
	bool isValid() { 
		return ((destination != LS_INVALID_NODE_ID) && 
			(cost != LS_INVALID_COST) && 
			(nextHop != LS_INVALID_COST));
	}

	// public data
	int destination;
	int cost;
	int nextHop;
};

/*
  LsEqualPaths -- A struct with one cost and a list of multiple next hops
  Used by LsPaths
*/
struct LsEqualPaths {
public:
	int  cost;
	LsNodeIdList nextHopList;

	// constructors
	LsEqualPaths() : cost(LS_INVALID_COST) {}
	LsEqualPaths(int c, int nh) : cost(c), nextHopList() {
		nextHopList.push_back(nh);
	}
	LsEqualPaths(const LsPath & path) : cost (path.cost), nextHopList() {
		nextHopList.push_back(path.nextHop);
	}
	LsEqualPaths(int c, const LsNodeIdList & nhList) 
		: cost(c), nextHopList(nhList) {}
  
	LsEqualPaths& operator = (const LsEqualPaths & x ) {
		cost = x.cost;
		nextHopList = x.nextHopList;
		return *this;
	}

	// copy 
	LsEqualPaths& copy(const LsEqualPaths & x) { return operator = (x) ;}

	// appendNextHopList 
	int appendNextHopList(const LsNodeIdList & nhl) {
		return nextHopList.appendUnique (nhl);
	}
};

/* 
   LsEqualPathsMap -- A map of LsEqualPaths
*/
typedef LsMap< int, LsEqualPaths > LsEqualPathsMap;

/*
  LsPaths -- enhanced LsEqualPathsMap, used in LsRouting
*/
class LsPaths : public LsEqualPathsMap {
public:
	LsPaths(): LsEqualPathsMap() {}
  
	// -- map operations 
	iterator begin() { return LsEqualPathsMap::begin();}
	iterator end() { return LsEqualPathsMap::end();}

	// -- specical methods that facilitates computeRoutes of LsRouting
	// lookupCost
	int lookupCost(int destId) {
		LsEqualPaths * pEP = findPtr (destId);
		if ( pEP == NULL ) return LS_MAX_COST + 1;
		// else
		return pEP->cost;
	}

	// lookupNextHopListPtr
	LsNodeIdList* lookupNextHopListPtr(int destId) {
		LsEqualPaths* pEP = findPtr(destId);
		if (pEP == NULL ) 
			return (LsNodeIdList *) NULL;
		// else
		return &(pEP->nextHopList);
	}
  
	// insertPath without checking validity
	iterator insertPathNoChecking(int destId, int cost, int nextHop) {
		LsEqualPaths ep(cost, nextHop);
		iterator itr = insert(destId, ep);
		return itr; // for clarity
	}     

	// insertPathNoChekcing()
	iterator insertPathNoChecking (const LsPath & path) {
		return insertPathNoChecking(path.destination, path.cost, 
					    path.nextHop);
	}
	// insertPath(), returns end() if error, else return iterator 
	iterator insertPath(int destId, int cost, int nextHop);
	iterator insertPath(const LsPath& path) {
		return insertPath(path.destination, path.cost, path.nextHop);
	}
	// insertNextHopList 
	iterator insertNextHopList(int destId, int cost, 
				   const LsNodeIdList& nextHopList);
};

/*
  LsPathsTentative -- Used in LsRouting, remembers min cost and location 
*/
class LsPathsTentative : public LsPaths {
public:
	LsPathsTentative() : LsPaths(), minCost(LS_MAX_COST+1) {
		minCostIterator = end();
	}
  
	// combining get and remove min path
	LsPath popShortestPath();
  
private:
	int minCost; // remembers the min cost
	iterator minCostIterator; // remembers where it's stored
	iterator findMinEqualPaths();
};

/* 
   LsMessage 
*/
struct LsMessage {
	LsMessage() : type_(LS_MSG_INVALID), contentPtr_(NULL) {}
	LsMessage(u_int32_t id, int nodeId, ls_message_type_t t) :
		type_(t), messageId_(id),
		sequenceNumber_(id), originNodeId_(nodeId), 
		contentPtr_(NULL) {}
	~LsMessage() {
		if ((type_ == LS_MSG_LSM) && (lslPtr_ != NULL)) {
			delete lslPtr_;
			lslPtr_ = NULL;
		}
	}
	ls_message_type_t type_;
	u_int32_t messageId_;
	u_int32_t sequenceNumber_; 
	int originNodeId_; 
	union {
		LsLinkStateList* lslPtr_;
		const LsTopoMap* topoPtr_;
		void* contentPtr_;
	};
};

// TODO -- not used, comment out
// Some time we just want the header, since the message has a content
// which will be destroyed with it goes out of scope, 
// used by ack manager
struct LsMessageInfo
{
	ls_message_type_t type_;
	int destId_; 
	u_int32_t msgId_;
	u_int32_t sequenceNumber_;    
	union {
		// for LSA, the originator of the msg
		int originNodeId_;  
		// for LSA_ACK, the originator of the lsa being acked
		int originNodeIdAck_; 
	};
	// constructor 
	LsMessageInfo() {}
	LsMessageInfo(int d, const LsMessage& msg ) :
		type_(msg.type_), destId_(d),  msgId_(msg.messageId_),
		sequenceNumber_(msg.sequenceNumber_),
		originNodeId_(msg.originNodeId_)  {}
	LsMessageInfo(int d , ls_message_type_t t, int o, 
		      u_int32_t seq, u_int32_t mId) :
		type_(t), destId_(d), msgId_(mId), sequenceNumber_(seq), 
		originNodeId_(o) {}
};

/* 
   LsMessageCenter -- Global storage of LsMessage's for retrieval
*/
class LsMessageCenter {
public:
	typedef LsMap <u_int32_t, LsMessage> baseMap;
	// constructor
	LsMessageCenter () 
		: current_lsa_id(LS_INVALID_MESSAGE_ID + 1), 
		current_other_id(LS_INVALID_MESSAGE_ID + 2),
		max_size(0), lsa_messages(), other_messages() {}
  
	void setNodeNumber (int number_of_nodes) {
		max_size = number_of_nodes * LS_MESSAGE_CENTER_SIZE_FACTOR;
	}
	LsMessage* newMessage (int senderNodeId, ls_message_type_t type);
	u_int32_t duplicateMessage( u_int32_t msgId) {
		return msgId;
	}
	u_int32_t duplicateMessage(const LsMessage& msg) {
		return duplicateMessage(msg.messageId_);
	}
	bool deleteMessage(u_int32_t msgId) ;
	bool deleteMessage (const LsMessage& msg) {
		return deleteMessage(msg.messageId_);
	}
	LsMessage* retrieveMessagePtr(u_int32_t msgId){
		if (isLSA(msgId)) {
			return lsa_messages.findPtr(msgId);
		} else {
			return other_messages.findPtr(msgId);
		}
	}
	static LsMessageCenter& instance() { 
		return msgctr_;
	}

private:
	static LsMessageCenter msgctr_;	// Singleton class

	u_int32_t current_lsa_id ;
	u_int32_t current_other_id;
	unsigned int max_size; // if size() greater than this number, erase begin().
	typedef LsMap <u_int32_t, LsMessage > message_storage;
	message_storage lsa_messages;
	message_storage other_messages;
	void init();
	int isLSA (u_int32_t msgId) {
		// to see if msgId's last bit is different from 
		// LS_INVALID_MESSAGE_ID
		return (0x1 & (msgId ^ LS_INVALID_MESSAGE_ID));
	}
};

/* 
   LsMessageHistory 
*/
typedef LsList<u_int32_t> LsMessageIdList;
typedef less<int> less_node_id;
class LsMessageHistory : public LsMap<int, u_int32_t> {
public:
	// isNewMessage, note: it saves this one in the history as well
	bool isNewMessage ( const LsMessage & msg ); 
};

class LsRetransmissionManager;
class LsRetransTimer : public TimerHandler {
public:
	LsRetransTimer() {}
	LsRetransTimer (LsRetransmissionManager *amp , int nbrId)
		: ackManagerPtr_(amp), neighborId_(nbrId) {}
	virtual void expire(Event *e);
protected:
	LsRetransmissionManager* ackManagerPtr_;
	int neighborId_;
};

struct LsIdSeq {
	u_int32_t msgId_;
	u_int32_t seq_;
	LsIdSeq() {}
	LsIdSeq(u_int32_t id, u_int32_t s) : msgId_(id), seq_(s) {}
};

/* LsUnackPeer 
   used in ackManager to keep record a peer who still haven't ack some of 
   its LSA or Topo packets
*/
struct LsUnackPeer {
	double rtxTimeout_; // time out value 
	LsRetransTimer timer_;
	u_int32_t tpmSeq_; // topo message Id

	LsMap<int, LsIdSeq> lsaMap_;

	// constructor
	LsUnackPeer() : tpmSeq_(LS_INVALID_MESSAGE_ID) {}
	LsUnackPeer(LsRetransmissionManager* amp, int nbrId, 
		    double timeout = LS_RTX_TIMEOUT) : 
		rtxTimeout_(timeout), timer_(amp, nbrId), 
		tpmSeq_(LS_INVALID_MESSAGE_ID) {}
};

/* 
   LsDelayMap
   store the estimated one-way total delay for eay neighbor, in second
*/
typedef LsMap< int, double > LsDelayMap; 

/* 
   LsRetransmissionManager -- handles retransmission and acknowledgement
*/
class LsRouting; 
class LsRetransmissionManager : public LsMap<int, LsUnackPeer> {
public:
	LsRetransmissionManager(LsRouting& lsr) : lsRouting_(lsr) {} 

	void initTimeout(LsDelayMap* delayMapPtr);
	void cancelTimer(int neighborId);

	// Called by LsRouting when a message is sent out 
	int messageOut(int peerId, const LsMessage& msg);
  
	// Called by LsRouting when an ack is received
	int ackIn(int peerId,  const LsMessage& ack);

	// Called by retransmit timer
	int resendMessages(int peerId);

private:
	// data
	LsRouting& lsRouting_;
};

inline void LsRetransTimer::expire(Event *e) 
{ 
	ackManagerPtr_->resendMessages(neighborId_); 
}
   
/* 
   LsNode -- represents the node environment interface 
   It serves as the interface between the Routing class and the actual 
   simulation enviroment 
   rtProtoLS will derive from LsNode as well as Agent
*/
class LsNode {
public:
	virtual bool sendMessage(int destId, u_int32_t msgId, 
				  int msgsz = LS_DEFAULT_MESSAGE_SIZE) = 0;
	virtual void receiveMessage(int sender, u_int32_t msgId) = 0;
	// TODO , maybe not, use one type of message, that's it. 
	// All go to message center.
	// sendAck 
	// receiveAck
	virtual int getNodeId() = 0;
	virtual LsLinkStateList* getLinkStateListPtr()= 0; 
	virtual LsNodeIdList* getPeerIdListPtr() = 0;
	virtual LsDelayMap* getDelayMapPtr() = 0;
};

/* 
   LsRouting -- The implementation of the Link State Routing protocol
*/
class LsRouting {
public:
	static int msgSizes[ LS_MESSAGE_TYPES ];
	friend class LsRetransmissionManager;

	// constructor and distructor
	LsRouting() : myNodePtr_(NULL),  myNodeId_(LS_INVALID_NODE_ID), 
		peerIdListPtr_(NULL), linkStateListPtr_(NULL),
		routingTablePtr_(NULL),
		linkStateDatabase_(), lsaHistory_(), ackManager_(*this) {}
	~LsRouting() {
		//delete pLinkStateDatabase;
		if (routingTablePtr_ != NULL)
			delete routingTablePtr_;
	}

	bool init(LsNode* nodePtr);
	void computeRoutes() {
	        if (routingTablePtr_ != NULL)
	                delete routingTablePtr_;
	        routingTablePtr_ = _computeRoutes();
	}
	LsEqualPaths* lookup(int destId) {
		return (routingTablePtr_ == NULL) ? 
			(LsEqualPaths *)NULL : 
			routingTablePtr_->findPtr(destId);
	}

	// to propogate LSA, all Links, called by node and self
	bool sendLinkStates(bool buffer = false); 
	void linkStateChanged();
	void sendBufferedMessages() ;

	// called by node when messages arrive
	bool receiveMessage(int senderId , u_int32_t msgId);

private:
	// most of these pointers should have been references, 
	// except routing table
	LsNode * myNodePtr_; // where I am residing in
	int myNodeId_; // who am I
	LsNodeIdList* peerIdListPtr_; // my peers
	LsLinkStateList* linkStateListPtr_; // My links
	LsMessageCenter* messageCenterPtr_; // points to static messageCenter
	LsPaths* routingTablePtr_; // the routing table
	LsTopoMap linkStateDatabase_; // topology;
	LsMessageHistory lsaHistory_; // Remember what we've seen
	LsMessageHistory tpmHistory_; 
	LsRetransmissionManager ackManager_; // Handles ack and retransmission

	struct IdMsgPtr {
		int peerId_;
		const LsMessage* msgPtr_;
		IdMsgPtr() {};
		IdMsgPtr(int id, const LsMessage* p) :
			peerId_(id), msgPtr_(p) {}
	};
	typedef LsList<IdMsgPtr> MessageBuffer;
	MessageBuffer messageBuffer_;

private:
	LsMessageCenter& msgctr() { return LsMessageCenter::instance(); }
	LsPaths* _computeRoutes();
	bool isUp(int neighborId);

	bool receiveAck (int neighborId, LsMessage* msgPtr) {
		ackManager_.ackIn(neighborId, *msgPtr);
		return true;
	}
	bool receiveLSA (int neighborId, LsMessage* msgPtr);
	bool receiveTopo(int neighborId, LsMessage* msgPtr);

	// send the entire topomap
	// don't worry, in simulation only the pointer is sent
	// in ospf, only the descrpition of it is sent first, 
	void sendTopo(int neighborId);
	void regenAndSend(int exception, int origin, 
			  const LsLinkStateList& lsl);
	bool sendAck(int nbrId, ls_message_type_t type, 
		     int originNodeIdAcked, u_int32_t originMsgIdAcked);
	void resendMessage(int neighborId, u_int32_t msgId, 
			    ls_message_type_t type) {
		myNodePtr_->sendMessage(neighborId, msgId, msgSizes[type]);
	}
	// just store the outgoing messages, and wait for cmd flushBuffer to 
	// actually send out 
	void bufferedSend (int peerId, const LsMessage* mp) {
		messageBuffer_.push_back(IdMsgPtr(peerId, mp));
	}
};

#endif // ns_ls_h
