/*
 *
 *    Copyright (c) 2020-2021 Project CHIP Authors
 *    Copyright (c) 2019 Nest Labs, Inc.
 *
 *    Licensed under the Apache License, Version 2.0 (the "License");
 *    you may not use this file except in compliance with the License.
 *    You may obtain a copy of the License at
 *
 *        http://www.apache.org/licenses/LICENSE-2.0
 *
 *    Unless required by applicable law or agreed to in writing, software
 *    distributed under the License is distributed on an "AS IS" BASIS,
 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *    See the License for the specific language governing permissions and
 *    limitations under the License.
 */

/**
 *    @file
 *          Provides an generic implementation of ConnectivityManager features
 *          for use on platforms that support Thread.
 */

#ifndef GENERIC_CONNECTIVITY_MANAGER_IMPL_THREAD_CPP
#define GENERIC_CONNECTIVITY_MANAGER_IMPL_THREAD_CPP

#include <lib/support/CodeUtils.h>
#include <platform/internal/CHIPDeviceLayerInternal.h>
#include <platform/internal/GenericConnectivityManagerImpl_Thread.h>

namespace chip {
namespace DeviceLayer {
namespace Internal {

template <class ImplClass>
void GenericConnectivityManagerImpl_Thread<ImplClass>::_OnPlatformEvent(const ChipDeviceEvent * event)
{
    // Define some short-hands for various interesting event conditions.
    const bool threadConnChanged = (event->Type == DeviceEventType::kThreadConnectivityChange &&
                                    event->ThreadConnectivityChange.Result != kConnectivity_NoChange);
    const bool threadAddrChanged = (event->Type == DeviceEventType::kThreadStateChange && event->ThreadStateChange.AddressChanged);
    const bool threadNetDataChanged =
        (event->Type == DeviceEventType::kThreadStateChange && event->ThreadStateChange.NetDataChanged);

    if (threadConnChanged && event->ThreadConnectivityChange.Result == kConnectivity_Established)
    {
        ThreadStackMgrImpl().OnThreadAttachFinished();
    }

    // If any of the above events has occurred, assess whether there's been a change in
    // service connectivity via Thread.
    if (threadConnChanged || threadAddrChanged || threadNetDataChanged)
    {
        UpdateServiceConnectivity();
    }
}

template <class ImplClass>
void GenericConnectivityManagerImpl_Thread<ImplClass>::UpdateServiceConnectivity()
{
    constexpr bool haveServiceConnectivity = false;

    // If service connectivity via Thread has changed, post an event signaling the change.
    if (mFlags.Has(Flags::kHaveServiceConnectivity) != haveServiceConnectivity)
    {
        ChipLogProgress(DeviceLayer, "ConnectivityManager: Service connectivity via Thread %s",
                        (haveServiceConnectivity) ? "ESTABLISHED" : "LOST");

        mFlags.Set(Flags::kHaveServiceConnectivity, haveServiceConnectivity);

        {
            ChipDeviceEvent event{ .Type                      = DeviceEventType::kServiceConnectivityChange,
                                   .ServiceConnectivityChange = { .ViaThread = { .Result = (haveServiceConnectivity)
                                                                                     ? kConnectivity_Established
                                                                                     : kConnectivity_Lost } } };
            event.ServiceConnectivityChange.Overall.Result = event.ServiceConnectivityChange.ViaThread.Result;
            CHIP_ERROR status                              = PlatformMgr().PostEvent(&event);
            if (status != CHIP_NO_ERROR)
            {
                ChipLogError(DeviceLayer, "Failed to post thread connectivity change: %" CHIP_ERROR_FORMAT, status.Format());
            }
        }
    }
}

} // namespace Internal
} // namespace DeviceLayer
} // namespace chip

#endif // GENERIC_CONNECTIVITY_MANAGER_IMPL_THREAD_CPP
