#ifndef _PROTO_ADDRESS
#define _PROTO_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 "protoDefs.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>
#ifdef HAVE_IPV6
#include "tpipv6.h"  // not in Platform SDK
#endif // HAVE_IPV6
typedef unsigned int uint32_t;
#endif // WIN32

#ifdef SIMULATE

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

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

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

#endif // SIMULATE

#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 ProtoAddress
{
    public:            
        enum Type {INVALID, IPv4, IPv6, SIM};
        // Construction/initialization
        ProtoAddress();
        bool IsValid() const {return (INVALID != type);}
        void Invalidate()
        {
            type = INVALID;
            length = 0;
        }
        void Reset(ProtoAddress::Type theType, bool zero = true);
        
        // Address info
        ProtoAddress::Type GetType() const {return type;}
        UINT8 GetLength() const {return length;}
        bool IsMulticast() const;
        bool IsLoopback() const;
        bool IsSiteLocal() const;
        bool IsLinkLocal() const;
        bool IsUnspecified() const;
        const char* GetHostString(char*         buffer = NULL, 
                                  unsigned int  buflen = 0) const;
        
        // Host address/port get/set
        const char* GetRawHostAddress() const;
        bool SetRawHostAddress(ProtoAddress::Type   theType,
                               const char*          buffer,
                               UINT8                bufferLen);
        const struct sockaddr& GetSockAddr() const 
            {return ((const struct sockaddr&)addr);}
        bool SetSockAddr(const struct sockaddr& theAddr);
        struct sockaddr& AccessSockAddr() 
            {return ((struct sockaddr&)addr);}
        UINT16 GetPort() const; 
        void SetPort(UINT16 thePort);
        void PortSet(UINT16 thePort);
        
        // Address comparison
        bool IsEqual(const ProtoAddress& theAddr) const
            {return (HostIsEqual(theAddr) && (GetPort() == theAddr.GetPort()));}
        bool HostIsEqual(const ProtoAddress& theAddr) const;
        int CompareHostAddr(const ProtoAddress& theAddr) const;
        
        
        UINT8 GetPrefixLength() const;
        void ApplyPrefixMask(UINT8 prefixLen);        
        void GetSubnetAddress(UINT8         prefixLen, 
                              ProtoAddress& subnetAddr) const;
        void GetBroadcastAddress(UINT8          prefixLen, 
                                 ProtoAddress&  broadcastAddr) const;
        
        // Name/address resolution
        bool ResolveFromString(const char* string);
        bool ResolveToName(char* nameBuffer, unsigned int buflen) const;
        bool ResolveLocalAddress(char* nameBuffer = NULL, unsigned int buflen = 0);
        
        // Miscellaneous
        // This function returns a 32-bit number which might _sometimes_
        // be useful as an address-based end system identifier.
        // (For IPv4 it's the address in host byte order)
        UINT32 EndIdentifier() const; 
        // Return IPv4 address in host byte order
        UINT32 IPv4GetAddress() const 
            {return (IPv4 == type) ? EndIdentifier() : 0;}
#ifdef SIMULATE
        SIMADDR SimGetAddress() const
        {
            return ((SIM == type) ? ((struct sockaddr_sim *)&addr)->addr : 
                                    0);
        }
        void SimSetAddress(SIMADDR theAddr)
        {
            type = SIM;
            length = sizeof(SIMADDR); // NSv2 addresses are 4 bytes? (32 bits)
            ((struct sockaddr_sim*)&addr)->addr = theAddr;
        }
#endif // SIMULATE
#ifdef WIN32
            static bool Win32Startup() 
            {
                WSADATA wsaData;
                WORD wVersionRequested = MAKEWORD(2, 2);
                return (0 == WSAStartup(wVersionRequested, &wsaData));
            }
            static void Win32Cleanup() {WSACleanup();}
#endif  // WIN32

        private:
            Type                    type; 
            UINT8                   length;
#ifdef HAVE_IPV6
            struct sockaddr_storage addr;
#else       
#ifdef _WIN32_WCE
            struct sockaddr_in      addr;  // WINCE doesn't like anything else???
#else
            struct sockaddr         addr;
#endif // if/else WIN32_WCE
#endif // if/else (HAVE_IPV6 || WIN32)
};  // end class ProtoAddress

#endif // _PROTO_ADDRESS

