/*
 * Copyright (C) 2006-2019 Christopho, Solarus - http://www.solarus-games.org
 *
 * Solarus is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * Solarus is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License along
 * with this program. If not, see <http://www.gnu.org/licenses/>.
 */
#include "solarus/core/Map.h"
#include "solarus/graphics/Color.h"
#include "solarus/lua/LuaException.h"
#include "solarus/lua/LuaTools.h"
#include "solarus/lua/LuaContext.h"
#include "solarus/lua/ScopedLuaRef.h"
#include "solarus/core/Profiler.h"
#include "solarus/core/CurrentQuest.h"
#include <cctype>
#include <sstream>
#include <unordered_set>

namespace Solarus {
namespace LuaTools {

/**
 * \brief For an index in the Lua stack, returns an equivalent positive index.
 *
 * Pseudo-indexes are unchanged.
 *
 * \param l A Lua state.
 * \param index An index in the stack.
 * \return The corresponding positive index.
 */
int get_positive_index(lua_State* l, int index) {

  int positive_index = index;
  if (index < 0 && index >= -lua_gettop(l)) {
    positive_index = lua_gettop(l) + index + 1;
  }

  return positive_index;
}

/**
 * \brief Get pointer to userdata if it is of the given type.
 *
 * This is luaL_testudata from the Lua auxiliary library.
 * It should be replaced when Lua 5.3/LuaJIT 2.1 or higher is required.
 *
 * \param l A Lua context.
 * \param index An index in the stack.
 * \param metatable_name Name of a userdata metatable in the registry.
 * \return Pointer to userdata if it is a userdata of the given type,
 *   nullptr otherwise.
 */
void* test_userdata(lua_State* l, int index, const char* metatable_name) {

  index = get_positive_index(l, index);

  void* udata = lua_touserdata(l, index);
  // ... value ...
  if (udata == nullptr || !lua_getmetatable(l, index)) {
    return nullptr;
  }
  // ... udata ... meta(found)
  lua_getfield(l, LUA_REGISTRYINDEX, metatable_name);
  // ... udata ... meta(found) meta(expected)
  if (lua_rawequal(l, -1, -2) == 0) {
    udata = nullptr;
  }
  lua_pop(l, 2);
  // ... udata ...
  return udata;
}

/* \brief Check to see if a name is a Lua keyword.
 *
 * This is update to date with Lua 5.4.
 * \param name Any valid string.
 * \return True if the name is a keyword, otherwise false.
 */
static bool is_lua_keyword(const std::string& name) {

  static const std::unordered_set<std::string> keywords = {
    "and",
    "break",
    "do",
    "else",
    "elseif",
    "end",
    "false",
    "for",
    "function",
    "goto",
    "if",
    "in",
    "local",
    "nil",
    "not",
    "or",
    "repeat",
    "return",
    "then",
    "true",
    "until",
    "while",
  };
  return keywords.count(name);
}

/**
 * \brief Returns whether the specified name is a valid identifier.
 *
 * Lua keywords are considered valid identifiers by this function.
 *
 * \param name The name to check.
 * \return \c true if the name only contains alphanumeric characters or '_' and
 * does not start with a digit.
 */
bool is_valid_identifier(const std::string& name) {

  if (name.empty() || std::isdigit(name[0])) {
    return false;
  }

  for (char character: name) {
    if (!std::isalnum(character) && character != '_') {
      return false;
    }
  }
  return true;
}

/**
 * \brief Returns whether the specified name is a legal savegame variable name.
 *
 * Same as is_valid_identifier(), except that Lua keywords are considered invalid.
 *
 * \param name The name to check.
 * \return \c true if the name only contains alphanumeric characters or '_',
 * does not start with a digit and is not a Lua keyword.
 */
bool is_valid_lua_identifier(const std::string& name) {

  return is_valid_identifier(name) && !is_lua_keyword(name);
}

/**
 * \brief Returns the type name of a value.
 * Similar to the standard Lua function type(), except that for userdata
 * known by Solarus, it returns the exact Solarus type name.
 * \param l A Lua state.
 * \param index An index in the stack.
 * \return The type name.
 */
std::string get_type_name(lua_State* l, int index) {

  std::string module_name;
  if (!LuaContext::is_solarus_userdata(l, index, module_name)) {
    // Return the same thing as the usual Lua type() function.
    return luaL_typename(l, index);
  }

  // This is a Solarus type.
  return get_type_name(module_name);
}

/**
 * \brief Returns the type name of a Solarus module.
 *
 * Converts Solarus module names into the name of the type the module wraps.
 *
 * \param module_name
 * \return The type name.
 */
std::string get_type_name(const std::string& module_name) {

  // Just remove the "sol." prefix.
  return module_name.substr(4);
}

/**
 * \brief Creates a reference to the Lua value on top of the stack and pops
 * this value.
 * \param l A Lua state.
 * \return The reference created, wrapped in an object that manages its
 * lifetime.
 */
ScopedLuaRef create_ref(lua_State* l) {
  lua_State* main = LuaContext::get().get_main_state();
  //Cross move values to main state to avoid  coroutines GC to invalidate them
  if(l != main) {
    lua_xmove(l,main,1);
  }
  return ScopedLuaRef(main, luaL_ref(main, LUA_REGISTRYINDEX));
}

/**
 * \brief Creates a reference to the Lua value at the specified index.
 * \param l A Lua state.
 * \param index An index in the Lua stack.
 * \return The reference created, wrapped in an object that manages its
 * lifetime.
 */
ScopedLuaRef create_ref(lua_State* l, int index) {
  lua_pushvalue(l, index);
  return create_ref(l);
}

/**
 * \brief Calls the Lua function with its arguments on top of the stack.
 *
 * This function is like lua_pcall, except that it additionally handles the
 * error message if an error occurs in the Lua code (the error is printed).
 * This function leaves the results on the stack if there is no error,
 * and leaves nothing on the stack in case of error.
 *
 * \param l A Lua state.
 * \param nb_arguments Number of arguments placed on the Lua stack above the
 * function to call.
 * \param nb_results Number of results expected (you get them on the stack if
 * there is no error).
 * \param function_name A name describing the Lua function (only used to print
 * the error message if any).
 * This is not a <tt>const std::string&</tt> but a <tt>const char*</tt> on
 * purpose to avoid costly conversions as this function is called very often.
 * \return \c true in case of success.
 */
bool call_function(
    lua_State* l,
    int nb_arguments,
    int nb_results,
    const char* function_name
) {
  SOL_PBLOCK(function_name, profiler::colors::Blue);
  SOLARUS_ASSERT(lua_gettop(l) > nb_arguments, "Missing arguments");
  int base = lua_gettop(l) - nb_arguments;
  lua_pushcfunction(l, &LuaContext::l_backtrace);
  lua_insert(l, base);
  int status = lua_pcall(l, nb_arguments, nb_results, base);
  lua_remove(l, base);
  if (status != 0) {
    SOLARUS_ASSERT(lua_isstring(l, -1), "Missing error message");
    Debug::error(std::string("In ") + function_name + ": "
        + lua_tostring(l, -1)
    );
    lua_pop(l, 1);
    return false;
  }
  return true;
}

/**
 * \brief Similar to luaL_error() but throws a LuaException.
 *
 * This function never returns.
 *
 * \param l A Lua state.
 * \param message Error message.
 */
void error(lua_State* l, const std::string& message) {
  throw LuaException(l, message);
}

/**
 * \brief Similar to luaL_argerror() but throws a LuaException.
 *
 * This function never returns.
 *
 * \param l A Lua state.
 * \param arg_index Index of an argument in the stack.
 * \param message Error message.
 */
void arg_error(lua_State* l, int arg_index, const std::string& message) {

  // The code below is very similar to luaL_argerror(), but ends with
  // an exception instead of a luaL_error() call.

  std::ostringstream oss;
  lua_Debug info;
  if (!lua_getstack(l, 0, &info)) {
    // No stack frame.
    oss << "bad argument #" << arg_index << " (" << message << ")";
    error(l, oss.str());
  }

  lua_getinfo(l, "n", &info);
  if (std::string(info.namewhat) == "method") {
     arg_index--;  // Do not count self.
     if (arg_index == 0) {
       // Error is in the self argument itself.
       oss << "calling " << info.name << " on bad self (" << message << ")";
       error(l, oss.str());
     }
  }

  if (info.name == nullptr) {
    info.name = "?";
  }
  oss << "bad argument #" << arg_index << " to " << info.name << " (" << message << ")";
  error(l, oss.str());
}

/**
 * \brief Similar to luaL_typerror() but throws a LuaException.
 *
 * This function never returns.
 *
 * \param l A Lua state.
 * \param arg_index Index of an argument in the stack.
 * \param expected_type_name A name describing the type that was expected.
 */
void type_error(
    lua_State* l,
    int arg_index,
    const std::string& expected_type_name
) {
  arg_error(l, arg_index,
      expected_type_name + " expected, got " + get_type_name(l, arg_index));
}

/**
 * \brief Similar to arg_error but adds field information to the message.
 *
 * This function never returns.
 *
 * \param l A Lua state.
 * \param table_index Index of an argument (usually a table) on the stack.
 * \param key Key of the field in the table.
 * \param message Error message.
 */
void field_error(
    lua_State* l,
    int table_index,
    const std::string& key,
    const std::string& message
) {
  arg_error(l, table_index,
      std::string("Bad field '") + key + "': " + message);
}

/**
 * \brief Similar to type_error but adds field information to the message.
 *
 * This function never returns. The field's value (of the incorrect type)
 * should be at the top of the stack, as if it had just been pushed.
 *
 * \param l A Lua state.
 * \param table_index Index of an argument (usually a table) on the stack.
 * \param key Key of the field in the table.
 * \param expected_type_name A name describing the type that was expected.
 */
void field_type_error(
    lua_State* l,
    int table_index,
    const std::string& key,
    const std::string& expected_type_name
) {
  field_error(l, table_index, key,
      expected_type_name + " expected, got " + get_type_name(l, -1));
}

/**
 * \brief Like luaL_checktype() but throws a LuaException in case of error.
 * \param l A Lua state.
 * \param arg_index Index of an argument in the stack.
 * \param expected_type A Lua type value.
 */
void check_type(
    lua_State* l,
    int arg_index,
    int expected_type
) {
  if (lua_type(l, arg_index) != expected_type) {
    type_error(l, arg_index, std::string(lua_typename(l, expected_type)));
  }
}

/**
 * \brief Throws a LuaException if not enough arguments have been provided.
 * \param l A Lua state.
 * \param minimum The minimum number of values on the Lua stack.
 * \return The current value of Lua top.
 */
int check_mintop(
    lua_State* l,
    int minimum
) {
  int top = lua_gettop(l);
  if (top < minimum) {
    arg_error(l, top + 1, "value expected");
  }
  return top;
}

/**
 * \brief Checks that a value is a number and returns it as an integer.
 *
 * This function acts like luaL_checkint() except that it throws a LuaException
 * in case of error.
 *
 * \param l A Lua state.
 * \param index Index of a table in the stack.
 * \return The value as an integer.
 */
int check_int(
    lua_State* l,
    int index
) {
  if (!lua_isnumber(l, index)) {
    type_error(l, index, "integer");
  }

  return static_cast<int>(lua_tointeger(l, index));
}

/**
 * \brief Checks that a table field is a number and returns it as an integer.
 *
 * This function acts like lua_getfield() followed by LuaTools::check_int().
 *
 * \param l A Lua state.
 * \param table_index Index of a table in the stack.
 * \param key Key of the field to get in that table.
 * \return The wanted field as an integer.
 */
int check_int_field(
    lua_State* l,
    int table_index,
    const std::string& key
) {
  lua_getfield(l, table_index, key.c_str());
  if (!lua_isnumber(l, -1)) {
    field_type_error(l, table_index, key, "integer");
  }

  int value = static_cast<int>(lua_tointeger(l, -1));
  lua_pop(l, 1);
  return value;
}

/**
 * \brief Like LuaTools::check_int() but with a default value.
 *
 * This function acts like luaL_optint() except that it throws a LuaException
 * in case of error.
 *
 * \param l A Lua state.
 * \param index Index of a value in the stack.
 * \param default_value The default value to return if the value is \c nil.
 * \return The wanted value as an integer.
 */
int opt_int(
    lua_State* l,
    int index,
    int default_value
) {
  if (lua_isnoneornil(l, index)) {
    return default_value;
  }
  return check_int(l, index);
}

/**
 * \brief Like LuaTools::check_int_field() but with a default value.
 *
 * This function acts like lua_getfield() followed by luaL_optint().
 *
 * \param l A Lua state.
 * \param table_index Index of a table in the stack.
 * \param key Key of the field to get in that table.
 * \param default_value The default value to return if the field is \c nil.
 * \return The wanted field as an integer.
 */
int opt_int_field(
    lua_State* l,
    int table_index,
    const std::string& key,
    int default_value
) {
  lua_getfield(l, table_index, key.c_str());
  if (lua_isnil(l, -1)) {
    lua_pop(l, 1);
    return default_value;
  }

  if (!lua_isnumber(l, -1)) {
    field_type_error(l, table_index, key, "integer");
  }
  int value = static_cast<int>(lua_tointeger(l, -1));
  lua_pop(l, 1);
  return value;
}

/**
 * \brief Checks that a value is a number and returns it as a double.
 *
 * This function acts like luaL_checknumber() except that it throws a
 * LuaException in case of error.
 *
 * \param l A Lua state.
 * \param index Index of a value in the stack.
 * \return The number value.
 */
double check_number(
    lua_State* l,
    int index
) {
  if (!lua_isnumber(l, index)) {
    type_error(l, index, "number");
  }

  return lua_tonumber(l, index);
}

/**
 * \brief Checks that a table field is a number and returns it as a double.
 *
 * This function acts like lua_getfield() followed by LuaTools::check_number().
 *
 * \param l A Lua state.
 * \param table_index Index of a table in the stack.
 * \param key Key of the field to get in that table.
 * \return The wanted field as a double.
 */
double check_number_field(
    lua_State* l,
    int table_index,
    const std::string& key
) {
  lua_getfield(l, table_index, key.c_str());
  if (!lua_isnumber(l, -1)) {
    field_type_error(l, table_index, key, "number");
  }

  double value = lua_tonumber(l, -1);
  lua_pop(l, 1);
  return value;
}

/**
 * \brief Like LuaTools::check_number() but with a default value.
 *
 * This function acts like luaL_optnumber() except that it throws a
 * LuaException in case of error.
 *
 * \param l A Lua state.
 * \param index Index of a value in the stack.
 * \param default_value The default value to return if the value is \c nil.
 * \return The wanted value as a double.
 */
double opt_number(
    lua_State* l,
    int index,
    double default_value
) {
  if (lua_isnoneornil(l, index)) {
    return default_value;
  }
  return check_number(l, index);
}

/**
 * \brief Like LuaTools::check_number_field() but with a default value.
 *
 * This function acts like lua_getfield() followed by LuaTools::opt_number().
 *
 * \param l A Lua state.
 * \param table_index Index of a table in the stack.
 * \param key Key of the field to get in that table.
 * \param default_value The default value to return if the field is \c nil.
 * \return The wanted field as a double.
 */
double opt_number_field(
    lua_State* l,
    int table_index,
    const std::string& key,
    double default_value
) {
  lua_getfield(l, table_index, key.c_str());
  if (lua_isnil(l, -1)) {
    lua_pop(l, 1);
    return default_value;
  }

  if (!lua_isnumber(l, -1)) {
    field_type_error(l, table_index, key, "number");
  }
  double value = lua_tonumber(l, -1);
  lua_pop(l, 1);
  return value;
}

/**
 * @brief LuaTools::islstring
 * @param L A lua state.
 * @param index INdex of a value in the stack
 * @param len lenght output parameter
 * @return The C-string value or nullptr if value is not a string
 */
const char * islstring(
    lua_State * L,
    int index,
    size_t * len
) {
  return (lua_isstring(L, index)) ? lua_tolstring(L, index, len) : nullptr;
}

/**
 * \brief Checks that a value is a string and returns it.
 *
 * This function acts like luaL_checkstring() except that it throws a
 * LuaException in case of error.
 *
 * \param l A Lua state.
 * \param index Index of a value in the stack.
 * \return The string value.
 */
std::string check_string(
    lua_State* l,
    int index
) {
  if (!lua_isstring(l, index)) {
    type_error(l, index, "string");
  }
  size_t size = 0;
  const char* data = lua_tolstring(l, index, &size);
  return {data, size};
}

/**
 * \brief Checks that a table field is a string and returns it.
 *
 * This function acts like lua_getfield() followed by LuaTools::check_string().
 *
 * \param l A Lua state.
 * \param table_index Index of a table in the stack.
 * \param key Key of the field to get in that table.
 * \return The wanted field as a string.
 */
std::string check_string_field(
    lua_State* l,
    int table_index,
    const std::string& key
) {
  lua_getfield(l, table_index, key.c_str());
  if (!lua_isstring(l, -1)) {
    field_type_error(l, table_index, key, "string");
  }

  size_t size = 0;
  const char* data = lua_tolstring(l, -1, &size);
  const std::string value = {data, size};
  lua_pop(l, 1);
  return value;
}

/**
 * \brief Like LuaTools::check_string() but with a default value.
 *
 * This function acts like luaL_optstring() except that it throws a
 * LuaException in case of error.
 *
 * \param l A Lua state.
 * \param index Index of a value in the stack.
 * \param default_value The default value to return if the value is \c nil.
 * \return The wanted value as a string.
 */
std::string opt_string(
    lua_State* l,
    int index,
    const std::string& default_value
) {
  if (lua_isnoneornil(l, index)) {
    return default_value;
  }
  return check_string(l, index);
}

/**
 * \brief Like LuaTools::check_string_field() but with a default value.
 *
 * This function acts like lua_getfield() followed by LuaTools::opt_string().
 *
 * \param l A Lua state.
 * \param table_index Index of a table in the stack.
 * \param key Key of the field to get in that table.
 * \param default_value The default value to return if the field is \c nil.
 * \return The wanted field as a string.
 */
std::string opt_string_field(
    lua_State* l,
    int table_index,
    const std::string& key,
    const std::string& default_value
) {
  lua_getfield(l, table_index, key.c_str());
  if (lua_isnil(l, -1)) {
    lua_pop(l, 1);
    return default_value;
  }

  if (!lua_isstring(l, -1)) {
    field_type_error(l, table_index, key, "string");
  }
  size_t size = 0;
  const char* data = lua_tolstring(l, -1, &size);
  const std::string value = {data, size};
  lua_pop(l, 1);
  return value;
}

/**
 * \brief Checks that a value is a string list and returns it.
 *
 * Throws a LuaException in case of error.
 *
 * \param l A Lua state.
 * \param index Index of a value in the stack.
 * \return The string list value.
 */
std::vector<std::string> check_string_list(
    lua_State* l,
    int index
) {
  std::vector<std::string> value;
    index = get_positive_index(l, index);

  if (lua_istable(l, index)) {
    lua_pushnil(l);
    while (lua_next(l, index) != 0) {
      value.push_back(check_string(l, -1));
      lua_pop(l, 1);
    }
  } else if (!lua_isnil(l, index)) {
    type_error(l, index, "table");
  }

  return value;
}

/**
 * \brief Checks that a table field is a string list and returns it.
 *
 * \param l A Lua state.
 * \param table_index Index of a table in the stack.
 * \param key Key of the field to get in that table.
 * \return The wanted field as a string list.
 */
std::vector<std::string> check_string_list_field(
    lua_State* l,
    int table_index,
    const std::string& key
) {
  lua_getfield(l, table_index, key.c_str());
  std::vector<std::string> result = check_string_list(l, -1);
  lua_pop(l, 1);
  return result;
}

/**
 * \brief Like LuaTools::check_string_list() but with a default value.
 *
 * Throws a LuaException in case of error.
 *
 * \param l A Lua state.
 * \param index Index of a value in the stack.
 * \param default_value The default value to return if the value is \c nil.
 * \return The wanted value as a string list.
 */
std::vector<std::string> opt_string_list(
    lua_State* l,
    int index,
    const std::vector<std::string>& default_value
) {
  if (lua_isnoneornil(l, index)) {
    return default_value;
  }
  return check_string_list(l, index);
}

/**
 * \brief Like LuaTools::check_string_list_field() but with a default value.
 *
 * This function acts like lua_getfield() followed by LuaTools::opt_string_list().
 *
 * \param l A Lua state.
 * \param table_index Index of a table in the stack.
 * \param key Key of the field to get in that table.
 * \param default_value The default value to return if the field is \c nil.
 * \return The wanted field as a string list.
 */
std::vector<std::string> opt_string_list_field(
    lua_State* l,
    int table_index,
    const std::string& key,
    const std::vector<std::string>& default_value
) {
  lua_getfield(l, table_index, key.c_str());

  if (lua_isnil(l, -1)) {
    lua_pop(l, 1);
    return default_value;
  }

  std::vector<std::string> result = check_string_list(l, -1);
  lua_pop(l, 1);
  return result;
}

/**
 * \brief Checks that a value is a boolean and returns it.
 *
 * This function throws a LuaException in case of error.
 *
 * \param l A Lua state.
 * \param index Index of a value in the stack.
 * \return The boolean value.
 */
bool check_boolean(
    lua_State* l,
    int index
) {
  if (!lua_isboolean(l, index)) {
    type_error(l, index, "boolean");
  }
  return lua_toboolean(l, index);
}

/**
 * \brief Checks that a table field is a boolean and returns it.
* \param l A Lua state.
 * \param table_index Index of a table in the stack.
 * \param key Key of the field to get in that table.
 * \return The wanted field as a boolean.
 */
bool check_boolean_field(
    lua_State* l,
    int table_index,
    const std::string& key
) {
  lua_getfield(l, table_index, key.c_str());
  if (lua_type(l, -1) != LUA_TBOOLEAN) {
    field_type_error(l, table_index, key, "boolean");
  }

  bool value = lua_toboolean(l, -1);
  lua_pop(l, 1);
  return value;
}

/**
 * \brief Like LuaTools::check_boolean() but with a default value.
 * \param l A Lua state.
 * \param index Index of a value in the stack.
 * \param default_value The default value to return if the value is \c nil.
 * \return The wanted value as a boolean.
 */
bool opt_boolean(
    lua_State* l,
    int index,
    bool default_value
) {
  if (lua_isnone(l, index)
    || (CurrentQuest::is_format_at_most({1, 7}) && lua_isnoneornil(l, index))
  ) {
    return default_value;
  }
  return check_boolean(l, index);
}

/**
 * \brief Like check_boolean_field() but with a default value.
 *
 * This function acts like lua_getfield() followed by lua_toboolean().
 *
 * \param l A Lua state.
 * \param table_index Index of a table in the stack.
 * \param key Key of the field to get in that table.
 * \param default_value The default value to return if the field is \c nil.
 * \return The wanted field as a string.
 */
bool opt_boolean_field(
    lua_State* l,
    int table_index,
    const std::string& key,
    bool default_value
) {
  lua_getfield(l, table_index, key.c_str());
  if (lua_isnil(l, -1)) {
    lua_pop(l, 1);
    return default_value;
  }

  if (lua_type(l, -1) != LUA_TBOOLEAN) {
    field_type_error(l, table_index, key, "boolean");
  }
  bool value = lua_toboolean(l, -1);
  lua_pop(l, 1);
  return value;
}

/**
 * \brief Checks that a value is a function and returns a ref to it.
 *
 * This function throws a LuaException in case of error.
 *
 * \param l A Lua state.
 * \param index Index of a value in the stack.
 * \return The wanted value as a Lua ref to the function.
 */
ScopedLuaRef check_function(
    lua_State* l,
    int index
) {
  check_type(l, index, LUA_TFUNCTION);
  return create_ref(l, index);  // Leave the function in the stack.
}

/**
 * \brief Checks that a table field is a function and returns a ref to it.
 * \param l A Lua state.
 * \param table_index Index of a table in the stack.
 * \param key Key of the field to get in that table.
 * \return The wanted field as a Lua ref to the function.
 */
ScopedLuaRef check_function_field(
    lua_State* l,
    int table_index,
    const std::string& key
) {
  lua_getfield(l, table_index, key.c_str());
  if (!lua_isfunction(l, -1)) {
    field_type_error(l, table_index, key, "function");
  }

  return create_ref(l);  // This also pops the function from the stack.
}

/**
 * \brief Like LuaTools::check_function() but the value is optional.
 * \param l A Lua state.
 * \param index Index of a value in the stack.
 * \return The wanted value as a Lua ref to the function, or an empty ref.
 */
ScopedLuaRef opt_function(
    lua_State* l,
    int index
) {
  if (lua_isnoneornil(l, index)) {
    return ScopedLuaRef();
  }
  return check_function(l, index);
}

/**
 * \brief Like LuaTools::check_function_field() but the field is optional.
 * \param l A Lua state.
 * \param table_index Index of a table in the stack.
 * \param key Key of the field to get in that table.
 * \return The wanted field as a Lua ref to the function, or an empty ref.
 */
ScopedLuaRef opt_function_field(
    lua_State* l,
    int table_index,
    const std::string& key
) {
  lua_getfield(l, table_index, key.c_str());
  if (lua_isnil(l, -1)) {
    lua_pop(l, 1);
    return ScopedLuaRef();
  }

  if (!lua_isfunction(l, -1)) {
    field_type_error(l, table_index, key, "function");
  }
  return create_ref(l);  // This also pops the function from the stack.
}
/**
 * \brief Returns whether a value is a layer.
 * \param l A Lua context.
 * \param index An index in the stack.
 * \param map The map whose number of layer matters.
 * \return \c true if the value is a valid layer for the map.
 */
bool is_layer(
    lua_State* l,
    int index,
    const Map& map
) {
  if (!lua_isnumber(l, index)) {
    return false;
  }
  int layer = check_int(l, index);
  return map.is_valid_layer(layer);
}

/**
 * \brief Checks that the value at the given index is a valid layer and returns it.
 * \param l A Lua state.
 * \param index An index in the Lua stack.
 * \param map The map whose number of layer matters.
 * \return The layer at this index.
 */
int check_layer(
    lua_State* l,
    int index,
    const Map& map
) {
  if (!lua_isnumber(l, index)) {
    type_error(l, index, "number");
  }

  if (!is_layer(l, index, map)) {
    std::ostringstream oss;
    oss << "Invalid layer: " << lua_tonumber(l, index);
    arg_error(l, index, oss.str());
  }

  return static_cast<int>(lua_tointeger(l, index));
}

/**
 * \brief Checks that a table field is a valid layer and returns it.
 *
 * This function acts like lua_getfield() followed by LuaTools::check_layer().
 *
 * \param l A Lua state.
 * \param table_index Index of a table in the stack.
 * \param key Key of the field to get in that table.
 * \param map The map whose number of layer matters.
 * \return The wanted field as a layer.
 */
int check_layer_field(
    lua_State* l,
    int table_index,
    const std::string& key,
    const Map& map
) {
  lua_getfield(l, table_index, key.c_str());
  if (!is_layer(l, -1, map)) {
    field_type_error(l, table_index, key, "layer");
  }

  int value = static_cast<int>(lua_tointeger(l, -1));
  lua_pop(l, 1);
  return value;
}

/**
 * \brief Like LuaTools::check_layer() but with a default value.
 * \param l A Lua state.
 * \param index Index of a value in the stack.
 * \param map The map whose number of layer matters.
 * \param default_value The default value to return if the value is \c nil.
 * \return The wanted value as a layer.
 */
int opt_layer(
    lua_State* l,
    int index,
    const Map& map,
    int default_value
) {
  if (lua_isnoneornil(l, index)) {
    return default_value;
  }
  return check_layer(l, index, map);
}

/**
 * \brief Like LuaTools::check_layer_field() but with a default value.
 * \param l A Lua state.
 * \param table_index Index of a table in the stack.
 * \param key Key of the field to get in that table.
 * \param map The map whose number of layer matters.
 * \param default_value The default value to return if the field is \c nil.
 * \return The wanted field as a layer.
 */
int opt_layer_field(
    lua_State* l,
    int table_index,
    const std::string& key,
    const Map& map,
    int default_value
) {
  lua_getfield(l, table_index, key.c_str());
  if (lua_isnil(l, -1)) {
    lua_pop(l, 1);
    return default_value;
  }

  if (!is_layer(l, -1, map)) {
    field_type_error(l, table_index, key, "layer");
  }
  int value = static_cast<int>(lua_tointeger(l, -1));
  lua_pop(l, 1);
  return value;
}

/**
 * \brief Returns whether a value is a color.
 * \param l A Lua context.
 * \param index An index in the stack.
 * \return true if the value is a color, that is, an array with three integers.
 */
bool is_color(lua_State* l, int index) {

  index = get_positive_index(l, index);

  if (lua_type(l, index) != LUA_TTABLE) {
    return false;
  }

  lua_rawgeti(l, index, 1);
  lua_rawgeti(l, index, 2);
  lua_rawgeti(l, index, 3);
  lua_rawgeti(l, index, 4);
  bool result = lua_isnumber(l, -4) &&
      lua_isnumber(l, -3) &&
      lua_isnumber(l, -2) &&
      (lua_isnumber(l, -1) || lua_isnil(l, -1));
  lua_pop(l, 4);
  return result;
}

/**
 * \brief Checks that the value at the given index is a color and returns it.
 *
 * Set opaque by default if alpha channel is not specified.
 *
 * \param l a Lua state
 * \param index an index in the Lua stack
 * \return the color at this index
 */
Color check_color(lua_State* l, int index) {

  index = get_positive_index(l, index);

  check_type(l, index, LUA_TTABLE);
  lua_rawgeti(l, index, 1);
  lua_rawgeti(l, index, 2);
  lua_rawgeti(l, index, 3);
  lua_rawgeti(l, index, 4);
  Color color(
      check_int(l, -4),
      check_int(l, -3),
      check_int(l, -2),
      opt_int(l, -1, 255)
  );
  lua_pop(l, 4);

  return color;
}

/**
 * \brief Checks that a table field is a color and returns it.
 *
 * This function acts like lua_getfield() followed by LuaTools::check_color().
 *
 * \param l A Lua state.
 * \param table_index Index of a table in the stack.
 * \param key Key of the field to get in that table.
 * \return The wanted field as a layer.
 */
Color check_color_field(
    lua_State* l,
    int table_index,
    const std::string& key
) {
  lua_getfield(l, table_index, key.c_str());
  if (!is_color(l, -1)) {
    field_type_error(l, table_index, key, "color");
  }

  const Color& value = check_color(l, -1);
  lua_pop(l, 1);
  return value;
}

/**
 * \brief Like LuaTools::check_color() but with a default value.
 * \param l A Lua state.
 * \param index Index of a value in the stack.
 * \param default_value The default value to return if the value is \c nil.
 * \return The wanted value as a color.
 */
Color opt_color(
    lua_State* l,
    int index,
    const Color& default_value
) {
  if (lua_isnoneornil(l, index)) {
    return default_value;
  }
  return check_color(l, index);
}

/**
 * \brief Like LuaTools::check_color_field() but with a default value.
 * \param l A Lua state.
 * \param table_index Index of a table in the stack.
 * \param key Key of the field to get in that table.
 * \param default_value The default value to return if the field is \c nil.
 * \return The wanted field as a color.
 */
Color opt_color_field(
    lua_State* l,
    int table_index,
    const std::string& key,
    const Color& default_value
) {
  lua_getfield(l, table_index, key.c_str());
  if (lua_isnil(l, -1)) {
    lua_pop(l, 1);
    return default_value;
  }

  if (!is_color(l, -1)) {
    field_type_error(l, table_index, key, "color");
  }
  const Color& color = check_color(l, -1);
  lua_pop(l, 1);
  return color;
}

}  // namespace LuaTools

}  // namespace Solarus
