/* -*-  Mode: C++; c-file-style: "gnu"; indent-tabs-mode:nil; -*- */
/*
 * Author: Yu Cao <caoyu08@csnet1.cs.tsinghua.edu.cn>
 */

#ifndef NAMPT_L4_PROTOCOL_H
#define NAMPT_L4_PROTOCOL_H

#include <stdint.h>
#include <map>

#include "ns3/packet.h"
#include "ns3/ipv4-address.h"
#include "ns3/ptr.h"
#include "ns3/object-factory.h"
#include "ns3/net-device.h"

//#include "ipv4-l4-protocol.h"
#include "ip-l4-protocol.h"
#include "tcp-l4-protocol.h"

//#define NAMPT_LARGE_SCALE 

#ifdef NAMPT_LARGE_SCALE
#define NAMPT_PATH_PREFIX "/home/Frank/work/output/"
#define NAMPT_LOG(_nampt_log_str) 
#define NAMPT_LOG2(_nampt_log_str) \
	*(m_node->m_outputStream->GetStream()) << \
	Simulator::Now().GetSeconds() << " " << \
	_nampt_log_str << std::endl
#else
#define NAMPT_PATH_PREFIX "/home/Frank/work/output/"
#define NAMPT_LOG(_nampt_log_str) \
	std::clog << Simulator::Now().GetSeconds() << " " << _nampt_log_str << std::endl
#define NAMPT_LOG2(_nampt_log_str) \
	if (m_node->m_outputStream) { \
	*(m_node->m_outputStream->GetStream()) << \
	Simulator::Now().GetSeconds() << " " << \
	_nampt_log_str << std::endl; }
#endif


#define NAMPT_SCALEWND(__u16_val) \
	( __u16_val & 0x8000 ? \
	 (__u16_val & 0x7fff) << 10 : \
	  __u16_val )

namespace ns3 {

class Socket;
class NaMPTSocket;
class NaMPTHeader;


typedef enum {
	NAMPT_PM_SINGLE_SUBFLOW = 0x00000001, 
	NAMPT_PM_SINGLE_SRC = 0x00000002, 
	NAMPT_PM_DETECT_LAN = 0x00000004, 
	NAMPT_PM_SRC_ROUNDROBIN = 0x00000008, 
	NAMPT_PM_ACCESS_POINTS = 0x00000010, 
	NAMPT_RESERVED = 0x80000000, 
	
	NAMPT_RR,
	NAMPT_SOD,

	NAMPT_NA,

	NAMPT_CC_INDE, 
	NAMPT_CC_LIA,
	NAMPT_CC_WVEGAS, 
	NAMPT_CC_DCTCP, 
	NAMPT_CC_XMP
} NaMPTAlgo_t;


/**
 * \ingroup tcp
 * \brief A sublayer between the sockets interface and tcp
 * 
 * XXX
*/
class FlowItem : public Object
{
public: 
  //static TypeId GetTypeId (void);
  FlowItem (const Ipv4Address& addr, const Ipv4Mask& mask);
  virtual ~FlowItem (void);

  Ipv4Address	m_network;
  Ipv4Mask		m_mask;
  uint32_t		m_nFrom;
  uint32_t		m_nTo;
};

typedef std::vector<Ptr<FlowItem> > FlowTable_t;

class NATItem : public Object
{
public:
  static TypeId GetTypeId (void);
  static const double POLLING_INTERVAL; // seconds
  static const uint8_t TTL;
  
  NATItem(void);
  virtual ~NATItem(void);

  uint32_t	m_addrs[2];
  uint16_t	m_ports[2];
  uint8_t	m_ttl;
  uint8_t	m_flag;
  int32_t	m_next;
};

typedef std::vector<Ptr<NATItem> > DCNNATTable_t; // key is the port number

class NaMPTL4Protocol : public TcpL4Protocol {
public:
  typedef std::map<uint32_t, Ptr<NaMPTSocket> > TokenDB_t;
  
  static TypeId GetTypeId (void);
  /**
   * \brief Constructor
   */
  NaMPTL4Protocol ();
  virtual ~NaMPTL4Protocol ();

  /**
   * \return A smart Socket pointer to a NaMPTSocket allocated by this instance
   * of the NaMPT protocol
   */
  virtual Ptr<Socket> CreateSocket (void);
  virtual Ptr<Socket> CreateSocket (TypeId socketTypeId);

  void EnableDCNNAT(void);
  void DisableDCNNAT(void);
  uint16_t ApplyForDCNNAT(const Ipv4Address& desAddr, uint16_t desPort, const Ipv4Address& srcAddr);
  uint32_t QueryInputFlows(const Ipv4Address& addr, const Ipv4Mask& mask);
  uint32_t QueryOutputFlows(const Ipv4Address& addr, const Ipv4Mask& mask);

protected:
friend class NaMPTSocket;
friend class TcpSocketBase;
friend class NaMPTSubflow;
  virtual void DoDispose (void);
  /* 
   * This function will notify other components connected to the node that a new stack member is now connected
   * This will be used to notify Layer 3 protocol of layer 4 protocol stack to connect them together.
   */
  virtual void NotifyNewAggregate ();
  
  virtual enum IpL4Protocol::RxStatus Receive (Ptr<Packet> p,
                                                 Ipv4Header const &header,
                                                 Ptr<Ipv4Interface> incomingInterface);  
  virtual void SendPacket (Ptr<Packet>, const TcpHeader &,
              Ipv4Address, Ipv4Address, Ptr<NetDevice> oif = 0);
  
  void SendPacket (Ptr<Packet>, NaMPTHeader &,
              Ipv4Address, Ipv4Address, Ptr<NetDevice> oif = 0);

  Ptr<NaMPTSocket> LookupSocketFromTokenDB(uint16_t sport, const Ipv4Address &saddr, uint16_t dport, const Ipv4Address &daddr);
  Ptr<NaMPTSocket> LookupToken (uint32_t token);
  uint32_t RegisterToken (const Ptr<NaMPTSocket>& sock);
  void UnregisterToken (uint32_t token);
  void RecordFlow(const Ipv4Address& srcAddr, const Ipv4Address& desAddr);
  void EraseFlow(const Ipv4Address& srcAddr, const Ipv4Address& desAddr);

private:
  TokenDB_t		m_tokenDB;
  uint32_t		m_tokenSource;

  void DCNNATPollingWoker(void);
  bool				m_enableDCNNAT;
  EventId			m_pollingEvent;
  DCNNATTable_t		m_dcnNATTable;
  uint32_t			m_nNAT;
  int32_t			m_freeNAT;
  FlowTable_t		m_flowTable;
};

}; // namespace ns3

#endif /* NAMPT_L4_PROTOCOL_H */
