
using System;
using System.Runtime.InteropServices;
using KeraLua;
using LuaState=KeraLua.Lua;

namespace NLua.Extensions
{
    static class LuaExtensions
    {
        public static bool CheckMetaTable(this LuaState state, int index, IntPtr tag)
        {
            if (!state.GetMetaTable(index))
                return false;

            state.PushLightUserData(tag);
            state.RawGet(-2);
            bool isNotNil = !state.IsNil(-1);
            state.SetTop(-3);
            return isNotNil;
        }

        public static void PopGlobalTable(this LuaState luaState)
        {
            luaState.RawSetInteger(LuaRegistry.Index, (long) LuaRegistryIndex.Globals);
        }

        public static void GetRef (this LuaState luaState, int reference)
        {
            luaState.RawGetInteger(LuaRegistry.Index, reference);
        }

        // ReSharper disable once IdentifierTypo
        public static void Unref (this LuaState luaState, int reference)
        {
            luaState.Unref(LuaRegistry.Index, reference);
        }

        public static bool AreEqual(this LuaState luaState, int ref1, int ref2)
        {
            return luaState.Compare(ref1, ref2, LuaCompare.Equal);
        }

        public static IntPtr CheckUData(this LuaState state, int ud, string name)
        {
            IntPtr p = state.ToUserData(ud);
            if (p == IntPtr.Zero)
                return IntPtr.Zero;
            if (!state.GetMetaTable(ud))
                return IntPtr.Zero;

            state.GetField(LuaRegistry.Index, name);

            bool isEqual = state.RawEqual(-1, -2);

            state.Pop(2);

            if (isEqual)
                return p;

            return IntPtr.Zero;
        }       

        public static int ToNetObject(this LuaState state, int index, IntPtr tag)
        {
            if (state.Type(index) != LuaType.UserData)
                return -1;

            IntPtr userData;

            if (state.CheckMetaTable(index, tag))
            {
                userData = state.ToUserData(index);
                if (userData != IntPtr.Zero)
                    return Marshal.ReadInt32(userData);
            }

            userData = state.CheckUData(index, "luaNet_class");
            if (userData != IntPtr.Zero)
                return Marshal.ReadInt32(userData);

            userData = state.CheckUData(index, "luaNet_searchbase");
            if (userData != IntPtr.Zero)
                return Marshal.ReadInt32(userData);

            userData = state.CheckUData(index, "luaNet_function");
            if (userData != IntPtr.Zero)
                return Marshal.ReadInt32(userData);

            return -1;
        }

        public static void NewUData(this LuaState state, int val)
        {
            IntPtr pointer = state.NewUserData(Marshal.SizeOf(typeof(int)));
            Marshal.WriteInt32(pointer, val);
        }

        public static int RawNetObj(this LuaState state, int index)
        {
            IntPtr pointer = state.ToUserData(index);
            if (pointer == IntPtr.Zero)
                return -1;

            return Marshal.ReadInt32(pointer);
        }

        public static int CheckUObject(this LuaState state, int index, string name)
        {
            IntPtr udata = state.CheckUData(index, name);
            if (udata == IntPtr.Zero)
                return -1;

            return Marshal.ReadInt32(udata);
        }

        public static bool IsNumericType(this LuaState state, int index)
        {
            return state.Type(index) == LuaType.Number;
        }
    }
}
