//*****************************************************************************
//  luatype.h
//
//  Conversion functions from Lua type to C type and vice versa
//    
//	File maintained by:
//		Young Tae Son (yson@ea.com)
//
//	(c) 2005 Electronic Arts Inc.
//*****************************************************************************

#pragma once

#include "lua.h"
#include "lauxlib.h"
#include <eastl/string.h>
#include <EAAssert/eaassert.h>

namespace Luax
{
struct LuaxPlacementNewDifferentiator {};
}
inline void* operator new(size_t size, void* p, Luax::LuaxPlacementNewDifferentiator) { (void)(size);return p; }

namespace Luax
{
//-----------------------------------------------------------------------------
//> Id - this is a helper struct that optimizes the lookup in lua by using lua_tointeger
struct Id
{
	constexpr operator int() const { return id;	}
	int id = 0;
};

template<typename T>
inline T GetFromLuaType(lua_State* l, int idx)
{
	if constexpr (eastl::is_same_v<eastl::remove_cv_t<T>, lua_State*>)
	{
		return l;
	}
	else if constexpr(eastl::is_same_v<T, Id>)
	{
		EA_ASSERT_FORMATTED(lua_tointeger(l, idx) == static_cast<int>(lua_tonumber(l, idx)), ("Lua value is expecting an integer but is a float: %f", lua_tonumber(l, idx)));
		return { lua_tointeger(l, idx) };
	}
	else if constexpr (eastl::is_enum_v<T>)
	{
		return static_cast<T>(lua_tointeger(l, idx));
	}
	else if constexpr (eastl::is_same_v<T, bool>)
	{
		return lua_toboolean(l, idx) != 0;
	}
	else if constexpr (eastl::is_floating_point_v<T> || eastl::is_signed_v<T> || eastl::is_same_v<T, char>)
	{
		return static_cast<T>(lua_tonumber(l, idx));
	}
	else if constexpr (eastl::is_unsigned_v<T>)
	{
		return static_cast<T>(lua_tointeger(l, idx));
	}
	else if constexpr (eastl::is_same_v<T, const char*> || eastl::is_same_v<T, eastl::string>)
	{
		return lua_tostring(l, idx);
	}
	else
	{
		// don't know about this type.  going to try userdata. it should be a pointer
		static_assert(eastl::is_pointer<T>::value, "GetFromLuaType type not defined and is not a pointer.");

		return static_cast<T>(lua_touserdata(l, idx));
	}
}

template<typename T>
inline bool luax_argcheck(lua_State* l, int idx = 1)
{
	if constexpr (eastl::is_same_v<eastl::remove_cv_t<T>, lua_State*>)
	{
		return true;
	}
	else if constexpr (eastl::is_same_v<T, const char*> || eastl::is_same_v<T, eastl::string>)
	{
		if (lua_isstring(l, idx)) return true;
		lua_pushstring(l, "Invalid argument, string expected");
		return false;
	}
	else if constexpr (eastl::is_same_v<T, bool>)
	{
		if (lua_isboolean(l, idx)) return true;
		lua_pushstring(l, "Invalid argument, boolean expected");
		return false;
	}
	else if constexpr (eastl::is_same_v<T, Id> || eastl::is_enum_v<T>)
	{
		if (lua_isinteger(l, idx)) return true;
		lua_pushstring(l, "Invalid argument, integer expected");
		return false;
	}
	else if constexpr (eastl::is_floating_point_v<T> || eastl::is_integral_v<T>)
	{
		if (lua_isnumber(l, idx)) return true;
		lua_pushstring(l, "Invalid argument, number expected");
		return false;
	}
	else
	{
		if (lua_islightuserdata(l, idx)) return true;
		lua_pushstring(l, "Invalid argument, userdata (c pointer) expected");
		return false;
	}
}

//-----------------------------------------------------------------------------
inline void PushReturnToLua(lua_State* lstate, int arg)
{
    lua_pushinteger(lstate, static_cast<lua_Integer>(arg));
}

inline void PushReturnToLua(lua_State* lstate, float arg)
{
    lua_pushnumber(lstate, static_cast<lua_Number>(arg));
}

inline void PushReturnToLua(lua_State* lstate, const char* arg)
{
    lua_pushstring(lstate, arg);
}

inline void PushReturnToLua(lua_State* lstate, bool arg)
{
    lua_pushboolean(lstate, arg);
}

inline void PushReturnToLua(lua_State* lstate, void* arg)
{
	lua_pushlightuserdata(lstate, arg);
}

class LuaFunction
{
public:
	LuaFunction(const char* name)
	{
		strncpy(mName, name, sizeof(mName));
	}
	const char* GetName() const { return mName; }
private:
	char mName[256];
};

inline void PushReturnToLua(lua_State* lstate, const LuaFunction& arg)
{
	lua_getglobal(lstate, arg.GetName());
}

inline void PushReturnToLua(lua_State* lstate, const eastl::string& arg)
{
	lua_pushstring(lstate, arg.c_str());
}

}
