////////////////////////////////////////////////////////////////////
//
// LuaKeywordParser
//
// This is a helper class to help parse tables full of keywords.
// It is meant to make it easier to write LuaDefine functions that
// accept keyword arguments.  It helps with the following tasks:
//
// * It makes sure the keyword table actually is a table.
//
// * It makes sure that all required keywords are present.
//
// * It makes sure that you didn't put anything that isn't a
//   known keyword into the keyword table.
//
// This module adds two fields to the table:
//
//  [ERROR] - stores an error message, initially nil.
//  [FOUND] - the set of keywords successfully parsed.
//
// If at any time, this module detects an error, it doesn't throw.
// Instead, it stores an error report in the table key [ERROR].
// Later, you can check for an error string using the function
// final_check or final_check_throw.  If an error is
// detected when there is already an error report in the table,
// the error report is not overwritten, so therefore, the error
// reported is always the first error detected.
//
// When this module finds a keyword in the table, it adds the keyword
// to the [FOUND] set of all keywords successfully parsed.
//
// If the keyword table that you pass in isn't a table at all,
// then the keyword-fetching functions will always return false.
// Later, when you call 'check', an appropriate error will be
// generated.
//
// The lua module 'keywords' contains the same functions as this
// C++ class.  You can write code where a C++ function does some
// of the parsing, and the lua code does the rest of the parsing.
//
////////////////////////////////////////////////////////////////////

#pragma once

#include "luastack.hpp"

class LuaKeywordParser {
private:
    LuaVar found, error, key, val;
    LuaSpecial keytab;
    LuaCoreStack LS;
    bool istable;

    void init(const lua_State *L, int slot);
public:
    LuaKeywordParser(const LuaCoreStack &LS, LuaSlot slot);

    // Fetch the value of the keyword.  If the keyword is found, then the
    // keyword is added to the [FOUND] set, the value is returned in slot,
    // and returns true.  Otherwise, sets slot to nil and returns false.
    bool optional(LuaSlot slot, std::string_view kw);

    // Fetch the value of the keyword.  If the keyword is found, then the
    // keyword is added to the [FOUND] set, the value is returned in slot,
    // and returns true.  Otherwise, sets slot to nil, returns false, and
    // stores an [ERROR] report in the keyword table.
    bool required(LuaSlot slot, std::string_view kw);

    // Check if there are any errors so far, by checking for an [ERROR]
    // report in the keyword table.  If any error has been
    // detected, returns an error message, otherwise, returns empty
    // string.
    eng::string check();

    // Check if there are any errors so far, by checking for an [ERROR]
    // report in the keyword table.  Also check that all keyword
    // arguments present in the table are in the [FOUND] set.  If there are
    // any errors, returns an error message, otherwise returns empty string.
    eng::string final_check();

    // If check() returns an error, throws the error using luaL_error.
    void check_throw();

    // If final_check() returns an error, throws the error using luaL_error.
    void final_check_throw();

    // Fetch the state pointer.
    lua_State *state() const { return LS.state(); }
};
