// -*- C++ -*-
//===----------------------------------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES
// SPDX-FileCopyrightText: Copyright (c) Microsoft Corporation.
//
//===----------------------------------------------------------------------===//

#ifndef _CUDA_STD___RANGES_VARIANT_LIKE_H
#define _CUDA_STD___RANGES_VARIANT_LIKE_H

#include <cuda/std/detail/__config>

#if defined(_CCCL_IMPLICIT_SYSTEM_HEADER_GCC)
#  pragma GCC system_header
#elif defined(_CCCL_IMPLICIT_SYSTEM_HEADER_CLANG)
#  pragma clang system_header
#elif defined(_CCCL_IMPLICIT_SYSTEM_HEADER_MSVC)
#  pragma system_header
#endif // no system header

#include <cuda/std/__concepts/constructible.h>
#include <cuda/std/__concepts/swappable.h>
#include <cuda/std/__memory/addressof.h>
#include <cuda/std/__memory/construct_at.h>
#include <cuda/std/__type_traits/is_nothrow_constructible.h>
#include <cuda/std/__type_traits/is_nothrow_copy_assignable.h>
#include <cuda/std/__type_traits/is_nothrow_copy_constructible.h>
#include <cuda/std/__type_traits/is_nothrow_default_constructible.h>
#include <cuda/std/__type_traits/is_nothrow_move_assignable.h>
#include <cuda/std/__type_traits/is_nothrow_move_constructible.h>
#include <cuda/std/__type_traits/is_swappable.h>
#include <cuda/std/__type_traits/is_trivially_copy_assignable.h>
#include <cuda/std/__type_traits/is_trivially_copy_constructible.h>
#include <cuda/std/__type_traits/is_trivially_destructible.h>
#include <cuda/std/__type_traits/is_trivially_move_assignable.h>
#include <cuda/std/__type_traits/is_trivially_move_constructible.h>
#include <cuda/std/__utility/delegate_constructors.h>
#include <cuda/std/__utility/forward.h>
#include <cuda/std/__utility/move.h>

#include <cuda/std/__cccl/prologue.h>

_CCCL_BEGIN_NAMESPACE_CUDA_STD

template <class _Tp>
_CCCL_CONCEPT __is_trivially_swappable_v =
  is_trivially_destructible_v<_Tp> && is_trivially_move_constructible_v<_Tp> && is_trivially_move_assignable_v<_Tp>
  && !::cuda::std::ranges::__swap::__unqualified_swappable_with<_Tp, _Tp>;

// __variant_like is a simplified variant with just two alternatives that does not need all the complexity of variant

enum class __variant_like_state : unsigned char
{
  __nothing,
  __holds_first,
  __holds_second
};

struct __construct_first
{};
struct __construct_second
{};

template <class _Tp, class _Up, bool = is_trivially_destructible_v<_Tp> && is_trivially_destructible_v<_Up>>
struct __vl_destruct_base
{
  union
  {
    _CCCL_NO_UNIQUE_ADDRESS _Tp __first_;
    _CCCL_NO_UNIQUE_ADDRESS _Up __second_;
  };
  __variant_like_state __contains_;

  _CCCL_EXEC_CHECK_DISABLE
  _CCCL_TEMPLATE(class _Tp2 = _Tp)
  _CCCL_REQUIRES(default_initializable<_Tp2>)
  _CCCL_API constexpr __vl_destruct_base() noexcept(is_nothrow_default_constructible_v<_Tp2>)
      : __first_()
      , __contains_{__variant_like_state::__holds_first}
  {}

  _CCCL_API constexpr explicit __vl_destruct_base(__variant_like_state __state) noexcept
      : __contains_{__state}
  {}

  _CCCL_EXEC_CHECK_DISABLE
  template <class... _Types>
  _CCCL_API constexpr explicit __vl_destruct_base(__construct_first, _Types&&... _Args) noexcept(
    is_nothrow_constructible_v<_Tp, _Types...>)
      : __first_{::cuda::std::forward<_Types>(_Args)...}
      , __contains_{__variant_like_state::__holds_first}
  {}

  _CCCL_EXEC_CHECK_DISABLE
  _CCCL_TEMPLATE(class... _Types)
  _CCCL_REQUIRES((!same_as<_Tp, _Up>) )
  _CCCL_API constexpr explicit __vl_destruct_base(__construct_second, _Types&&... _Args) noexcept(
    is_nothrow_constructible_v<_Up, _Types...>)
      : __second_{::cuda::std::forward<_Types>(_Args)...}
      , __contains_{__variant_like_state::__holds_second}
  {}

  _CCCL_API _CCCL_CONSTEXPR_CXX20 ~__vl_destruct_base() noexcept
  {
    __clear();
  }

protected:
  _CCCL_EXEC_CHECK_DISABLE
  _CCCL_API _CCCL_CONSTEXPR_CXX20 void __clear() noexcept
  {
    switch (__contains_)
    {
      case __variant_like_state::__holds_first:
        ::cuda::std::destroy_at(::cuda::std::addressof(__first_));
        break;
      case __variant_like_state::__holds_second:
        ::cuda::std::destroy_at(::cuda::std::addressof(__second_));
        break;
      case __variant_like_state::__nothing:
        break;
    }
    __contains_ = __variant_like_state::__nothing;
  }
};

template <class _Tp, class _Up>
struct __vl_destruct_base<_Tp, _Up, true>
{
  union
  {
    _CCCL_NO_UNIQUE_ADDRESS _Tp __first_;
    _CCCL_NO_UNIQUE_ADDRESS _Up __second_;
  };
  __variant_like_state __contains_;

  _CCCL_EXEC_CHECK_DISABLE
  _CCCL_TEMPLATE(class _Tp2 = _Tp)
  _CCCL_REQUIRES(default_initializable<_Tp2>)
  _CCCL_API constexpr __vl_destruct_base() noexcept(is_nothrow_default_constructible_v<_Tp2>)
      : __first_()
      , __contains_{__variant_like_state::__holds_first}
  {}

  _CCCL_API constexpr explicit __vl_destruct_base(__variant_like_state __state) noexcept
      : __contains_{__state}
  {}

  _CCCL_EXEC_CHECK_DISABLE
  template <class... _Types>
  _CCCL_API constexpr explicit __vl_destruct_base(__construct_first, _Types&&... _Args) noexcept(
    is_nothrow_constructible_v<_Tp, _Types...>)
      : __first_{::cuda::std::forward<_Types>(_Args)...}
      , __contains_{__variant_like_state::__holds_first}
  {}

  _CCCL_EXEC_CHECK_DISABLE
  _CCCL_TEMPLATE(class... _Types)
  _CCCL_REQUIRES((!same_as<_Tp, _Up>) )
  _CCCL_API constexpr explicit __vl_destruct_base(__construct_second, _Types&&... _Args) noexcept(
    is_nothrow_constructible_v<_Up, _Types...>)
      : __second_{::cuda::std::forward<_Types>(_Args)...}
      , __contains_{__variant_like_state::__holds_second}
  {}

protected:
  _CCCL_API constexpr void __clear() noexcept
  {
    __contains_ = __variant_like_state::__nothing;
  }
};

template <class _Tp, class _Up>
class __variant_like : public __vl_destruct_base<_Tp, _Up>
{
public:
  _CCCL_DELEGATE_CONSTRUCTORS(__variant_like, __vl_destruct_base, _Tp, _Up)

#if _CCCL_HAS_CONCEPTS()
  _CCCL_HIDE_FROM_ABI constexpr __variant_like(const __variant_like&)
    requires is_trivially_copy_constructible_v<_Tp> && is_trivially_copy_constructible_v<_Up>
  = default;
#endif // _CCCL_HAS_CONCEPTS()

  _CCCL_API _CCCL_CONSTEXPR_CXX20 __variant_like(const __variant_like& __other) noexcept(
    is_nothrow_copy_constructible_v<_Tp> && is_nothrow_copy_constructible_v<_Up>)
      : __base{__other.__contains_}
  {
    switch (this->__contains_)
    {
      case __variant_like_state::__holds_first:
        ::cuda::std::__construct_at(::cuda::std::addressof(this->__first_), __other.__first_);
        break;
      case __variant_like_state::__holds_second:
        ::cuda::std::__construct_at(::cuda::std::addressof(this->__second_), __other.__second_);
        break;
      case __variant_like_state::__nothing:
        break;
    }
  }

  _CCCL_TEMPLATE(class _OTp, class _OUp)
  _CCCL_REQUIRES((!same_as<__variant_like<_OTp, _OUp>, __variant_like>) )
  _CCCL_API _CCCL_CONSTEXPR_CXX20 __variant_like(const __variant_like<_OTp, _OUp>& __other) noexcept(
    is_nothrow_constructible_v<_Tp, const _OTp&> && is_nothrow_constructible_v<_Up, const _OUp&>)
      : __base{__other.__contains_}
  {
    switch (__other.__contains_)
    {
      case __variant_like_state::__holds_first:
        ::cuda::std::__construct_at(::cuda::std::addressof(this->__first_), __other.__first_);
        break;
      case __variant_like_state::__holds_second:
        ::cuda::std::__construct_at(::cuda::std::addressof(this->__second_), __other.__second_);
        break;
      case __variant_like_state::__nothing:
        break;
    }
  }

#if _CCCL_HAS_CONCEPTS()
  _CCCL_HIDE_FROM_ABI constexpr __variant_like(__variant_like&&)
    requires is_trivially_move_constructible_v<_Tp> && is_trivially_move_constructible_v<_Up>
  = default;
#endif // _CCCL_HAS_CONCEPTS()

  _CCCL_API _CCCL_CONSTEXPR_CXX20 __variant_like(__variant_like&& __other) noexcept(
    is_nothrow_move_constructible_v<_Tp> && is_nothrow_move_constructible_v<_Up>)
      : __base{__other.__contains_}
  {
    switch (this->__contains_)
    {
      case __variant_like_state::__holds_first:
        ::cuda::std::__construct_at(::cuda::std::addressof(this->__first_), ::cuda::std::move(__other.__first_));
        break;
      case __variant_like_state::__holds_second:
        ::cuda::std::__construct_at(::cuda::std::addressof(this->__second_), ::cuda::std::move(__other.__second_));
        break;
      case __variant_like_state::__nothing:
        break;
    }
  }

#if _CCCL_HAS_CONCEPTS()
  _CCCL_HIDE_FROM_ABI constexpr __variant_like& operator=(const __variant_like&)
    requires is_trivially_destructible_v<_Tp> && is_trivially_destructible_v<_Up>
            && is_trivially_copy_constructible_v<_Tp> && is_trivially_copy_constructible_v<_Up>
            && is_trivially_copy_assignable_v<_Tp> && is_trivially_copy_assignable_v<_Up>
  = default;
#endif // _CCCL_HAS_CONCEPTS()

  _CCCL_EXEC_CHECK_DISABLE
  _CCCL_API _CCCL_CONSTEXPR_CXX20 __variant_like& operator=(const __variant_like& __other) noexcept(
    is_nothrow_copy_constructible_v<_Tp> && is_nothrow_copy_constructible_v<_Up> && is_nothrow_copy_assignable_v<_Tp>
    && is_nothrow_copy_assignable_v<_Up>)
  {
    if (this->__contains_ == __other.__contains_)
    {
      switch (this->__contains_)
      {
        case __variant_like_state::__holds_first:
          this->__first_ = __other.__first_;
          break;
        case __variant_like_state::__holds_second:
          this->__second_ = __other.__second_;
          break;
        case __variant_like_state::__nothing:
          break;
      }

      return *this;
    }

    this->__clear();

    switch (__other.__contains_)
    {
      case __variant_like_state::__holds_first:
        ::cuda::std::__construct_at(::cuda::std::addressof(this->__first_), __other.__first_);
        break;
      case __variant_like_state::__holds_second:
        ::cuda::std::__construct_at(::cuda::std::addressof(this->__second_), __other.__second_);
        break;
      case __variant_like_state::__nothing:
        break;
    }

    this->__contains_ = __other.__contains_;

    return *this;
  }

#if _CCCL_HAS_CONCEPTS()
  _CCCL_HIDE_FROM_ABI constexpr __variant_like& operator=(__variant_like&&)
    requires is_trivially_destructible_v<_Tp> && is_trivially_destructible_v<_Up>
            && is_trivially_move_constructible_v<_Tp> && is_trivially_move_constructible_v<_Up>
            && is_trivially_move_assignable_v<_Tp> && is_trivially_move_assignable_v<_Up>
  = default;
#endif // _CCCL_HAS_CONCEPTS()

  _CCCL_EXEC_CHECK_DISABLE
  _CCCL_API _CCCL_CONSTEXPR_CXX20 __variant_like& operator=(__variant_like&& __other) noexcept(
    is_nothrow_move_constructible_v<_Tp> && is_nothrow_move_constructible_v<_Up> && is_nothrow_move_assignable_v<_Tp>
    && is_nothrow_move_assignable_v<_Up>)
  {
    if (this->__contains_ == __other.__contains_)
    {
      switch (this->__contains_)
      {
        case __variant_like_state::__holds_first:
          this->__first_ = ::cuda::std::move(__other.__first_);
          break;
        case __variant_like_state::__holds_second:
          this->__second_ = ::cuda::std::move(__other.__second_);
          break;
        case __variant_like_state::__nothing:
          break;
      }

      return *this;
    }

    this->__clear();

    switch (__other.__contains_)
    {
      case __variant_like_state::__holds_first:
        ::cuda::std::__construct_at(::cuda::std::addressof(this->__first_), __other.__first_);
        break;
      case __variant_like_state::__holds_second:
        ::cuda::std::__construct_at(::cuda::std::addressof(this->__second_), __other.__second_);
        break;
      case __variant_like_state::__nothing:
        break;
    }

    this->__contains_ = __other.__contains_;

    return *this;
  }

  _CCCL_DIAG_PUSH
  // MSVC hallucinates that
  //
  // this->__second_ = __other.__second_;
  //
  // is unreachable.
  _CCCL_DIAG_SUPPRESS_MSVC(4702)

  _CCCL_EXEC_CHECK_DISABLE
  _CCCL_TEMPLATE(class _OTp, class _OUp)
  _CCCL_REQUIRES((!same_as<__variant_like<_OTp, _OUp>, __variant_like>) )
  _CCCL_API _CCCL_CONSTEXPR_CXX20 __variant_like& operator=(const __variant_like<_OTp, _OUp>& __other) noexcept(
    is_nothrow_constructible_v<_Tp, const _OTp&> && is_nothrow_constructible_v<_Up, const _OUp&>
    && is_nothrow_assignable_v<_Tp&, const _OTp&> && is_nothrow_assignable_v<_Up&, const _OUp&>)
  {
    if (this->__contains_ == __other.__contains_)
    {
      switch (this->__contains_)
      {
        case __variant_like_state::__holds_first:
          this->__first_ = __other.__first_;
          break;
        case __variant_like_state::__holds_second:
          this->__second_ = __other.__second_;
          break;
        case __variant_like_state::__nothing:
          break;
      }
      return *this;
    }

    this->__clear();

    switch (__other.__contains_)
    {
      case __variant_like_state::__holds_first:
        ::cuda::std::__construct_at(::cuda::std::addressof(this->__first_), __other.__first_);
        break;
      case __variant_like_state::__holds_second:
        ::cuda::std::__construct_at(::cuda::std::addressof(this->__second_), __other.__second_);
        break;
      case __variant_like_state::__nothing:
        break;
    }

    this->__contains_ = __other.__contains_;
    return *this;
  }

  _CCCL_DIAG_POP

  _CCCL_EXEC_CHECK_DISABLE
  _CCCL_TEMPLATE(class _Tp2 = _Tp)
  _CCCL_REQUIRES((!__is_trivially_swappable_v<_Tp2> || !__is_trivially_swappable_v<_Up>) )
  _CCCL_API friend constexpr void swap(__variant_like& __x, __variant_like& __y) noexcept(
    is_nothrow_move_constructible_v<_Tp2> && is_nothrow_move_constructible_v<_Up> && is_nothrow_swappable_v<_Tp2>
    && is_nothrow_swappable_v<_Up>)
  {
    if (__x.__contains_ == __y.__contains_)
    {
      switch (__x.__contains_)
      {
        case __variant_like_state::__holds_first:
          ::cuda::std::ranges::__swap_cpo{}(__x.__first_, __y.__first_);
          break;
        case __variant_like_state::__holds_second:
          ::cuda::std::ranges::__swap_cpo{}(__x.__second_, __y.__second_);
          break;
        case __variant_like_state::__nothing:
          break;
      }
    }
    else
    {
      auto __tmp = ::cuda::std::move(__x);
      __x        = ::cuda::std::move(__y);
      __y        = ::cuda::std::move(__tmp);
    }
  }

  template <class... _Types>
  _CCCL_API _CCCL_CONSTEXPR_CXX20 void
  __emplace_first(_Types&&... _Args) noexcept(is_nothrow_constructible_v<_Tp, _Types...>)
  {
    this->__clear();
    ::cuda::std::__construct_at(::cuda::std::addressof(this->__first_), ::cuda::std::forward<_Types>(_Args)...);
    this->__contains_ = __variant_like_state::__holds_first;
  }

  template <class... _Types>
  _CCCL_API _CCCL_CONSTEXPR_CXX20 void
  __emplace_second(_Types&&... _Args) noexcept(is_nothrow_constructible_v<_Up, _Types...>)
  {
    this->__clear();
    ::cuda::std::__construct_at(::cuda::std::addressof(this->__second_), ::cuda::std::forward<_Types>(_Args)...);
    this->__contains_ = __variant_like_state::__holds_second;
  }

  [[nodiscard]] _CCCL_API constexpr bool __valueless_by_exception() const noexcept
  {
    return this->__contains_ == __variant_like_state::__nothing;
  }
  [[nodiscard]] _CCCL_API constexpr bool __holds_first() const noexcept
  {
    return this->__contains_ == __variant_like_state::__holds_first;
  }
  [[nodiscard]] _CCCL_API constexpr bool __holds_second() const noexcept
  {
    return this->__contains_ == __variant_like_state::__holds_second;
  }
};

_CCCL_END_NAMESPACE_CUDA_STD

#include <cuda/std/__cccl/epilogue.h>

#endif // _CUDA_STD___RANGES_VARIANT_LIKE_H
