/*
 *  Copyright (c) 2016-21, The OpenThread Authors.
 *  All rights reserved.
 *
 *  Redistribution and use in source and binary forms, with or without
 *  modification, are permitted provided that the following conditions are met:
 *  1. Redistributions of source code must retain the above copyright
 *     notice, this list of conditions and the following disclaimer.
 *  2. Redistributions in binary form must reproduce the above copyright
 *     notice, this list of conditions and the following disclaimer in the
 *     documentation and/or other materials provided with the distribution.
 *  3. Neither the name of the copyright holder nor the
 *     names of its contributors may be used to endorse or promote products
 *     derived from this software without specific prior written permission.
 *
 *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 *  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 *  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 *  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
 *  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 *  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 *  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 *  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 *  POSSIBILITY OF SUCH DAMAGE.
 */

/**
 * @file
 *   This file implements Thread Network Data related types and constants.
 */

#include "network_data_types.hpp"

#include "instance/instance.hpp"
#include "thread/network_data_tlvs.hpp"

namespace ot {
namespace NetworkData {

#if OPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE

static bool IsPrefixValid(Instance &aInstance, const Ip6::Prefix &aPrefix)
{
    // Check that prefix length is within the valid range and the prefix
    // does not overlap with the mesh-local prefix.

    return aPrefix.IsValid() && !aPrefix.ContainsPrefix(aInstance.Get<Mle::Mle>().GetMeshLocalPrefix());
}

bool OnMeshPrefixConfig::IsValid(Instance &aInstance) const
{
    bool isValid = false;

    if (mDhcp && mSlaac)
    {
        // A valid prefix MUST NOT allow both DHCPv6 and SLAAC for
        // address configuration.
        ExitNow();
    }

    if (mSlaac)
    {
        // An IPv6 address prefix used for stateless auto-configuration
        // [RFC4862] of an IEEE 802.15.4 interface MUST have a length of
        // 64 bits.
        VerifyOrExit(GetPrefix().GetLength() == Ip6::NetworkPrefix::kLength);
    }

    VerifyOrExit(IsRoutePreferenceValid(mPreference));
    VerifyOrExit(IsPrefixValid(aInstance, GetPrefix()));
    VerifyOrExit(GetPrefix().GetLength() > 0);

    isValid = true;

exit:
    return isValid;
}

uint16_t OnMeshPrefixConfig::ConvertToTlvFlags(void) const
{
    uint16_t flags = 0;

    if (mPreferred)
    {
        flags |= BorderRouterEntry::kPreferredFlag;
    }

    if (mSlaac)
    {
        flags |= BorderRouterEntry::kSlaacFlag;
    }

    if (mDhcp)
    {
        flags |= BorderRouterEntry::kDhcpFlag;
    }

    if (mConfigure)
    {
        flags |= BorderRouterEntry::kConfigureFlag;
    }

    if (mDefaultRoute)
    {
        flags |= BorderRouterEntry::kDefaultRouteFlag;
    }

    if (mOnMesh)
    {
        flags |= BorderRouterEntry::kOnMeshFlag;
    }

    if (mNdDns)
    {
        flags |= BorderRouterEntry::kNdDnsFlag;
    }

#if OPENTHREAD_FTD && OPENTHREAD_CONFIG_BACKBONE_ROUTER_ENABLE
    if (mDp)
    {
        flags |= BorderRouterEntry::kDpFlag;
    }
#endif

    flags |= (static_cast<uint16_t>(RoutePreferenceToValue(mPreference)) << BorderRouterEntry::kPreferenceOffset);

    return flags;
}

#endif // OPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE

void OnMeshPrefixConfig::SetFrom(const PrefixTlv         &aPrefixTlv,
                                 const BorderRouterTlv   &aBorderRouterTlv,
                                 const BorderRouterEntry &aBorderRouterEntry)
{
    Clear();

    aPrefixTlv.CopyPrefixTo(GetPrefix());
    SetFromTlvFlags(aBorderRouterEntry.GetFlags());
    mRloc16 = aBorderRouterEntry.GetRloc();
    mStable = aBorderRouterTlv.IsStable();
}

void OnMeshPrefixConfig::SetFromTlvFlags(uint16_t aFlags)
{
    mPreferred    = ((aFlags & BorderRouterEntry::kPreferredFlag) != 0);
    mSlaac        = ((aFlags & BorderRouterEntry::kSlaacFlag) != 0);
    mDhcp         = ((aFlags & BorderRouterEntry::kDhcpFlag) != 0);
    mConfigure    = ((aFlags & BorderRouterEntry::kConfigureFlag) != 0);
    mDefaultRoute = ((aFlags & BorderRouterEntry::kDefaultRouteFlag) != 0);
    mOnMesh       = ((aFlags & BorderRouterEntry::kOnMeshFlag) != 0);
    mNdDns        = ((aFlags & BorderRouterEntry::kNdDnsFlag) != 0);
    mDp           = ((aFlags & BorderRouterEntry::kDpFlag) != 0);
    mPreference   = RoutePreferenceFromValue(static_cast<uint8_t>(aFlags >> BorderRouterEntry::kPreferenceOffset));
}

#if OPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE
bool ExternalRouteConfig::IsValid(Instance &aInstance) const
{
    bool isValid = false;

    if (mNat64)
    {
        VerifyOrExit(GetPrefix().IsValidNat64());
    }

    VerifyOrExit(IsRoutePreferenceValid(mPreference));
    VerifyOrExit(IsPrefixValid(aInstance, GetPrefix()));

    isValid = true;

exit:
    return isValid;
}

uint8_t ExternalRouteConfig::ConvertToTlvFlags(void) const
{
    uint8_t flags = 0;

    if (mNat64)
    {
        flags |= HasRouteEntry::kNat64Flag;
    }

    if (mAdvPio)
    {
        flags |= HasRouteEntry::kAdvPioFlag;
    }

    flags |= (RoutePreferenceToValue(mPreference) << HasRouteEntry::kPreferenceOffset);

    return flags;
}

#endif // OPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE

void ExternalRouteConfig::SetFrom(Instance            &aInstance,
                                  const PrefixTlv     &aPrefixTlv,
                                  const HasRouteTlv   &aHasRouteTlv,
                                  const HasRouteEntry &aHasRouteEntry)
{
    Clear();

    aPrefixTlv.CopyPrefixTo(GetPrefix());
    SetFromTlvFlags(aHasRouteEntry.GetFlags());
    mStable              = aHasRouteTlv.IsStable();
    mRloc16              = aHasRouteEntry.GetRloc();
    mNextHopIsThisDevice = (aHasRouteEntry.GetRloc() == aInstance.Get<Mle::MleRouter>().GetRloc16());
}

void ExternalRouteConfig::SetFromTlvFlags(uint8_t aFlags)
{
    mNat64      = ((aFlags & HasRouteEntry::kNat64Flag) != 0);
    mAdvPio     = ((aFlags & HasRouteEntry::kAdvPioFlag) != 0);
    mPreference = RoutePreferenceFromValue(aFlags >> HasRouteEntry::kPreferenceOffset);
}

bool ServiceConfig::ServerConfig::operator==(const ServerConfig &aOther) const
{
    return (mStable == aOther.mStable) && (mServerDataLength == aOther.mServerDataLength) &&
           (memcmp(mServerData, aOther.mServerData, mServerDataLength) == 0);
}

void ServiceConfig::ServerConfig::SetFrom(const ServerTlv &aServerTlv)
{
    ServerData serverData;

    aServerTlv.GetServerData(serverData);
    mStable           = aServerTlv.IsStable();
    mRloc16           = aServerTlv.GetServer16();
    mServerDataLength = serverData.GetLength();
    serverData.CopyBytesTo(mServerData);
}

bool ServiceConfig::operator==(const ServiceConfig &aOther) const
{
    return (mEnterpriseNumber == aOther.mEnterpriseNumber) && (mServiceDataLength == aOther.mServiceDataLength) &&
           (memcmp(mServiceData, aOther.mServiceData, mServiceDataLength) == 0) &&
           (GetServerConfig() == aOther.GetServerConfig());
}

void ServiceConfig::SetFrom(const ServiceTlv &aServiceTlv, const ServerTlv &aServerTlv)
{
    ServiceData serviceData;

    Clear();

    aServiceTlv.GetServiceData(serviceData);
    mServiceId         = aServiceTlv.GetServiceId();
    mEnterpriseNumber  = aServiceTlv.GetEnterpriseNumber();
    mServiceDataLength = serviceData.GetLength();
    serviceData.CopyBytesTo(mServiceData);
    GetServerConfig().SetFrom(aServerTlv);
}

void LowpanContextInfo::SetFrom(const PrefixTlv &aPrefixTlv, const ContextTlv &aContextTlv)
{
    mContextId    = aContextTlv.GetContextId();
    mCompressFlag = aContextTlv.IsCompress();
    aPrefixTlv.CopyPrefixTo(GetPrefix());
    GetPrefix().SetLength(aContextTlv.GetContextLength());
}

} // namespace NetworkData
} // namespace ot
