#ifndef _NETWORK_ADDRESS
#define _NETWORK_ADDRESS
// This is an attempt at some generic network address generalization
// along with some methods for standard address lookup/ manipulation.
// This will evolve as we actually support address types besides IPv4

#include "sysdefs.h"

#ifdef UNIX
#include <sys/types.h>
#include <netdb.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string.h>    
#ifndef INADDR_NONE
const unsigned long INADDR_NONE = 0xffffffff;
#endif // !INADDR_NONE
#endif // UNIX


#ifdef WIN32
#include <winsock2.h>
#include <ws2tcpip.h>
#include "tpipv6.h"  // not in Platform SDK
typedef unsigned int uint32_t;
#endif // WIN32

#ifdef NS2
#include "config.h"  // for nsaddr_t which is a 32 bit int?
typedef nsaddr_t SIMADDR;
#define SIMULATOR_ADDRESS 1
#endif // NS2

#ifdef OPNET
#include "opnet.h"
#include "ip_addr_v4.h"
typedef IpT_Address SIMADDR;
extern IpT_Address IPI_BroadcastAddr;
#define SIMULATOR_ADDRESS 1
#endif // OPNET


#ifdef SIMULATOR_ADDRESS
#ifndef _SOCKADDRSIM
#define _SOCKADDRSIM
struct sockaddr_sim
{
    SIMADDR         addr;
    unsigned short  port;  
};
#endif  // ! _SOCKADDRSIM
#endif // SIMULATOR_ADDRESS

// Network Address Types
enum NetworkAddressType {NETWORK_INVALID_ADDRESS, IPv4, IPv6, SIM};


#ifdef HAVE_IPV6
// returns IPv4 portion of mapped address in network byte order
inline unsigned long IN6_V4MAPPED_ADDR(struct in6_addr* a) {return (((UINT32*)a)[3]);}
#endif // HAVE_IPV6


class NetworkAddress

{
    public:
        NetworkAddressType  type; 
        unsigned int        len;  // length of host address portion
#ifdef HAVE_IPV6
        struct sockaddr_storage addr;
        //struct sockaddr_in6 addr;
#else       
        struct sockaddr         addr;
#endif // if/else (HAVE_IPV6 || WIN32)
        bool IsValid() const {return (NETWORK_INVALID_ADDRESS != type);}
        void Init()
        {
            type = NETWORK_INVALID_ADDRESS;
            len = 0;
            memset(&addr, 0, sizeof(addr));
        }
        void Reset(NetworkAddressType theType, bool zero = true);
        
        NetworkAddressType Type() const {return type;}
        unsigned int Length() const {return len;}
        const char* RawHostAddress() const;
        unsigned short Port() const;        
        
        bool IsMulticast() const;
        bool IsLoopback() const;
        bool IsSiteLocal() const;
        bool IsLinkLocal() const;
        bool IsUnspecified() const;
        
        unsigned int PrefixLength() const;
        void ApplyPrefixMask(unsigned int prefixLen);        
        void GetSubnetAddress(unsigned int    prefixLen, 
                              NetworkAddress& subnetAddr) const;
        void GetBroadcastAddress(unsigned int    prefixLen, 
                                 NetworkAddress& broadcastAddr) const;
        
        bool SetRawHostAddress(NetworkAddressType   theType,
                               const char*          buffer,
                               unsigned int         bufferLen);
        bool SetAddress(const struct sockaddr* theAddr);
        const struct sockaddr* GetAddress() const 
            {return ((const struct sockaddr*)&addr);}
        void SetPort(unsigned short thePort);
                                      
        const char* HostAddressString() const;
        
        bool LookupHostAddress(const char* text);
        bool LookupHostName(char *buf, unsigned int maxLen) const;
        bool LookupLocalHostAddress(char* buf = NULL, unsigned int maxLen = 0);

        bool IsEqual(const NetworkAddress* theAddr) const;
        bool HostIsEqual(const NetworkAddress* theAddr) const;
        int CompareHostAddr(const NetworkAddress* theAddr) const;

        // This function returns a 32-bit number which might _sometimes_
        // be useful as an address-based end system identifier.
        unsigned long EndIdentifier() const;    
    
        unsigned long IPv4HostAddr() const;  // returns host addr in host order
    
#if defined(HAVE_IPV6) && defined(WIN32)
        static bool IsIPv6Capable();
#endif // (HAVE_IPV6) && (WIN32)        

#ifdef SIMULATOR_ADDRESS
        SIMADDR SimHostAddr() const
            {return ((SIM == type) ? 
                    ((struct sockaddr_sim *)&addr)->addr : 
                    0);}
        void SimSetAddress(SIMADDR theAddr)
        {
            type = SIM;
            len = sizeof(SIMADDR);  // NSv2 addresses are 4 bytes? (32 bits)
            ((struct sockaddr_sim *)&addr)->addr = theAddr;
        }
#endif // SIMULATOR_ADDRESS

        private:
#ifdef WIN32
            static bool Win32Startup() 
            {
                // Startup WinSock for name lookup
                WSADATA wsaData;
                WORD wVersionRequested = MAKEWORD(2, 2);
                return (0 == WSAStartup(wVersionRequested, &wsaData));
            }
            static void Win32Cleanup() {WSACleanup();}
#endif  // WIN32
};  // end class NetworkAddress


class PortItem
{
    friend class PortList;
    friend class PortPool;

    public:
        PortItem(unsigned short portMin, unsigned short portMax);
        bool Overlap(unsigned short portMin, unsigned short portMax);
        void Merge(unsigned short portMin, unsigned short portMax);
        PortItem* Next() {return next;}
        unsigned short NextValue(unsigned short index);

    private:
        unsigned short  port_min;
        unsigned short  port_max;
        PortItem*       prev;
        PortItem*       next;
};  // end class PortItem



class PortList
{
    friend class PortPool;
    private:
        PortList();
        ~PortList();
        PortItem* Top() {return top;}
        void Append(PortItem* theItem);
        void Insert(PortItem* theItem);
        void Remove(PortItem* theItem);
        PortItem* FindMatch(unsigned short value);
        PortItem* FindOverlap(unsigned short portMin, unsigned short portMax);
        void Collapse();
        void Destroy();

    private:
        PortItem* top;
        PortItem* bottom;
};  // end class PortList



class PortPool
{
    public:
        PortPool();
        bool AddPorts(const char* portText);
        bool SetContent(const char* portText)
        {
            pool.Destroy();
            return AddPorts(portText);
        }
        unsigned short GetAvailablePort();
        void ReturnPort(unsigned short thePort);
        void WriteContent(char* text, unsigned int maxLen);

    private:

        PortList    pool;
        PortList    used;
};  // end class PortPool



#endif // _NETWORK_ADDRESS

