// https://github.com/vinniefalco/LuaBridge
 
// Copyright 2018, Dmitry Tarakanov
 
// Copyright 2012, Vinnie Falco <vinnie.falco@gmail.com>
 
// SPDX-License-Identifier: MIT
 
 
 
#pragma once
 
 
 
#include <LuaBridge/detail/LuaRef.h>
 
 
 
#include <utility>
 
 
 
namespace luabridge {
 
 
 
/** Allows table iteration.
 
 */
 
class Iterator
 
{
 
    lua_State* m_L;
 
    LuaRef m_table;
 
    LuaRef m_key;
 
    LuaRef m_value;
 
 
 
    void next()
 
    {
 
        m_table.push();
 
        m_key.push();
 
        if (lua_next(m_L, -2))
 
        {
 
            m_value.pop();
 
            m_key.pop();
 
        }
 
        else
 
        {
 
            m_key = Nil();
 
            m_value = Nil();
 
        }
 
        lua_pop(m_L, 1);
 
    }
 
 
 
public:
 
    explicit Iterator(const LuaRef& table, bool isEnd = false)
 
        : m_L(table.state())
 
        , m_table(table)
 
        , m_key(table.state()) // m_key is nil
 
        , m_value(table.state()) // m_value is nil
 
    {
 
        if (!isEnd)
 
        {
 
            next(); // get the first (key, value) pair from table
 
        }
 
    }
 
 
 
    /// Return an associated Lua state.
 
    ///
 
    /// @returns A Lua state.
 
    ///
 
    lua_State* state() const { return m_L; }
 
 
 
    /// Dereference the iterator.
 
    ///
 
    /// @returns A key-value pair for a current table entry.
 
    ///
 
    std::pair<LuaRef, LuaRef> operator*() const { return std::make_pair(m_key, m_value); }
 
 
 
    /// Return the value referred by the iterator.
 
    ///
 
    /// @returns A value for the current table entry.
 
    ///
 
    LuaRef operator->() const { return m_value; }
 
 
 
    /// Compare two iterators.
 
    ///
 
    /// @param rhs Another iterator.
 
    /// @returns True if iterators point to the same entry of the same table,
 
    ///         false otherwise.
 
    ///
 
    bool operator!=(const Iterator& rhs) const
 
    {
 
        assert(m_L == rhs.m_L);
 
        return !m_table.rawequal(rhs.m_table) || !m_key.rawequal(rhs.m_key);
 
    }
 
 
 
    /// Move the iterator to the next table entry.
 
    ///
 
    /// @returns This iterator.
 
    ///
 
    Iterator& operator++()
 
    {
 
        if (isNil())
 
        {
 
            // if the iterator reaches the end, do nothing
 
            return *this;
 
        }
 
        else
 
        {
 
            next();
 
            return *this;
 
        }
 
    }
 
 
 
    /// Check if the iterator points after the last table entry.
 
    ///
 
    /// @returns True if there are no more table entries to iterate,
 
    ///         false otherwise.
 
    ///
 
    bool isNil() const { return m_key.isNil(); }
 
 
 
    /// Return the key for the current table entry.
 
    ///
 
    /// @returns A reference to the entry key.
 
    ///
 
    LuaRef key() const { return m_key; }
 
 
 
    /// Return the key for the current table entry.
 
    ///
 
    /// @returns A reference to the entry value.
 
    ///
 
    LuaRef value() const { return m_value; }
 
 
 
private:
 
    // Don't use postfix increment, it is less efficient
 
    Iterator operator++(int);
 
};
 
 
 
namespace detail {
 
 
 
class Range
 
{
 
    Iterator m_begin;
 
    Iterator m_end;
 
 
 
public:
 
    Range(const Iterator& begin, const Iterator& end) : m_begin(begin), m_end(end) {}
 
 
 
    const Iterator& begin() const { return m_begin; }
 
    const Iterator& end() const { return m_end; }
 
};
 
 
 
} // namespace detail
 
 
 
/// Return a range for the Lua table reference.
 
///
 
/// @returns A range suitable for range-based for statement.
 
///
 
inline detail::Range pairs(const LuaRef& table)
 
{
 
    return detail::Range(Iterator(table, false), Iterator(table, true));
 
}
 
 
 
} // namespace luabridge