/*
 * Copyright (c) 2023 Huawei Device Co., Ltd.
 * 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.
 */

#include "cpp/condition_variable.h"
#include "c/condition_variable.h"
#include "sync/wait_queue.h"
#include "sync/mutex_private.h"
#include "internal_inc/osal.h"
#include "dfx/log/ffrt_log_api.h"

namespace {
FFRT_NOINLINE void CondEmptyLogPrint()
{
    FFRT_LOGE("cond should not be empty");
}

FFRT_NOINLINE void TimePointEmptyLogPrint()
{
    FFRT_LOGE("time_point should not be empty");
}
}

namespace ffrt {
using condition_variable_private = WaitQueue;
}

#ifdef __cplusplus
extern "C" {
#endif

API_ATTRIBUTE((visibility("default")))
int ffrt_cond_init(ffrt_cond_t* cond, const ffrt_condattr_t* attr)
{
    if unlikely(!cond) {
        CondEmptyLogPrint();
        return ffrt_error_inval;
    }
    static_assert(sizeof(ffrt::condition_variable_private) <= ffrt_cond_storage_size,
        "size must be less than ffrt_cond_storage_size");

    new (cond) ffrt::condition_variable_private();
    return ffrt_success;
}

API_ATTRIBUTE((visibility("default")))
int ffrt_cond_signal(ffrt_cond_t* cond)
{
    if unlikely(!cond) {
        CondEmptyLogPrint();
        return ffrt_error_inval;
    }
    auto p = reinterpret_cast<ffrt::condition_variable_private *>(cond);
    p->NotifyOne();
    return ffrt_success;
}

API_ATTRIBUTE((visibility("default")))
int ffrt_cond_broadcast(ffrt_cond_t* cond)
{
    if unlikely(!cond) {
        CondEmptyLogPrint();
        return ffrt_error_inval;
    }
    auto p = reinterpret_cast<ffrt::condition_variable_private *>(cond);
    p->NotifyAll();
    return ffrt_success;
}

API_ATTRIBUTE((visibility("default")))
int ffrt_cond_wait(ffrt_cond_t* cond, ffrt_mutex_t* mutex)
{
    if unlikely(!cond) {
        CondEmptyLogPrint();
        return ffrt_error_inval;
    }
    if unlikely(!mutex) {
        MutexEmptyLogPrint();
        return ffrt_error_inval;
    }
    auto pc = reinterpret_cast<ffrt::condition_variable_private *>(cond);
    auto pm = reinterpret_cast<ffrt::mutexPrivate *>(mutex);
    pc->SuspendAndWait(pm);
    return ffrt_success;
}

API_ATTRIBUTE((visibility("default")))
int ffrt_cond_timedwait(ffrt_cond_t* cond, ffrt_mutex_t* mutex, const struct timespec* time_point)
{
    if unlikely(!cond) {
        CondEmptyLogPrint();
        return ffrt_error_inval;
    }
    if unlikely(!mutex) {
        MutexEmptyLogPrint();
        return ffrt_error_inval;
    }
    if unlikely(!time_point) {
        TimePointEmptyLogPrint();
        return ffrt_error_inval;
    }
    auto pc = reinterpret_cast<ffrt::condition_variable_private *>(cond);
    auto pm = reinterpret_cast<ffrt::mutexPrivate *>(mutex);

    using namespace std::chrono;
    auto duration = seconds{ time_point->tv_sec } + nanoseconds{ time_point->tv_nsec };
    auto tp = ffrt::TimePoint {
        duration_cast<steady_clock::duration>(duration_cast<nanoseconds>(duration))
    };

    return pc->SuspendAndWaitUntil(pm, tp);
}

API_ATTRIBUTE((visibility("default")))
int ffrt_cond_destroy(ffrt_cond_t* cond)
{
    if (!cond) {
        CondEmptyLogPrint();
        return ffrt_error_inval;
    }
    auto p = reinterpret_cast<ffrt::condition_variable_private *>(cond);
    p->~WaitQueue();
    return ffrt_success;
}
#ifdef __cplusplus
}
#endif