/******************************************************************************
 *                       ____    _    _____                                   *
 *                      / ___|  / \  |  ___|    C++                           *
 *                     | |     / _ \ | |_       Actor                         *
 *                     | |___ / ___ \|  _|      Framework                     *
 *                      \____/_/   \_|_|                                      *
 *                                                                            *
 * Copyright 2011-2018 Dominik Charousset                                     *
 *                                                                            *
 * Distributed under the terms and conditions of the BSD 3-Clause License or  *
 * (at your option) under the terms and conditions of the Boost Software      *
 * License 1.0. See accompanying files LICENSE and LICENSE_ALTERNATIVE.       *
 *                                                                            *
 * If you did not receive a copy of the license files, see                    *
 * http://opensource.org/licenses/BSD-3-Clause and                            *
 * http://www.boost.org/LICENSE_1_0.txt.                                      *
 ******************************************************************************/

#pragma once

#include <cstdint>
#include <map>
#include <set>
#include <string>
#include <vector>

#include "caf/atom.hpp"
#include "caf/byte.hpp"
#include "caf/config.hpp"
#include "caf/detail/core_export.hpp"
#include "caf/detail/squashed_int.hpp"
#include "caf/detail/type_list.hpp"
#include "caf/fwd.hpp"
#include "caf/timespan.hpp"
#include "caf/timestamp.hpp"

namespace caf {

/// Compile-time list of all built-in types.
/// @warning Types are sorted by uniform name.
using sorted_builtin_types
  = detail::type_list<actor,                              // @actor
                      std::vector<actor>,                 // @actorvec
                      actor_addr,                         // @addr
                      std::vector<actor_addr>,            // @addrvec
                      atom_value,                         // @atom
                      std::vector<char>,                  // @charbuf
                      config_value,                       // @config_value
                      down_msg,                           // @down
                      downstream_msg,                     // @downstream_msg
                      duration,                           // @duration
                      error,                              // @error
                      exit_msg,                           // @exit
                      group,                              // @group
                      group_down_msg,                     // @group_down
                      int16_t,                            // @i16
                      int32_t,                            // @i32
                      int64_t,                            // @i64
                      int8_t,                             // @i8
                      long double,                        // @ldouble
                      message,                            // @message
                      message_id,                         // @message_id
                      node_id,                            // @node
                      open_stream_msg,                    // @open_stream_msg
                      std::string,                        // @str
                      std::map<std::string, std::string>, // @strmap
                      strong_actor_ptr,                   // @strong_actor_ptr
                      std::set<std::string>,              // @strset
                      std::vector<std::string>,           // @strvec
                      timeout_msg,                        // @timeout
                      timespan,                           // @timespan
                      timestamp,                          // @timestamp
                      uint16_t,                           // @u16
                      std::u16string,                     // @u16_str
                      uint32_t,                           // @u32
                      std::u32string,                     // @u32_str
                      uint64_t,                           // @u64
                      uint8_t,                            // @u8
                      unit_t,                             // @unit
                      upstream_msg,                       // @upstream_msg
                      weak_actor_ptr,                     // @weak_actor_ptr
                      bool,                               // bool
                      double,                             // double
                      float                               // float
                      >;

/// Computes the type number for `T`.
template <class T, bool IsIntegral = std::is_integral<T>::value>
struct type_nr {
  static constexpr uint16_t value = static_cast<uint16_t>(
    detail::tl_index_of<sorted_builtin_types, T>::value + 1);
};

template <class T>
struct type_nr<T, true> {
  using type = detail::squashed_int_t<T>;
  static constexpr uint16_t value = static_cast<uint16_t>(
    detail::tl_index_of<sorted_builtin_types, type>::value + 1);
};

template <>
struct type_nr<bool, true> {
  static constexpr uint16_t value = static_cast<uint16_t>(
    detail::tl_index_of<sorted_builtin_types, bool>::value + 1);
};

template <atom_value V>
struct type_nr<atom_constant<V>, false> {
  static constexpr uint16_t value = type_nr<atom_value>::value;
};

/// The number of built-in types.
static constexpr size_t type_nrs
  = detail::tl_size<sorted_builtin_types>::value + 1;

/// List of all type names, indexed via `type_nr`.
CAF_CORE_EXPORT extern const char* numbered_type_names[];

template <uint32_t R, uint16_t... Is>
struct type_token_helper;

template <uint32_t R>
struct type_token_helper<R> : std::integral_constant<uint32_t, R> {
  // nop
};

template <uint32_t R, uint16_t I, uint16_t... Is>
struct type_token_helper<R, I, Is...> : type_token_helper<(R << 6) | I, Is...> {
  // nop
};

template <class... Ts>
constexpr uint32_t make_type_token() {
  return type_token_helper<0xFFFFFFFF, type_nr<Ts>::value...>::value;
}

constexpr uint32_t add_to_type_token(uint32_t token, uint16_t tnr) {
  return (token << 6) | tnr;
}

template <class T>
struct make_type_token_from_list_helper;

template <class... Ts>
struct make_type_token_from_list_helper<detail::type_list<Ts...>>
  : type_token_helper<0xFFFFFFFF, type_nr<Ts>::value...> {
  // nop
};

template <class T>
constexpr uint32_t make_type_token_from_list() {
  return make_type_token_from_list_helper<T>::value;
}

} // namespace caf
