#include <iostream>
#include <string>
#include <utility>

struct Image { std::string id; };

struct ogl_backend
{
    typedef unsigned texture_handle_type;
    
    void load(texture_handle_type& texture, const Image& image)
    {
        std::cout << "loading, " << image.id << '\n';
    }
    
    void destroy(texture_handle_type& texture)
    {
        std::cout << "destroying texture\n";
    }
};

template <class GraphicsBackend>
struct texture_gpu_resource
{
    typedef GraphicsBackend graphics_backend;
    typedef typename GraphicsBackend::texture_handle_type texture_handle;

    texture_gpu_resource(graphics_backend& backend)
        : _backend(backend)
    {
    }
    
    ~texture_gpu_resource()
    {
    	// should check if it is a valid handle first
        _backend.destroy(_handle);
    }
    
    void load(const Image& image)
    {
        _backend.load(_handle, image);
    }
    
    const texture_handle& handle() const
    {
        return _handle;
    }
    
private:
    
    graphics_backend& _backend;
    texture_handle _handle;
};


template <typename GraphicBackend>
class graphics_device
{
	typedef graphics_device<GraphicBackend> this_type;
    
public:
    
    typedef texture_gpu_resource<GraphicBackend> texture;
    
	template <typename... Args>
	texture createTexture(Args&&... args)
	{
        return texture{_backend, std::forward(args)...};
	}
	
	template <typename Resource, typename... Args>
	Resource create(Args&&... args)
	{
		return Resource{_backend, std::forward(args)...};
    }
    
private:
    
    GraphicBackend _backend;
};


class ogl_graphics_device : public graphics_device<ogl_backend>
{
public:
    
    enum class feature
    {
        texturing
    };
    
    void enableFeature(feature f)
    {
        std::cout << "enabling feature... " << (int)f << '\n';
    }
};


// or...
// typedef graphics_device<ogl_backend> ogl_graphics_device


int main()
{
    ogl_graphics_device device;
    
    device.enableFeature(ogl_graphics_device::feature::texturing);
    
    auto texture = device.create<decltype(device)::texture>();
    
    texture.load({"hello"});
    
	return 0;
}

/*
 
 Expected output:
    enabling feature... 0
    loading, hello
    destroying texture

*/