/*
 *  Copyright (c) 2022, 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 Network Name management.
 *
 */

#include "network_name.hpp"

#include "common/locator_getters.hpp"
#include "common/notifier.hpp"

namespace ot {
namespace MeshCoP {

const char NetworkNameManager::sNetworkNameInit[] = "OpenThread";

#if (OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_2)
const char NetworkNameManager::sDomainNameInit[] = "DefaultDomain";
#endif

uint8_t NameData::CopyTo(char *aBuffer, uint8_t aMaxSize) const
{
    MutableData<kWithUint8Length> destData;

    destData.Init(aBuffer, aMaxSize);
    destData.ClearBytes();
    IgnoreError(destData.CopyBytesFrom(*this));

    return destData.GetLength();
}

NameData NetworkName::GetAsData(void) const
{
    return NameData(m8, static_cast<uint8_t>(StringLength(m8, kMaxSize + 1)));
}

Error NetworkName::Set(const char *aNameString)
{
    // When setting `NetworkName` from a string, we treat it as `NameData`
    // with `kMaxSize + 1` chars. `NetworkName::Set(data)` will look
    // for null char in the data (within its given size) to calculate
    // the name's length and ensure that the name fits in `kMaxSize`
    // chars. The `+ 1` ensures that a `aNameString` with length
    // longer than `kMaxSize` is correctly rejected (returning error
    // `kErrorInvalidArgs`).

    Error    error;
    NameData data(aNameString, kMaxSize + 1);

    VerifyOrExit(IsValidUtf8String(aNameString), error = kErrorInvalidArgs);

    error = Set(data);

exit:
    return error;
}

Error NetworkName::Set(const NameData &aNameData)
{
    Error    error  = kErrorNone;
    NameData data   = aNameData;
    uint8_t  newLen = static_cast<uint8_t>(StringLength(data.GetBuffer(), data.GetLength()));

    VerifyOrExit((0 < newLen) && (newLen <= kMaxSize), error = kErrorInvalidArgs);

    data.SetLength(newLen);

    // Ensure the new name does not match the current one.
    if (data.MatchesBytesIn(m8) && m8[newLen] == '\0')
    {
        ExitNow(error = kErrorAlready);
    }

    data.CopyBytesTo(m8);
    m8[newLen] = '\0';

exit:
    return error;
}

bool NetworkName::operator==(const NetworkName &aOther) const
{
    return GetAsData() == aOther.GetAsData();
}

NetworkNameManager::NetworkNameManager(Instance &aInstance)
    : InstanceLocator(aInstance)
{
    IgnoreError(SetNetworkName(sNetworkNameInit));

#if (OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_2)
    IgnoreError(SetDomainName(sDomainNameInit));
#endif
}

Error NetworkNameManager::SetNetworkName(const char *aNameString)
{
    return SignalNetworkNameChange(mNetworkName.Set(aNameString));
}

Error NetworkNameManager::SetNetworkName(const NameData &aNameData)
{
    return SignalNetworkNameChange(mNetworkName.Set(aNameData));
}

Error NetworkNameManager::SignalNetworkNameChange(Error aError)
{
    switch (aError)
    {
    case kErrorNone:
        Get<Notifier>().Signal(kEventThreadNetworkNameChanged);
        break;

    case kErrorAlready:
        Get<Notifier>().SignalIfFirst(kEventThreadNetworkNameChanged);
        aError = kErrorNone;
        break;

    default:
        break;
    }

    return aError;
}

#if (OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_2)
Error NetworkNameManager::SetDomainName(const char *aNameString)
{
    Error error = mDomainName.Set(aNameString);

    return (error == kErrorAlready) ? kErrorNone : error;
}

Error NetworkNameManager::SetDomainName(const NameData &aNameData)
{
    Error error = mDomainName.Set(aNameData);

    return (error == kErrorAlready) ? kErrorNone : error;
}
#endif // (OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_2)

} // namespace MeshCoP
} // namespace ot
