/*******************************************************************************
 *
 * File:     atom.hpp
 *
 * Contents: Facility for atoms, short strings statically encoded as integers.
 *
 * Copyright 2017
 *
 * Author: David Juhasz (david.juhasz@tuwien.ac.at)
 *
 * This implementation is based on the atom implementation of CAF.
 * TODO: Check license.
 *
 ******************************************************************************/

#ifndef ROSA_SUPPORT_ATOM_HPP
#define ROSA_SUPPORT_ATOM_HPP

#include "rosa/support/debug.hpp"

// Atoms can be used to turn short string literals into statically generated
// types. The literals may consist of at most 10 non-special characters, legal
// characters are _0-9A-Za-z and the whitespace character. Special characters
// are turned into whitespace, which may result in different string literals
// being encoded into the same integer value, if any of those contain at least
// one special character. Note that the usage of special characters in the
// string literals used to create atoms cannot be checked by the compiler.
//
// Example:
//
// constexpr AtomValue NameValue = atom("name");
// using NameAtom = AtomConstant<NameValue>;
//
// [](NameAtom){ std::cout << "Argument of type NameAtom"; }(NameAtom::Value)
//

namespace rosa {

// Maximal length of valid atom strings.
constexpr size_t MaxAtomLength = 10;

// Underlying integer type of atom values.
using atom_t = uint64_t;

// Turn atom_t into a strongly typed enumeration, so atom_t values casted to
// AtomValue may be used in a type-safe way.
enum class AtomValue : atom_t {};

// Anonymous namespace with implementational details, consider it private.
namespace {

// Encodes ASCII characters to 6-bit encoding.
constexpr unsigned char AtomEncodingTable[] = {
/*     ..0 ..1 ..2 ..3 ..4 ..5 ..6 ..7 ..8 ..9 ..A ..B ..C ..D ..E ..F  */
/* 0.. */  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
/* 1.. */  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
/* 2.. */  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
/* 3.. */  1,  2,  3,  4,  5,  6,  7,  8,  9,  10, 0,  0,  0,  0,  0,  0,
/* 4.. */  0, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25,
/* 5.. */ 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36,  0,  0,  0,  0, 37,
/* 6.. */  0, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52,
/* 7.. */ 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63,  0,  0,  0,  0,  0};

// Decodes 6-bit characters to ASCII
constexpr char AtomDecodingTable[] = " 0123456789"
                                     "ABCDEFGHIJKLMNOPQRSTUVWXYZ_"
                                     "abcdefghijklmnopqrstuvwxyz";

// Encodes one character and updates the integer representation.
constexpr atom_t nextInterim(atom_t Current, size_t CharCode) {
  return (Current << 6) | AtomEncodingTable[(CharCode <= 0x7F) ? CharCode : 0];
}

// Encodes a C-string into an integer value to be used as AtomValue.
constexpr atom_t atomValue(const char *CStr, atom_t Interim = 0xF) {
  return (*CStr == '\0')
             ? Interim
             : atomValue(CStr + 1,
                         nextInterim(Interim, static_cast<size_t>(*CStr)));
}

} // End namespace

// Converts an AtomValue into string.
std::string to_string(const AtomValue &What);

// Creates an AtomValue from the given string.
AtomValue atom_from_string(const std::string &S);

// Creates an AtomValue from the given string literal.
template <size_t Size>
constexpr AtomValue atom(char const (&Str)[Size]) {
  // Last character is the NULL terminator.
  STATIC_ASSERT(Size <= MaxAtomLength + 1,
                "Too many characters in atom definition");
  return static_cast<AtomValue>(atomValue(Str));
}

// Lifts an AtomValue to a compile-time constant.
template <AtomValue V>
struct AtomConstant {
  constexpr AtomConstant() {
    // nop
  }

  // Returns the wrapped value.
  constexpr operator AtomValue() const { return V; }

  // Returns the wrapped value as an integer as type atom_t.
  static constexpr atom_t uint_value() { return static_cast<atom_t>(V); }

  // Returns an instance *of this constant* (*not* an `AtomValue`).
  static const AtomConstant Value;
};

// Implementation of the static member field Value of AtomConstant.
template <AtomValue V>
const AtomConstant<V> AtomConstant<V>::Value = AtomConstant<V>{};

// Converts an AtomConstant into string.
template <AtomValue V>
std::string to_string(const AtomConstant<V> &) {
  return to_string(V);
}

} // End namespace rosa

#endif // ROSA_SUPPORT_ATOM_HPP

