#ifndef SURFACEINTEROPD3D11_H
#define SURFACEINTEROPD3D11_H
#ifdef WIN32

#ifdef SDK_SIMPLE_INCLUDE
#include "SurfaceInterop.h"
#include "dxcompat.h"
#include <d3d11.h>
#include <wrl/client.h>
#include "NVSurfaceInteropFactory.h"
#include "../YCKitExport.h"
#else
#include "../SurfaceInterop.h"
#include "dxcompat.h"
#include <d3d11_1.h>
#include <wrl/client.h>
#include "../NVSurfaceInteropFactory.h"
#include "YCKit/YCKitExport.h"
#endif

using namespace Microsoft::WRL;

namespace d3d11 {

	typedef uint32_t GLuint; // define here to avoid including gl headers which are not required by decoder
	typedef uint32_t GLenum;
	typedef int32_t  GLint;

enum InteropType {
    InteropAuto,
    InteropEGL,
    InteropGL
};

class DLL_DECL InteropResource
{
public:
    typedef unsigned int GLuint;
    static InteropResource* create(InteropType type = InteropAuto);
    /*!
     * \brief isSupported
     * \return true if support 0-copy interop. Currently only d3d11+egl, i.e. check egl build environment and runtime egl support.
     */
    static bool isSupported(InteropType type = InteropAuto);

    virtual ~InteropResource() {}
    void setDevice(ComPtr<ID3D11Device> dev);
    virtual NVPixelFormatEx::PixelFormat format(DXGI_FORMAT dxfmt) const = 0;
    /*!
     * \brief map
     * \param surface dxva decoded surface
     * \param index ID3D11Texture2D array  index
     * \param tex opengl texture
     * \param w frame width(visual width) without alignment, <= dxva surface width
     * \param h frame height(visual height)
     * \param plane useless now
     * \return true if success
     */
    virtual bool map(ComPtr<ID3D11Texture2D> surface, int index, GLuint* tex, int w, int h, int plane) = 0;
    virtual bool unmap(GLuint tex) { UNUSED(tex); return true;}

    void set_surface_map_failed();
    bool get_surface_map_failed() const;
    virtual void set_is_starting_close();
    virtual void set_is_starting_close_ui(bool closing);

    virtual bool is_need_release_in_uithread() = 0;

protected:
    ComPtr<ID3D11Device> d3ddev;
    int width = 0, height = 0; // video frame width and dx_surface width without alignment, not dxva decoded surface width
	bool surface_map_failed = false;
	bool is_starting_close = false;
	bool is_starting_close_ui = false;
};
typedef std::shared_ptr<InteropResource> InteropResourcePtr;


class SurfaceInterop : public VideoSurfaceInterop
{
public:
    SurfaceInterop(const InteropResourcePtr& res) : m_resource(res), frame_width(0), frame_height(0) {}
    ~SurfaceInterop();
    /*!
     * \brief setSurface
     * \param surface d3d11 decoded surface
     * \param index ID3D11Texture2D array  index
     * \param frame_w frame width(visual width) without alignment, <= d3d11 surface width
     * \param frame_h frame height(visual height)
     */
    void setSurface(ComPtr<ID3D11Texture2D> surface, HANDLE handle, int index, int frame_w, int frame_h);
    /// GLTextureSurface only supports rgb32
    void* map(SurfaceType type, const NVPixelFormatEx& fmt, void* handle, int plane) override;
    void unmap(void *handle) override;
	InteropResourcePtr getResouce() { return m_resource; }

    bool CopyTo(uint8_t* data[4], int strides[4]) override;
    bool CopyTo(void* textures[4]) override;

    virtual void set_hardware_surface_share_failed() override;
    virtual void set_display_closing_in_uithread(bool closing) override;

    bool is_need_release_in_uithread() override;

protected:
    /// copy from gpu (optimized if possible) and convert to target format if necessary
    void* mapToHost(const NVPixelFormatEx &format, void *handle, int plane);
private:
    ComPtr<ID3D11Texture2D> m_surface;
    HANDLE texture_handle_ = nullptr;
    int m_index;
    InteropResourcePtr m_resource;
    int frame_width, frame_height;
};

} //namespace d3d11

namespace NVSurfaceInterop {

	struct DLL_DECL D3D11SurfaceInterOpBuilder : public SurfaceInterOpBuilder {

		//we have a implementation for this.
		//You can override this if needed.
		virtual d3d11::InteropResource* create(d3d11::InteropType type);

		//Please provide this in Qt.
		virtual d3d11::InteropResource* CreateInteropGL() = 0;

		virtual d3d11::InteropResource* CreateInteropEGL() { return nullptr; }

		virtual std::string name() const override { return "D3D11SurfaceInterOpBuilder"; }

		virtual SurfaceInterOp* build() override { return nullptr; }

		virtual inline d3d11::SurfaceInterop* build(const d3d11::InteropResourcePtr& res) {
			return new d3d11::SurfaceInterop(res);
		}
	};
}

#endif
#endif //SURFACEINTEROPD3D11_H
