/*
 *  Copyright (c) 2016-2017, 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 the OpenThread Instance class.
 */

#include "instance.hpp"

#include <openthread/platform/misc.h>

#include "common/new.hpp"
#include "utils/heap.hpp"

namespace ot {

#if (OPENTHREAD_FTD + OPENTHREAD_MTD + OPENTHREAD_RADIO) != 1
#error "Exactly one of {OPENTHREAD_FTD, OPENTHREAD_MTD, OPENTHREAD_RADIO} MUST be set"
#endif

// Size of `ot::Instance` in `uint64_t` unit (aligned).
constexpr size_t kInstanceSizeInUint64s = DivideAndRoundUp<size_t>(sizeof(Instance), sizeof(uint64_t));

#if !OPENTHREAD_CONFIG_MULTIPLE_INSTANCE_ENABLE
// The raw storage used for OpenThread instance (in single-instance case)
uint64_t gInstanceRaw[kInstanceSizeInUint64s];
#endif

#if OPENTHREAD_CONFIG_MULTIPLE_INSTANCE_ENABLE && OPENTHREAD_CONFIG_LOG_INSTANCE_AWARE_API_ENABLE
// The currently active instance
Instance *gActiveInstance = nullptr;
#endif

#if OPENTHREAD_CONFIG_MULTIPLE_INSTANCE_ENABLE && OPENTHREAD_CONFIG_MULTIPLE_STATIC_INSTANCE_ENABLE

constexpr size_t kMultiInstanceSizeInUint64s = (Instance::kNumStaticInstances * kInstanceSizeInUint64s);

// Define the raw storage used for OpenThread instance (in multi-instance case).
static uint64_t gMultiInstanceRaw[kMultiInstanceSizeInUint64s];

#endif

#if OPENTHREAD_MTD || OPENTHREAD_FTD
#if !OPENTHREAD_CONFIG_HEAP_EXTERNAL_ENABLE
OT_DEFINE_ALIGNED_VAR(sHeapRaw, sizeof(Utils::Heap), uint64_t);
Utils::Heap *Instance::sHeap{nullptr};
#endif
#endif

#if OPENTHREAD_CONFIG_LOG_LEVEL_DYNAMIC_ENABLE && OPENTHREAD_CONFIG_MULTIPLE_INSTANCE_ENABLE
LogLevel Instance::sGlobalLogLevel = static_cast<LogLevel>(OPENTHREAD_CONFIG_LOG_LEVEL_INIT);
#endif

Instance::Instance(void)
    : mActiveInstanceTracker(*this)
    , mTimerMilliScheduler(*this)
#if OPENTHREAD_CONFIG_PLATFORM_USEC_TIMER_ENABLE
    , mTimerMicroScheduler(*this)
#endif
    , mRadio(*this)
#if OPENTHREAD_CONFIG_UPTIME_ENABLE
    , mUptimeTracker(*this)
#endif
#if OPENTHREAD_CONFIG_OTNS_ENABLE
    , mOtns(*this)
#endif
#if OPENTHREAD_MTD || OPENTHREAD_FTD
    , mNotifier(*this)
    , mTimeTicker(*this)
    , mSettings(*this)
    , mSettingsDriver(*this)
    , mMessagePool(*this)
#if OPENTHREAD_CONFIG_PLATFORM_DNSSD_ENABLE || OPENTHREAD_CONFIG_MULTICAST_DNS_ENABLE
    // DNS-SD (mDNS) platform is initialized early to
    // allow other modules to use it.
    , mDnssd(*this)
#endif
#if OPENTHREAD_CONFIG_MULTICAST_DNS_ENABLE
    , mMdnsCore(*this)
#endif
#if OPENTHREAD_CONFIG_PLATFORM_KEY_REFERENCES_ENABLE
    , mCryptoStorageKeyRefManager(*this)
#endif
    , mIp6(*this)
    , mThreadNetif(*this)
    , mTmfAgent(*this)
#if OPENTHREAD_CONFIG_DHCP6_CLIENT_ENABLE
    , mDhcp6Client(*this)
#endif
#if OPENTHREAD_CONFIG_DHCP6_SERVER_ENABLE
    , mDhcp6Server(*this)
#endif
#if OPENTHREAD_CONFIG_NEIGHBOR_DISCOVERY_AGENT_ENABLE
    , mNeighborDiscoveryAgent(*this)
#endif
#if OPENTHREAD_CONFIG_IP6_SLAAC_ENABLE
    , mSlaac(*this)
#endif
#if OPENTHREAD_CONFIG_DNS_CLIENT_ENABLE
    , mDnsClient(*this)
#endif
#if OPENTHREAD_CONFIG_SRP_CLIENT_ENABLE
    , mSrpClient(*this)
#endif
#if OPENTHREAD_CONFIG_SRP_CLIENT_BUFFERS_ENABLE
    , mSrpClientBuffers(*this)
#endif
#if OPENTHREAD_CONFIG_DNSSD_SERVER_ENABLE
    , mDnssdServer(*this)
#endif
#if OPENTHREAD_CONFIG_DNS_DSO_ENABLE
    , mDnsDso(*this)
#endif
#if OPENTHREAD_CONFIG_SNTP_CLIENT_ENABLE
    , mSntpClient(*this)
#endif
#if OPENTHREAD_FTD && OPENTHREAD_CONFIG_BACKBONE_ROUTER_ENABLE
    , mBackboneRouterLocal(*this)
#endif
    , mActiveDataset(*this)
    , mPendingDataset(*this)
    , mNetworkIdentity(*this)
    , mIp6Filter(*this)
    , mKeyManager(*this)
    , mLowpan(*this)
    , mMac(*this)
    , mMessageFramer(*this)
    , mMeshForwarder(*this)
    , mMle(*this)
    , mDiscoverScanner(*this)
    , mAddressResolver(*this)
#if OPENTHREAD_CONFIG_MULTI_RADIO
    , mRadioSelector(*this)
#endif
#if OPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE || OPENTHREAD_CONFIG_TMF_NETDATA_SERVICE_ENABLE
    , mNetworkDataLocal(*this)
#endif
    , mNetworkDataLeader(*this)
#if OPENTHREAD_FTD || OPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE || OPENTHREAD_CONFIG_TMF_NETDATA_SERVICE_ENABLE
    , mNetworkDataNotifier(*this)
#endif
#if OPENTHREAD_CONFIG_NETDATA_PUBLISHER_ENABLE
    , mNetworkDataPublisher(*this)
#endif
    , mNetworkDataServiceManager(*this)
    , mVendorInfo(*this)
    , mNetDiagServer(*this)
#if OPENTHREAD_CONFIG_TMF_NETDIAG_CLIENT_ENABLE
    , mNetDiagClient(*this)
#endif
#if OPENTHREAD_CONFIG_BORDER_AGENT_ENABLE
    , mBorderAgentTxtData(*this)
    , mBorderAgentManager(*this)
#endif
#if OPENTHREAD_CONFIG_BORDER_AGENT_ENABLE && OPENTHREAD_CONFIG_BORDER_AGENT_ADMITTER_ENABLE
    , mBorderAgentAdmitter(*this)
#endif
#if OPENTHREAD_CONFIG_BORDER_AGENT_ENABLE && OPENTHREAD_CONFIG_BORDER_AGENT_EPHEMERAL_KEY_ENABLE
    , mBorderAgentEphemeralKeyManager(*this)
#endif
#if OPENTHREAD_CONFIG_BORDER_AGENT_TRACKER_ENABLE
    , mBorderAgentTracker(*this)
#endif
#if OPENTHREAD_CONFIG_COMMISSIONER_ENABLE && OPENTHREAD_FTD
    , mCommissioner(*this)
#endif
#if OPENTHREAD_CONFIG_SECURE_TRANSPORT_ENABLE
    , mTmfSecureAgent(*this)
#endif
#if OPENTHREAD_CONFIG_SEEKER_ENABLE || OPENTHREAD_CONFIG_JOINER_ENABLE
    , mSeeker(*this)
#endif
#if OPENTHREAD_CONFIG_JOINER_ENABLE
    , mJoiner(*this)
#endif
#if OPENTHREAD_CONFIG_JAM_DETECTION_ENABLE
    , mJamDetector(*this)
#endif
#if OPENTHREAD_FTD
    , mJoinerRouter(*this)
    , mLeader(*this)
#endif
#if (OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_2)
    , mBackboneRouterLeader(*this)
#endif
#if OPENTHREAD_FTD && OPENTHREAD_CONFIG_BACKBONE_ROUTER_ENABLE
    , mBackboneRouterManager(*this)
#endif
#if OPENTHREAD_CONFIG_MLR_ENABLE || (OPENTHREAD_FTD && OPENTHREAD_CONFIG_TMF_PROXY_MLR_ENABLE)
    , mMlrManager(*this)
#endif

#if OPENTHREAD_CONFIG_DUA_ENABLE || (OPENTHREAD_FTD && OPENTHREAD_CONFIG_TMF_PROXY_DUA_ENABLE)
    , mDuaManager(*this)
#endif
#if OPENTHREAD_CONFIG_SRP_SERVER_ENABLE
    , mSrpServer(*this)
#if OPENTHREAD_CONFIG_SRP_SERVER_ADVERTISING_PROXY_ENABLE
    , mSrpAdvertisingProxy(*this)
#endif
#endif
#if OPENTHREAD_FTD
    , mChildSupervisor(*this)
#endif
    , mSupervisionListener(*this)
    , mAnnounceBegin(*this)
    , mPanIdQuery(*this)
    , mEnergyScan(*this)
#if OPENTHREAD_CONFIG_TMF_ANYCAST_LOCATOR_ENABLE
    , mAnycastLocator(*this)
#endif
#if OPENTHREAD_CONFIG_TIME_SYNC_ENABLE
    , mTimeSync(*this)
#endif
#if OPENTHREAD_CONFIG_MLE_LINK_METRICS_INITIATOR_ENABLE
    , mInitiator(*this)
#endif
#if OPENTHREAD_CONFIG_MLE_LINK_METRICS_SUBJECT_ENABLE
    , mSubject(*this)
#endif
#if OPENTHREAD_CONFIG_COAP_API_ENABLE
    , mApplicationCoap(*this)
#endif
#if OPENTHREAD_CONFIG_COAP_SECURE_API_ENABLE
    , mApplicationCoapSecure(*this, kWithLinkSecurity)
#endif
#if OPENTHREAD_CONFIG_BLE_TCAT_ENABLE
    , mBleSecure(*this)
    , mTcatAgent(*this)
#endif
#if OPENTHREAD_CONFIG_PING_SENDER_ENABLE
    , mPingSender(*this)
#endif
#if OPENTHREAD_CONFIG_CHANNEL_MONITOR_ENABLE
    , mChannelMonitor(*this)
#endif
#if OPENTHREAD_CONFIG_CHANNEL_MANAGER_ENABLE && \
    (OPENTHREAD_FTD || OPENTHREAD_CONFIG_CHANNEL_MANAGER_CSL_CHANNEL_SELECT_ENABLE)
    , mChannelManager(*this)
#endif
#if OPENTHREAD_CONFIG_MESH_DIAG_ENABLE && OPENTHREAD_FTD
    , mMeshDiag(*this)
#endif
#if OPENTHREAD_CONFIG_HISTORY_TRACKER_ENABLE
    , mHistoryTrackerLocal(*this)
#if OPENTHREAD_CONFIG_HISTORY_TRACKER_SERVER_ENABLE
    , mHistoryTrackerServer(*this)
#endif
#if OPENTHREAD_CONFIG_HISTORY_TRACKER_CLIENT_ENABLE
    , mHistoryTrackerClient(*this)
#endif
#endif
#if OPENTHREAD_CONFIG_LINK_METRICS_MANAGER_ENABLE
    , mLinkMetricsManager(*this)
#endif
#if (OPENTHREAD_CONFIG_DATASET_UPDATER_ENABLE || OPENTHREAD_CONFIG_CHANNEL_MANAGER_ENABLE) && OPENTHREAD_FTD
    , mDatasetUpdater(*this)
#endif
#if OPENTHREAD_CONFIG_ANNOUNCE_SENDER_ENABLE
    , mAnnounceSender(*this)
#endif
#if OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE
    , mInfraIf(*this)
    , mRxRaTracker(*this)
    , mRoutingManager(*this)
#if OPENTHREAD_CONFIG_BORDER_ROUTING_TRACK_PEER_BR_INFO_ENABLE
    , mNetDataBrTracker(*this)
#endif
#if OPENTHREAD_CONFIG_BORDER_ROUTING_MULTI_AIL_DETECTION_ENABLE
    , mMultiAilDetector(*this)
#endif
#if OPENTHREAD_CONFIG_BORDER_ROUTING_DHCP6_PD_ENABLE && OPENTHREAD_CONFIG_BORDER_ROUTING_DHCP6_PD_CLIENT_ENABLE
    , mDhcp6PdClient(*this)
#endif
#endif
#if OPENTHREAD_CONFIG_NAT64_TRANSLATOR_ENABLE
    , mNat64Translator(*this)
#endif
#if OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE
    , mDnsNameCompressionEnabled(true)
#endif
#endif // OPENTHREAD_MTD || OPENTHREAD_FTD
#if OPENTHREAD_RADIO || OPENTHREAD_CONFIG_LINK_RAW_ENABLE
    , mLinkRaw(*this)
#endif
#if OPENTHREAD_ENABLE_VENDOR_EXTENSION
    , mExtension(Extension::ExtensionBase::Init(*this))
#endif
#if OPENTHREAD_CONFIG_DIAG_ENABLE
    , mDiags(*this)
#endif
#if OPENTHREAD_CONFIG_POWER_CALIBRATION_ENABLE && OPENTHREAD_CONFIG_PLATFORM_POWER_CALIBRATION_ENABLE
    , mPowerCalibration(*this)
#endif
#if OPENTHREAD_CONFIG_LOG_LEVEL_DYNAMIC_ENABLE
    , mLogLevel(static_cast<LogLevel>(OPENTHREAD_CONFIG_LOG_LEVEL_INIT))
#if OPENTHREAD_CONFIG_LOG_LEVEL_OVERRIDE_ENABLE
    , mOriginalLogLevel(kLogLevelNone)
    , mOverrideLogLevel(kLogLevelNone)
    , mIsLogLevelOverridden(false)
#endif
#if OPENTHREAD_CONFIG_MULTIPLE_INSTANCE_ENABLE
    , mIsLogLevelSet(false)
#else
#endif
#endif
    , mIsInitialized(false)
    , mId(Random::NonCrypto::Generate<uint32_t>())
{
#if OPENTHREAD_CONFIG_MULTIPLE_INSTANCE_ENABLE && OPENTHREAD_CONFIG_PLATFORM_KEY_REFERENCES_ENABLE
#if OPENTHREAD_CONFIG_MULTIPLE_STATIC_INSTANCE_ENABLE
    mCryptoStorageKeyRefManager.SetKeyRefExtraOffset(Crypto::Storage::KeyRefManager::kKeyRefExtraOffset * GetIdx(this));
#else
#error "MULTIPLE_INSTANCE (without static allocation) is used with PLATFORM_KEY_REFERENCES_ENABLE " \
       "The `KeyRef` values will be shared across different `Instance` objects"
#endif
#endif
}

#if OPENTHREAD_CONFIG_MULTIPLE_INSTANCE_ENABLE && OPENTHREAD_CONFIG_LOG_INSTANCE_AWARE_API_ENABLE
Instance::~Instance(void) { gActiveInstance = this; }
#endif

#if (OPENTHREAD_MTD || OPENTHREAD_FTD) && !OPENTHREAD_CONFIG_HEAP_EXTERNAL_ENABLE
Utils::Heap &Instance::GetHeap(void)
{
    if (nullptr == sHeap)
    {
        sHeap = new (&sHeapRaw) Utils::Heap();
    }

    return *sHeap;
}
#endif

#if !OPENTHREAD_CONFIG_MULTIPLE_INSTANCE_ENABLE

Instance &Instance::InitSingle(void)
{
    Instance *instance = &Get();

    VerifyOrExit(!instance->mIsInitialized);

    instance = new (&gInstanceRaw) Instance();

    instance->AfterInit();

exit:
    return *instance;
}

Instance &Instance::Get(void)
{
    void *instance = &gInstanceRaw;

    return *static_cast<Instance *>(instance);
}

#else // #if !OPENTHREAD_CONFIG_MULTIPLE_INSTANCE_ENABLE

#if OPENTHREAD_CONFIG_MULTIPLE_STATIC_INSTANCE_ENABLE

Instance *Instance::InitMultiple(uint8_t aIdx)
{
    size_t    bufferSize;
    uint64_t *instanceBuffer = gMultiInstanceRaw + aIdx * kInstanceSizeInUint64s;
    Instance *instance       = reinterpret_cast<Instance *>(instanceBuffer);

    VerifyOrExit(aIdx < kNumStaticInstances);
    VerifyOrExit(!instance->mIsInitialized);

    bufferSize = (&gMultiInstanceRaw[kMultiInstanceSizeInUint64s] - instanceBuffer) * sizeof(uint64_t);
    instance   = Instance::Init(instanceBuffer, &bufferSize);

exit:
    return instance;
}

Instance &Instance::Get(uint8_t aIdx)
{
    void *instance = gMultiInstanceRaw + aIdx * kInstanceSizeInUint64s;
    return *static_cast<Instance *>(instance);
}

uint8_t Instance::GetIdx(Instance *aInstance)
{
    return static_cast<uint8_t>((reinterpret_cast<uint64_t *>(aInstance) - gMultiInstanceRaw) / kInstanceSizeInUint64s);
}

#endif // #if OPENTHREAD_CONFIG_MULTIPLE_STATIC_INSTANCE_ENABLE

Instance *Instance::Init(void *aBuffer, size_t *aBufferSize)
{
    Instance *instance = nullptr;

    VerifyOrExit(aBufferSize != nullptr);

    if (sizeof(Instance) > *aBufferSize)
    {
        *aBufferSize = kInstanceSizeInUint64s * sizeof(uint64_t);
        ExitNow();
    }

    VerifyOrExit(aBuffer != nullptr);

    instance = new (aBuffer) Instance();

    instance->AfterInit();

exit:
    return instance;
}

#if OPENTHREAD_CONFIG_LOG_INSTANCE_AWARE_API_ENABLE
Instance *Instance::GetActiveInstance(void) { return gActiveInstance; }
#endif

#endif // OPENTHREAD_CONFIG_MULTIPLE_INSTANCE_ENABLE

void Instance::Reset(void) { otPlatReset(this); }

#if OPENTHREAD_CONFIG_PLATFORM_BOOTLOADER_MODE_ENABLE
Error Instance::ResetToBootloader(void) { return otPlatResetToBootloader(this); }
#endif

#if OPENTHREAD_RADIO
void Instance::ResetRadioStack(void)
{
    mRadio.Init();
    mLinkRaw.Init();
}
#endif

void Instance::AfterInit(void)
{
    mIsInitialized = true;
#if OPENTHREAD_MTD || OPENTHREAD_FTD

    Get<KeyManager>().Init();
    Get<Mac::Mac>().Init();

    // Restore datasets and network information

    Get<Settings>().Init();
    Get<Mle::Mle>().Restore();

#if OPENTHREAD_CONFIG_RADIO_LINK_TREL_ENABLE
    Get<Trel::Link>().AfterInit();
#endif

#if OPENTHREAD_CONFIG_MULTICAST_DNS_ENABLE
    Get<Dns::Multicast::Core>().AfterInstanceInit();
#endif

#endif // OPENTHREAD_MTD || OPENTHREAD_FTD

#if OPENTHREAD_ENABLE_VENDOR_EXTENSION
    Get<Extension::ExtensionBase>().SignalInstanceInit();
#endif
}

void Instance::Finalize(void)
{
    VerifyOrExit(mIsInitialized);

    mIsInitialized = false;

#if OPENTHREAD_MTD || OPENTHREAD_FTD
    Get<Mle::Mle>().Stop();
    Get<ThreadNetif>().Down();
    Get<Mac::Mac>().SetEnabled(false);
#if OPENTHREAD_CONFIG_PLATFORM_KEY_REFERENCES_ENABLE
    Get<KeyManager>().DestroyTemporaryKeys();
#endif

    Get<Settings>().Deinit();
#endif

    IgnoreError(Get<Mac::SubMac>().Disable());

    this->~Instance();

exit:
    return;
}

#if OPENTHREAD_MTD || OPENTHREAD_FTD

void Instance::FactoryReset(void)
{
    Get<Settings>().Wipe();
#if OPENTHREAD_CONFIG_PLATFORM_KEY_REFERENCES_ENABLE
    Get<KeyManager>().DestroyTemporaryKeys();
    Get<KeyManager>().DestroyPersistentKeys();
#endif
    otPlatReset(this);
}

Error Instance::ErasePersistentInfo(void)
{
    Error error = kErrorNone;

    VerifyOrExit(Get<Mle::Mle>().IsDisabled(), error = kErrorInvalidState);
    Get<Settings>().Wipe();
#if OPENTHREAD_CONFIG_PLATFORM_KEY_REFERENCES_ENABLE
    Get<KeyManager>().DestroyTemporaryKeys();
    Get<KeyManager>().DestroyPersistentKeys();
#endif

exit:
    return error;
}

#if OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE
Error Instance::SetDnsNameCompressionEnabled(bool aEnabled)
{
    Error error = kErrorNone;

#if OPENTHREAD_CONFIG_MULTICAST_DNS_ENABLE
    if (Get<Dns::Multicast::Core>().IsEnabled())
    {
        VerifyOrExit(aEnabled, error = kErrorNotCapable);
    }
#endif

    mDnsNameCompressionEnabled = aEnabled;
    ExitNow();

exit:
    return error;
}
#endif

void Instance::GetBufferInfo(BufferInfo &aInfo)
{
    aInfo.Clear();

    aInfo.mTotalBuffers   = Get<MessagePool>().GetTotalBufferCount();
    aInfo.mFreeBuffers    = Get<MessagePool>().GetFreeBufferCount();
    aInfo.mMaxUsedBuffers = Get<MessagePool>().GetMaxUsedBufferCount();

    Get<MeshForwarder>().GetQueueInfo(aInfo.m6loSendQueue, aInfo.m6loReassemblyQueue);
    Get<Ip6::Ip6>().GetSendQueueInfo(aInfo.mIp6Queue);

#if OPENTHREAD_FTD
    Get<Ip6::Mpl>().GetBufferedMessageSetInfo(aInfo.mMplQueue);
#endif

    Get<Mle::Mle>().GetMessageQueueInfo(aInfo.mMleQueue);

    Get<Tmf::Agent>().GetRequestAndCachedResponsesQueueInfo(aInfo.mCoapQueue);

#if OPENTHREAD_CONFIG_SECURE_TRANSPORT_ENABLE
    Get<Tmf::SecureAgent>().GetRequestAndCachedResponsesQueueInfo(aInfo.mCoapSecureQueue);
#endif

#if OPENTHREAD_CONFIG_COAP_API_ENABLE
    Get<Coap::ApplicationCoap>().GetRequestAndCachedResponsesQueueInfo(aInfo.mApplicationCoapQueue);
#endif
}

void Instance::ResetBufferInfo(void) { Get<MessagePool>().ResetMaxUsedBufferCount(); }

#endif // OPENTHREAD_MTD || OPENTHREAD_FTD

#if OPENTHREAD_CONFIG_LOG_LEVEL_DYNAMIC_ENABLE

Error Instance::SetLogLevel(LogLevel aLogLevel)
{
    Error error = kErrorNone;

    VerifyOrExit(aLogLevel <= kLogLevelDebg, error = kErrorInvalidArgs);

#if OPENTHREAD_CONFIG_MULTIPLE_INSTANCE_ENABLE && !OPENTHREAD_CONFIG_LOG_INSTANCE_AWARE_API_ENABLE
    ExitNow(error = kErrorNotCapable);
#else
#if OPENTHREAD_CONFIG_LOG_LEVEL_OVERRIDE_ENABLE
    if (mIsLogLevelOverridden)
    {
        mOriginalLogLevel = aLogLevel;
        aLogLevel         = Max(aLogLevel, mOverrideLogLevel);
    }
#endif
    VerifyOrExit(mLogLevel != aLogLevel);
    mLogLevel = aLogLevel;
    SignalLogLevelChange();
#endif

exit:
    return error;
}

void Instance::SignalLogLevelChange(void)
{
#if OPENTHREAD_CONFIG_MULTIPLE_INSTANCE_ENABLE
    mIsLogLevelSet = true;
#else
    otPlatLogHandleLevelChanged(mLogLevel);
#endif

    otPlatLogHandleLogLevelChanged(this, mLogLevel);
}

#if OPENTHREAD_CONFIG_LOG_LEVEL_OVERRIDE_ENABLE
void Instance::OverrideLogLevel(LogLevel aLogLevel)
{
    LogLevel logLevel;

    if (!mIsLogLevelOverridden)
    {
        mOriginalLogLevel     = GetLogLevel();
        mIsLogLevelOverridden = true;
    }

    mOverrideLogLevel = aLogLevel;

    logLevel = Max(mOverrideLogLevel, mOriginalLogLevel);

    VerifyOrExit(mLogLevel != logLevel);
    mLogLevel = logLevel;
    SignalLogLevelChange();

exit:
    return;
}

void Instance::RestoreLogLevel(void)
{
    VerifyOrExit(mIsLogLevelOverridden);
    mIsLogLevelOverridden = false;
    IgnoreError(SetLogLevel(mOriginalLogLevel));

exit:
    return;
}
#endif // OPENTHREAD_CONFIG_LOG_LEVEL_OVERRIDE_ENABLE

#if OPENTHREAD_CONFIG_MULTIPLE_INSTANCE_ENABLE
Error Instance::SetGlobalLogLevel(LogLevel aLogLevel)
{
    Error error = kErrorNone;

    VerifyOrExit(aLogLevel <= kLogLevelDebg, error = kErrorInvalidArgs);
    VerifyOrExit(sGlobalLogLevel != aLogLevel);
    sGlobalLogLevel = aLogLevel;
    otPlatLogHandleLevelChanged(sGlobalLogLevel);

exit:
    return error;
}
#endif

extern "C" OT_TOOL_WEAK void otPlatLogHandleLevelChanged(otLogLevel aLogLevel) { OT_UNUSED_VARIABLE(aLogLevel); }

extern "C" OT_TOOL_WEAK void otPlatLogHandleLogLevelChanged(otInstance *aInstance, otLogLevel aLogLevel)
{
    OT_UNUSED_VARIABLE(aInstance);
    OT_UNUSED_VARIABLE(aLogLevel);
}

#endif // OPENTHREAD_CONFIG_LOG_LEVEL_DYNAMIC_ENABLE

} // namespace ot
