#ifndef SURFACEINTEROPD3D9_H
#define SURFACEINTEROPD3D9_H
#ifdef WIN32
#include <atomic>
#include <d3d9.h>
#include <wrl/client.h>
#ifdef SDK_SIMPLE_INCLUDE
#include "SurfaceInterop.h"
#include "NVSurfaceInteropFactory.h"
#include "../YCKitExport.h"
#else
#include "../SurfaceInterop.h"
#include "../NVSurfaceInteropFactory.h"
#include "YCKit/YCKitExport.h"
#endif


namespace d3d9 {

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
};

using Microsoft::WRL::ComPtr;

class DLL_DECL InteropResource
{
public:
    static bool isSupported(InteropType type = InteropAuto);
    //static InteropResource* create(IDirect3DDevice9 * dev, InteropType type = InteropAuto);
    typedef unsigned int GLuint;
    InteropResource(IDirect3DDevice9 * d3device);
    virtual ~InteropResource();
    /*!
     * \brief map
     * \param surface d3d9 surface
     * \param tex opengl texture
     * \param w frame width(visual width) without alignment, <= d3d9 surface width
     * \param h frame height(visual height)
     * \param plane useless now
     * \return true if success
     */
    virtual bool map(IDirect3DSurface9* surface, GLuint* tex, int w, int h, int plane) = 0;
    virtual bool unmap(GLuint tex) { UNUSED(tex); return true;}
    virtual void set_is_starting_close();
    virtual void set_is_starting_close_ui(bool closing);

    void set_surface_map_failed();
    bool get_surface_map_failed() const;

    virtual bool is_need_release_in_uithread() = 0;

protected:
    virtual void releaseDX();

    ComPtr<IDirect3DDevice9> d3ddev;
    ComPtr<IDirect3DTexture9> dx_texture;
    ComPtr<IDirect3DSurface9> dx_surface; // size is frame size(visual size) for display
    int width = 0, height = 0; // video frame width and dx_surface width without alignment, not dxva decoded surface width
	std::atomic_bool surface_map_failed = false;
    std::atomic_bool is_starting_close = false;
    std::atomic_bool is_starting_close_ui = false;
};
typedef std::shared_ptr<InteropResource> InteropResourcePtr;

class SurfaceInterop : public VideoSurfaceInterop
{
public:
    SurfaceInterop(const InteropResourcePtr& res) : m_surface(0), m_resource(res), frame_width(0), frame_height(0) {}
    ~SurfaceInterop();
    /*!
     * \brief setSurface
     * \param surface d3d9 surface
     * \param frame_w frame width(visual width) without alignment, <= d3d9 surface width
     * \param frame_h frame height(visual height)
     */
    void setSurface(IDirect3DSurface9* surface, 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;

    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<IDirect3DSurface9> m_surface;
    InteropResourcePtr m_resource;
    int frame_width = 0;
    int frame_height = 0;
};
} //namespace d3d9

namespace NVSurfaceInterop {

	struct DLL_DECL D3D9SurfaceInterOpBuilder : public SurfaceInterOpBuilder {

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

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

		virtual d3d9::InteropResource* CreateInteropEGL(IDirect3DDevice9* dev) { return nullptr; }

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

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

		virtual inline d3d9::SurfaceInterop* build(const d3d9::InteropResourcePtr& res) {
			return new d3d9::SurfaceInterop(res);
		}
	};
}
#endif
#endif // SURFACEINTEROPD3D9_H
