// Copyright (C) Microsoft Corporation. All rights reserved.
#pragma once

#include <string>
#include <array>
#include <arpa/inet.h>

using MacAddress = std::array<std::uint8_t, 6>;

constexpr unsigned int Ipv6MaxPrefixLen = 128;
constexpr unsigned int Ipv4MaxPrefixLen = 32;

#define MAX_PREFIX_LEN(AddressFamily) (((AddressFamily) == AF_INET) ? Ipv4MaxPrefixLen : Ipv6MaxPrefixLen)

enum class IpPrefixOrigin
{
    Other = 0,
    Manual,
    WellKnown,
    Dhcp,
    RouterAdvertisement,
};

enum class IpSuffixOrigin
{
    Other = 0,
    Manual,
    WellKnown,
    Dhcp,
    LinkLayerAddress,
    Random,
};

class Address
{
public:
    Address(short family, size_t prefixLength, const std::string& address);
    Address(short family, size_t prefixLength, const std::string& address, IpPrefixOrigin prefixOrigin, IpSuffixOrigin suffixOrigin, int preferredLifetime);

    bool operator==(const Address& other) const;
    bool operator!=(const Address& other) const;

    void ConvertToBytes(void* ptr) const;

    void SetIsPrefixRouteAutogenerationDisabled(bool disable) noexcept
    {
        m_disableAutogeneratedPrefixRoute = disable;
    }

    template <typename T>
    T AsBytes() const
    {
        T output;
        ConvertToBytes(&output);

        return output;
    }

    template <typename T>
    static Address FromBytes(int family, int prefixLength, const T& address)
    {
        return FromBytes(family, prefixLength, static_cast<const void*>(&address));
    }

    static Address FromBytes(int family, int prefixLength, const void* ptr);

    static Address FromPrefixString(int family, const std::string& address);

    static Address FromBinary(int family, int prefixLength, const void* data);

    short Family() const noexcept;
    size_t PrefixLength() const noexcept;
    const std::string Addr() const noexcept;
    int Flags() const noexcept;
    int Scope() const noexcept;
    int PreferredLifetime() const noexcept;
    bool IsIpv4() const noexcept;
    bool IsPrefixRouteAutogenerationDisabled() const noexcept;

private:
    short m_family = 0;
    uint32_t m_prefixLength = 0;
    std::string m_address = "";
    IpPrefixOrigin m_prefixOrigin = IpPrefixOrigin::Other;
    IpSuffixOrigin m_suffixOrigin = IpSuffixOrigin::Other;
    uint32_t m_preferredLifetime = 0;
    bool m_disableAutogeneratedPrefixRoute = false;
};

std::ostream& operator<<(std::ostream& out, const Address& address);
