/*
 * Copyright (c) 2022 Google Inc. All rights reserved
 *
 * Permission is hereby granted, free of charge, to any person obtaining
 * a copy of this software and associated documentation files
 * (the "Software"), to deal in the Software without restriction,
 * including without limitation the rights to use, copy, modify, merge,
 * publish, distribute, sublicense, and/or sell copies of the Software,
 * and to permit persons to whom the Software is furnished to do so,
 * subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be
 * included in all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
 * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */
#pragma once

#include <kernel/event.h>
#include <kernel/mutex.h>
#include <kernel/thread.h>
#include <lk/compiler.h>

_LIBCPP_BEGIN_NAMESPACE_STD

// Mutex
struct __libcpp_mutex_t {
  mutex_t __m;

  // Default constructor that can be used by _LIBCPP_MUTEX_INITIALIZER
  constexpr __libcpp_mutex_t() noexcept : __m(MUTEX_INITIAL_VALUE(__m)) {}

  // __m is self-referential so disable copies and moves
  __libcpp_mutex_t(const __libcpp_mutex_t&) = delete;
  __libcpp_mutex_t(__libcpp_mutex_t&&) = delete;
  __libcpp_mutex_t& operator=(const __libcpp_mutex_t&) = delete;
  __libcpp_mutex_t& operator=(__libcpp_mutex_t&&) = delete;
};
#define _LIBCPP_MUTEX_INITIALIZER (__libcpp_mutex_t())

#if defined(_M_IX86) || defined(__i386__) || defined(_M_ARM) || defined(__arm__)
typedef void* __libcpp_recursive_mutex_t[6];
#elif defined(_M_AMD64) || defined(__x86_64__) || defined(_M_ARM64) || defined(__aarch64__)
typedef void* __libcpp_recursive_mutex_t[5];
#else
# error Unsupported architecture
#endif

// Condition Variable
struct __libcpp_condvar_t {
  event_t __e;

  // Default constructor that can be used by _LIBCPP_CONDVAR_INITIALIZER
  constexpr __libcpp_condvar_t() noexcept : __e(EVENT_INITIAL_VALUE(__e, false, 0)) {}

  // __e is self-referential so disable copies and moves
  __libcpp_condvar_t(const __libcpp_condvar_t&) = delete;
  __libcpp_condvar_t(__libcpp_condvar_t&&) = delete;
  __libcpp_condvar_t& operator=(const __libcpp_condvar_t&) = delete;
  __libcpp_condvar_t& operator=(__libcpp_condvar_t&&) = delete;
};
#define _LIBCPP_CONDVAR_INITIALIZER (__libcpp_condvar_t())

// Execute Once
typedef void* __libcpp_exec_once_flag;
#define _LIBCPP_EXEC_ONCE_INITIALIZER (nullptr)

// Thread ID
typedef thread_t* __libcpp_thread_id;

// Thread
#define _LIBCPP_NULL_THREAD (nullptr)

typedef thread_t* __libcpp_thread_t;

// Thread Local Storage
typedef long __libcpp_tls_key;

#define _LIBCPP_TLS_DESTRUCTOR_CC

static inline _LIBCPP_INLINE_VISIBILITY
int __libcpp_recursive_mutex_init(__libcpp_recursive_mutex_t *__m);

static inline _LIBCPP_INLINE_VISIBILITY
int __libcpp_recursive_mutex_lock(__libcpp_recursive_mutex_t *__m);

static inline _LIBCPP_INLINE_VISIBILITY
bool __libcpp_recursive_mutex_trylock(__libcpp_recursive_mutex_t *__m);

static inline _LIBCPP_INLINE_VISIBILITY
int __libcpp_recursive_mutex_unlock(__libcpp_recursive_mutex_t *__m);

static inline _LIBCPP_INLINE_VISIBILITY
int __libcpp_recursive_mutex_destroy(__libcpp_recursive_mutex_t *__m);

static inline _LIBCPP_INLINE_VISIBILITY
int __libcpp_mutex_lock(__libcpp_mutex_t *__m)
{
  mutex_acquire(&__m->__m);
  return 0;
}

static inline _LIBCPP_INLINE_VISIBILITY
bool __libcpp_mutex_trylock(__libcpp_mutex_t *__m);

static inline _LIBCPP_INLINE_VISIBILITY
int __libcpp_mutex_unlock(__libcpp_mutex_t *__m)
{
  mutex_release(&__m->__m);
  return 0;
}

static inline _LIBCPP_INLINE_VISIBILITY
int __libcpp_mutex_destroy(__libcpp_mutex_t *__m);

// Condition Variable
static inline _LIBCPP_INLINE_VISIBILITY
int __libcpp_condvar_signal(__libcpp_condvar_t *__cv);

static inline _LIBCPP_INLINE_VISIBILITY
int __libcpp_condvar_broadcast(__libcpp_condvar_t *__cv)
{
  event_signal(&__cv->__e, true);
  event_unsignal(&__cv->__e);
  return 0;
}

static inline _LIBCPP_INLINE_VISIBILITY
int __libcpp_condvar_wait(__libcpp_condvar_t *__cv, __libcpp_mutex_t *__m)
{
  event_wait(&__cv->__e);
  return 0;
}

static inline _LIBCPP_INLINE_VISIBILITY
int __libcpp_condvar_timedwait(__libcpp_condvar_t *__cv, __libcpp_mutex_t *__m,
                               timespec *__ts);

static inline _LIBCPP_INLINE_VISIBILITY
int __libcpp_condvar_destroy(__libcpp_condvar_t *__cv);

// Thread id
// Returns non-zero if the thread ids are equal, otherwise 0
static inline _LIBCPP_INLINE_VISIBILITY
bool __libcpp_thread_id_equal(__libcpp_thread_id t1, __libcpp_thread_id t2);

// Returns non-zero if t1 < t2, otherwise 0
static inline _LIBCPP_INLINE_VISIBILITY
bool __libcpp_thread_id_less(__libcpp_thread_id t1, __libcpp_thread_id t2);

// Thread
static inline _LIBCPP_INLINE_VISIBILITY
bool __libcpp_thread_isnull(const __libcpp_thread_t *__t);

static inline _LIBCPP_INLINE_VISIBILITY
int __libcpp_thread_create(__libcpp_thread_t *__t, void *(*__func)(void *),
                           void *__arg);

static inline _LIBCPP_INLINE_VISIBILITY
__libcpp_thread_id __libcpp_thread_get_current_id();

static inline _LIBCPP_INLINE_VISIBILITY
__libcpp_thread_id __libcpp_thread_get_id(const __libcpp_thread_t *__t);

static inline _LIBCPP_INLINE_VISIBILITY
int __libcpp_thread_join(__libcpp_thread_t *__t);

static inline _LIBCPP_INLINE_VISIBILITY
int __libcpp_thread_detach(__libcpp_thread_t *__t);

static inline _LIBCPP_INLINE_VISIBILITY
void __libcpp_thread_sleep_for(const chrono::nanoseconds& __ns);

static inline _LIBCPP_INLINE_VISIBILITY
void __libcpp_thread_yield();

// Thread local storage
static inline _LIBCPP_INLINE_VISIBILITY
int __libcpp_tls_create(__libcpp_tls_key *__key, void (*__at_exit)(void *));

static inline _LIBCPP_INLINE_VISIBILITY
void *__libcpp_tls_get(__libcpp_tls_key __key);

static inline _LIBCPP_INLINE_VISIBILITY
int __libcpp_tls_set(__libcpp_tls_key __key, void *__p);

_LIBCPP_END_NAMESPACE_STD
