int ShaderResourceBindingParser::BindResources( lua_State *L )
    {
        try
        {
            auto NumArgs = lua_gettop( L );
            if( NumArgs < 3 )
            {
                SCRIPT_PARSING_ERROR( L, "At least 2 arguments (shader flags and resource mapping) are expected" );
            }
            
            
            int ArgStackInd = 1;

            auto *pResBinding = *GetUserData<IShaderResourceBinding**>( L, ArgStackInd, m_MetatableRegistryName.c_str() );
            VERIFY( pResBinding, "Resource mapping pointer is null" );
            if( !pResBinding )return 0;

            ++ArgStackInd;
            Uint32 ShaderFlags = 0;
            {
                FlagsLoader<SHADER_TYPE> FlagsLoader(0, "BindShaderResourceFlags", m_ShaderTypeEnumMapping);
                FlagsLoader.SetValue( L, ArgStackInd, &ShaderFlags );
            }

            ++ArgStackInd;
            auto *pResourceMapping = *GetUserData<IResourceMapping**>( L, ArgStackInd, m_ResMappingMetatableName.c_str() );
            if( !pResourceMapping )
            {
                SCRIPT_PARSING_ERROR( L, "Incorrect 2nd argument type: resource mapping is expected" );
            }

            ++ArgStackInd;
            Uint32 Flags = 0;
            // The last argument may be flags
            if( NumArgs >= ArgStackInd &&
                (lua_type( L, ArgStackInd ) == LUA_TSTRING || lua_type( L, ArgStackInd ) == LUA_TTABLE ) )
            {
                FlagsLoader<BIND_SHADER_RESOURCES_FLAGS> FlagsLoader(0, "BindShaderResourceFlags", m_BindShaderResFlagEnumMapping);
                FlagsLoader.SetValue( L, ArgStackInd, &Flags );
            }

            pResBinding->BindResources( ShaderFlags, pResourceMapping, Flags );
        }
        catch( const std::runtime_error& )
        {

        }
        return 0;
    }
    int EngineObjectParserBase::LuaCreate( lua_State *L )
    {
        INIT_LUA_STACK_TRACKING( L );

        EngineObjectParserBase *This = static_cast<EngineObjectParserBase *>(lua_touserdata( L, lua_upvalueindex( 1 ) ));
        // This pointer cannot be null because we stored it in an up value when created the library
        VERIFY( This, "This pointer is null" );
        if( !This )return 0;

        try
        {
            This->CreateObj( L );
        }
        catch( const std::runtime_error &err )
        {
            SCRIPT_PARSING_ERROR( L, "Failed to create ", This->m_LibName, " object: \n", err.what() );
        }

        // Push onto the stack the metatable associated with name given in the registry
        luaL_getmetatable( L, This->m_MetatableRegistryName.c_str() );  // -0 | +1 -> +1
        // Pop a table from the top of the stack and set it as the new metatable 
        // for the value at the given index (which is where the new user datum is)
        lua_setmetatable( L, -2 );                                      // -1 | +0 -> -1

        CHECK_LUA_STACK_HEIGHT( +1 );

        // Return number of return arguments
        // New userdatum is on the top of the stack
        return 1;
    }
    Type ReadIntValueFromLua( lua_State *L, int Index )
    {
        CheckType( L, Index, LUA_TNUMBER );

        int isnum;
        auto Val = lua_tonumberx( L, Index, &isnum );
        if( !isnum )
        {
            auto Str = lua_tostring( L, Index );
            SCRIPT_PARSING_ERROR( L, "Failed to convert parameter ", Str, " to int" );
        }
        if( static_cast<Type>(Val) != Val )
        {
            SCRIPT_PARSING_ERROR( L, "Parameter value (", Val, ") is not integer. Truncating to int" );
        }
        return static_cast<Type>(Val);
    }
    Type ReadFPValueFromLua( lua_State *L, int Index )
    {
        CheckType( L, Index, LUA_TNUMBER );

        int isnum;
        auto Val = lua_tonumberx( L, Index, &isnum );
        if( !isnum )
        {
            auto Str = lua_tostring( L, Index );
            SCRIPT_PARSING_ERROR( L, "Failed to convert parameter ", Str, " to floating point" );
        }
        return static_cast<Type>(Val);
    }
    void NumericArrayLoader::LoadArray( lua_State *L, int StackIndex, std::vector< Uint8 >& RawData )
    {
        VALUE_TYPE ValueType;
        m_ValueTypeBinder.SetValue( L, StackIndex-1, &ValueType );

        auto it = m_ParseFuncJumpTbl.find( ValueType );
        if( it != m_ParseFuncJumpTbl.end() )
        {
            it->second( L, StackIndex, RawData );
        }
        else
        {
            SCRIPT_PARSING_ERROR( L, "No method to parse array of value VALUE_TYPE==", ValueType);
        }
    }
    void SamplerParser::CreateObj( lua_State *L )
    {
        INIT_LUA_STACK_TRACKING( L );

        SSamDescWrapper SamplerDesc;
        ParseLuaTable( L, -1, &SamplerDesc, m_Bindings );

        CHECK_LUA_STACK_HEIGHT();

        auto ppSampler = reinterpret_cast<ISampler**>(lua_newuserdata( L, sizeof( ISampler* ) ));
        *ppSampler = nullptr;
        m_pRenderDevice->CreateSampler( SamplerDesc, ppSampler );
        if( *ppSampler == nullptr )
            SCRIPT_PARSING_ERROR(L, "Failed to create a sampler")

        CHECK_LUA_STACK_HEIGHT( +1 );
    }
    void TextureViewParser::CreateObj( lua_State *L )
    {
        INIT_LUA_STACK_TRACKING(L);
        
        auto *pTexture = *GetUserData<ITexture**>( L, 1, m_TextureLibMetatableName.c_str() );

        STexViewDescWrapper TextureViewDesc;
        ParseLuaTable( L, 2, &TextureViewDesc, m_Bindings );
        CHECK_LUA_STACK_HEIGHT();

        auto ppTextureView = reinterpret_cast<ITextureView**>(lua_newuserdata( L, sizeof( ITextureView* ) ));
        *ppTextureView = nullptr;
        pTexture->CreateView( TextureViewDesc, ppTextureView );
        if( *ppTextureView == nullptr )
            SCRIPT_PARSING_ERROR(L, "Failed to create texture view")

        CHECK_LUA_STACK_HEIGHT( +1 );
    }
    void ParseNumericArray( lua_State *L, int StackIndex, std::vector< Uint8 >& RawData )
    {
        typedef typename VALUE_TYPE2CType<VTType>::CType ElemType;
        CheckType( L, StackIndex, LUA_TTABLE );
        auto ArraySize = lua_rawlen( L, StackIndex );

        auto ElemSize = sizeof( ElemType );
        RawData.reserve( ArraySize * ElemSize );

        ParseLuaArray( L, StackIndex, &RawData, [ &]( void* pBasePointer, int StackIndex, int NewArrayIndex )
        {
            VERIFY( pBasePointer == &RawData, "Sanity check failed" );
            auto CurrIndex = RawData.size() / ElemSize;
            if(static_cast<int>(CurrIndex) != NewArrayIndex - 1 )
                SCRIPT_PARSING_ERROR( L, "Explicit array indices are not allowed in array initialization. Provided index ", NewArrayIndex - 1, " conflicts with actual index ", CurrIndex, "." );
            RawData.resize( (CurrIndex + 1) * ElemSize );
            auto CurrValue = ReadValueFromLua<double>( L, StackIndex );
            reinterpret_cast<ElemType&>(RawData[CurrIndex * ElemSize]) = static_cast<ElemType>(CurrValue);
        } );
    }
    int ShaderResourceBindingParser::GetVariable( lua_State *L )
    {
        auto NumArgs = lua_gettop( L );
        if( NumArgs < 3 )
        {
            SCRIPT_PARSING_ERROR( L, "2 arguments (shader type and variable name) are expected" );
        }

        INIT_LUA_STACK_TRACKING(L);

        int ArgStackInd = 1;

        // The object itself goes first
        auto *pShaderResBinding = *GetUserData<IShaderResourceBinding**>( L, ArgStackInd, m_MetatableRegistryName.c_str() );
        
        // Shader type should be the first argument
        ++ArgStackInd;
        SHADER_TYPE ShaderType = SHADER_TYPE_UNKNOWN;
        EnumMemberBinder<SHADER_TYPE> ShaderTypeParser(0, "ShaderType", m_ShaderTypeEnumMapping);
        ShaderTypeParser.SetValue(L, ArgStackInd, &ShaderType);

        ++ArgStackInd;
        // Variable name should be the second argument
        auto VarName = ReadValueFromLua<String>( L, ArgStackInd );

        auto pVar = pShaderResBinding->GetVariable(ShaderType, VarName.c_str());

        auto pNewShaderVarLuaObj = reinterpret_cast<IShaderVariable**>(lua_newuserdata( L, sizeof( IShaderVariable* ) ));
        *pNewShaderVarLuaObj = pVar;
        pVar->AddRef();

        // Push onto the stack the metatable associated with name given in the registry
        luaL_getmetatable( L, m_ShaderVarMetatableRegistryName.c_str() );   // -0 | +1 -> +1
        // Pop a table from the top of the stack and set it as the new metatable 
        // for the value at the given index (which is where the new user datum is)
        lua_setmetatable( L, -2 );                                          // -1 | +0 -> -1

        CHECK_LUA_STACK_HEIGHT( +1 );

        return 1;
    }
    int TextureViewParser::GetDefaultView( lua_State *L )
    {
        INIT_LUA_STACK_TRACKING( L );

        // Texture should be the first argument
        auto *pTexture = *GetUserData<ITexture**>( L, 1, m_TextureLibMetatableName.c_str() );
        
        // View type should be the second argument
        TEXTURE_VIEW_TYPE ViewType;
        m_ViewTypeParser.SetValue( L, 2, &ViewType );

        auto pView = pTexture->GetDefaultView( ViewType );
        if( !pView )
            SCRIPT_PARSING_ERROR( L, "Failed to get default texture view of type ", GetTexViewTypeLiteralName( ViewType ) );

        // Push existing object
        PushObject(L, pView);

        CHECK_LUA_STACK_HEIGHT( +1 );

        // Returning one value to Lua
        return 1;
    }
        CHECK_LUA_STACK_HEIGHT( +1 );
    }

    void ShaderResourceBindingParser::DestroyObj( void *pData )
    {
        if( pData != nullptr )
        {
            auto ppShaderResBinding = reinterpret_cast<IShaderResourceBinding**>(pData);
            if( *ppShaderResBinding != nullptr )
                (*ppShaderResBinding)->Release();
        }
    }

    void ShaderResourceBindingParser::ReadField( lua_State *L, void *pData, const Char *Field )
    {
        SCRIPT_PARSING_ERROR(L, "Shader resource binding have no fields that can be read")
    }

    void ShaderResourceBindingParser::UpdateField( lua_State *L, void *pData, const Char *Field )
    {
        SCRIPT_PARSING_ERROR(L, "Shader resource binding have no fields that can be updated")
    }


    void ShaderResourceBindingParser::PushExistingObject( lua_State *L, const void *pObject )
    {
        auto pShaderResourceBinding = reinterpret_cast<IShaderResourceBinding**>(lua_newuserdata( L, sizeof( IShaderResourceBinding* ) ));
        *pShaderResourceBinding = reinterpret_cast<IShaderResourceBinding*>( const_cast<void*>(pObject) );
        (*pShaderResourceBinding)->AddRef();
    }
 void EngineObjectParserBase::UpdateField( lua_State *L, void *pData, const Char *Field )
 {
     SCRIPT_PARSING_ERROR( L, "Attempting to update \"", Field, "\" field of a read-only object \"", m_LibName.c_str(), '\"' );
 }