/******************************************************************************\
* LuaFunction.hpp                                                              *
* A C++ equivalent of a Lua function.                                          *
*                                                                              *
*                                                                              *
* Copyright (C) 2005-2013 by Leandro Motta Barros.                             *
*                                                                              *
* Permission is hereby granted, free of charge, to any person obtaining a copy *
* of this software and associated documentation files (the "Software"), to     *
* deal in the Software without restriction, including without limitation the   *
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or  *
* sell copies of the Software, and to permit persons to whom the Software is   *
* furnished to do so, subject to the following conditions:                     *
*                                                                              *
* The above copyright notice and this permission notice shall be included in   *
* all copies or substantial portions of the Software.                          *
*                                                                              *
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR   *
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,     *
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE *
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER       *
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING      *
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS *
* IN THE SOFTWARE.                                                             *
\******************************************************************************/

#ifndef _DILUCULUM_LUA_FUNCTION_HPP_
#define _DILUCULUM_LUA_FUNCTION_HPP_

#include <cassert>
#include <string>
#include <lua.hpp>
#include <Diluculum/Types.hpp>


namespace Diluculum
{
   /** A C++ equivalent of a Lua function. This can store both C functions
    *  exported (or exportable) to Lua and pure Lua functions. In the first
    *  case, it stores a \c lua_CFunction. In the second case it stores data
    *  that would normally be in the Lua-space as a function implemented in Lua
    *  (one can think of it as a buffer that stores Lua bytecode that a Lua
    *  interpreter can understand as a function implemented in Lua). This is
    *  used as the value of a \c LuaValue when it holds a Lua function.
    *  @note A \c LuaFunction does not have any reference to a Lua
    *        interpreter. Thus, it does not make sense to call a \c LuaFunction
    *        object directly. To call a \c LuaFunction, use \c LuaState::call().
    */
   class LuaFunction
   {
      public:

         /** Constructs a \c LuaFunction from Lua source code (a Lua chunk). The
          *  default value (a "dummy" chunk, that doesn't return anything) can
          *  be useful for moments in which a default constructable \c
          *  LuaFunction is desired.
          *  @param luaChunk The Lua source of the chunk.
          *  @note The chunk can be seen as a vararg function. So, if one wants,
          *        for instance, a chunk corresponding to a function that
          *        returns its first parameter doubled, the following chunk can
          *        be used: <tt>"local p = {...}; return p[1]*2"</tt>.
          */
         LuaFunction (const::std::string& luaChunk = "");

         /** Constructs a \c LuaFunction from Lua bytecode. Typically, this
          *  constructor is not called directly by end users. Instead, end users
          *  use <tt>LuaState::operator[]</tt> to get a \c LuaVariable
          *  containing a \c LuaFunction value, which can be accessed via
          *  <tt>LuaVariable::value()</tt>.
          *  @param data A pointer to the beginning of the memory area
          *         containing Lua bytecode.
          *  @param size The number of bytecode bytes to read.
          */
         LuaFunction (const void* data, size_t size);

         /** Constructs a \c LuaFunction from a \c lua_CFunction (a Lua function
          * implemented in C).
          */
         LuaFunction (lua_CFunction func);

         /** The copy constructor. The newly constructed \c LuaFunction will
          *  have its own block of memory, with the same contents as the \c
          *  other. In other words, this constructor allocates and copies
          *  memory.
          */
         LuaFunction (const LuaFunction& other);

         /// Destroys the \c LuaFunction, freeing all the resources owned by it.
         ~LuaFunction() { destroyObjectAtData(); }

         /** Assigns a \c LuaFunction to this one. The memory currently
          *  allocated for \c this will be freed, new memory will be allocated,
          *  and the data stored in \c rhs will be copied to \c this.
          */
         const LuaFunction& operator= (const LuaFunction& rhs);

         /**
          * Checks if this \c LuaFunction holds a C function (instead of a
          * "pure" Lua function).
          */
         bool isCFunction() const { return functionType_ == LUA_C_FUNCTION; }

         /**
          * Returns the value of this \c LuaFunction as a C function.
          * @note Will \c assert() if this \c LuaFunction does not hold a C
          *       function.
          */
         lua_CFunction getCFunction() const;

         /** Returns the size, in bytes, of the data stored in this
          *  \c LuaFunction.
          */
         size_t getSize() const { return size_; }

         /// Returns a pointer to the data stored in this \c LuaFunction.
         void* getData() {
            assert(functionType_ == LUA_LUA_FUNCTION
                   && "Called LuaFunction::getData() for a non-Lua function.");

            return data_.typeLuaFunction;
         }

         /** Returns a \c const pointer to the data stored in this
          *  \c LuaFunction.
          */
         const void* getData() const {
            assert(functionType_ == LUA_LUA_FUNCTION
                   && "Called LuaFunction::getData() for a non-Lua function.");

            return data_.typeLuaFunction;
         }

         /// Sets the data stored in this \c LuaFunction.
         void setData(void* data, size_t size);

         /// Gets the "reader flag".
         bool getReaderFlag() const { return readerFlag_; }

         /// Sets the "reader flag" to a given value.
         void setReaderFlag(bool f) { readerFlag_ = f; }

         /** The "greater than" operator for \c LuaFunction.
          *  @note Given two <tt>LuaFunction</tt>s, the decision on which one is
          *        greater is somewhat arbitrary. Here, the function with larger
          *        size is considered greater. If both are equal, the decision
          *        is based on the contents of the stored data.
          */
         bool operator> (const LuaFunction& rhs) const;

         /** The "less than" operator for \c LuaFunction.
          *  @note Given two <tt>LuaFunction</tt>s, the decision on which one is
          *        lesser is somewhat arbitrary. The criterion is similar to the
          *        described for the "greater than" operator.
          */
         bool operator< (const LuaFunction& rhs) const;

         /** The "equal to" operator for \c LuaFunction.
          *  @note Two <tt>LuaFunctions</tt>s are considered equal if the data
          *        they store have the same size and the same contents.
          *  @todo In Lua, a function is considered equal only to itself. Things
          *        are different here. Does this have a reason to not be like in
          *        Lua?
          */
         bool operator== (const LuaFunction& rhs) const;

         /// The "different than" operator for \c LuaFunction.
         bool operator!= (const LuaFunction& rhs) const;

      private:

         /** Destroys the object allocated at the \c data_ member, freeing its
          *  resources.
          */
         void destroyObjectAtData();

         /** The possible types of functions than possibly be stored in a
          *  \c LuaFunction.
          */
         enum TypeOfFunction
         {
            /// A Lua function implemented in C.
            LUA_C_FUNCTION,

            /// A Lua function implemented in Lua.
            LUA_LUA_FUNCTION
         };

         /// The type of function stored in this \c LuaFunction.
         TypeOfFunction functionType_;

         /// The number of bytes stored "in" \c data_.
         size_t size_;

         union PossibleTypes
         {
               lua_CFunction typeCFunction;
               char* typeLuaFunction;
         };

         /** This stores the actual data of this \c LuaFunction.
          *  <p>Implementation details: The values are allocated here using
          *  placement new, with destructors explicitly called whenever
          *  necessary.
          */
         PossibleTypes data_;

         /** A flag used when reading the bytecode data, in calls to \c
          *  lua_load() and its \c lua_Reader.
          */
         bool readerFlag_;
   };

} // namespace Diluculum

#endif // _DILUCULUM_LUA_FUNCTION_HPP_
