#ifndef NVVIDEOIMAGEBUFFEREX_H
#define NVVIDEOIMAGEBUFFEREX_H
#ifdef SDK_SIMPLE_INCLUDE
#include "../NVVideoImageBuffer.h"
#include "../YCKitExport.h"
#else
#include "YCKit/Video/NVVideoImageBuffer.h"
#include "YCKit/YCKitExport.h"
#endif
#include "NVPixelFormatEx.h"
#include "SurfaceInterop.h"

class DLL_DECL NVHardwareVideoImageBuffer:public NVVideoImageBuffer {
public:
    /////////////from base/////////////
    virtual const void * getNativeBufferObj() override {return this;}
    virtual const NVVideoSize getImageSize() const override  {return vSize;}

    //CPU may not access real raw buffer, only GPU does.
    virtual bool isDMA() const override{return true;}

    //only available when isDMA() == false
    virtual const unsigned char* getRawBuffer() const override  {return nullptr;}

    //only available when isDMA() == false
    virtual size_t getRawBufferSize() const override {return 0;}

    virtual NVPixelFormat getPixelFormat() const override;
    virtual NVPixelFormat getPixelFormatForEncoder() const override;
    /////////////end of from base/////////////

    /////////////from base proctected/////////////////////////////
    //the real buffer is controlled by VideoSurfaceInterop. So need call unmap in VideoSurfaceInterop to release if you mapped.
    //Surface is released in decoder close in MacOS.

    //only use it in mode MODE_CALLEE_OWNED_MANUALLY
    virtual void retain() override {}
    //only use it in mode MODE_CALLEE_OWNED_MANUALLY
    virtual void release() override {}
    //only use it in mode MODE_CALLER_OWNED
    virtual void free() override {}

    virtual NativeBufferOwnMode getOwnMode() const override{return MODE_HARDWARE_OWNED;}


    /////////////from base proctected/////////////////////////////

    /////////////base frame ///////////////////
    /*!
     * \brief planeCount
     *  a decoded frame can be packed and planar. packed format has only 1 plane, while planar
     *  format has more than 1 plane. For audio, the number plane equals channel count. For
     *  video, rgb is 1 plane, yuv420p is 3 plane, p means planar
     * \param plane default is the first plane
     * \return
     */
    int planeCount() const;
    /*!
     * \brief channelCount
     * for audio, channel count equals plane count
     * for video, channels >= planes
     * \return
     */
    virtual int channelCount() const;
    /*!
     * \brief bytesPerLine
     *   For video, it's size of each picture line. For audio, it's the whole size of plane
     * \param plane
     * \return line size of plane
     */
    int bytesPerLine(int plane = 0) const;

    void setBytesPerLine(int lineSize, int plane);
    void setBytesPerLine(const std::vector<int> &lineSize);
    void setBytesPerLine(int stride[]);

    /////////////end of base frame ///////////////////
    NVPixelFormatEx format() const {return fmt;}
    NVPixelFormatEx::PixelFormat pixelFormat() const {return fmt.pixelFormat();}

    int pixelFormatFFmpeg() const {return fmt.pixelFormatFFmpeg();}

    bool isValid() const;
    operator bool() const { return isValid();}

    /*!
     * \brief effectiveBytesPerLine
     * The plane bytes contains valid image data without padded data for alignment reason
     */
    int effectiveBytesPerLine(int plane) const;
    // plane width with padded bytes for alignment.
    int planeWidth(int plane) const;
    int planeHeight(int plane) const;
    // display attributes
    float displayAspectRatio() const;
    void setDisplayAspectRatio(float displayAspectRatio);

    byte* bits(int plane) const;
    const byte* constBits(int plane) const;

//    ColorSpace colorSpace() const;
//    void setColorSpace(ColorSpace value);
//    ColorRange colorRange() const;
//    void setColorRange(ColorRange value);

    /*!
     * map a gpu frame to opengl texture or d3d texture or other handle.
     * handle: given handle. can be gl texture (& GLuint), d3d texture, or 0 if create a new handle
     * return the result handle or 0 if not supported
     */
    void* map(SurfaceType type, void* handle, int plane = 0);
    void* map(SurfaceType type, void* handle, const NVPixelFormatEx& fmt, int plane = 0);
    void unmap(void* handle);

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

    /*!
     * \brief createInteropHandle
     * \param handle input/output handle
     * \return null on error. otherwise return the input handle
     */
    void* createInteropHandle(void* handle, SurfaceType type, int plane);

    NVHardwareVideoImageBuffer(const NVVideoSize& s,const NVPixelFormatEx& fmt);
    ~NVHardwareVideoImageBuffer();

    void set_surface_interop(std::shared_ptr<VideoSurfaceInterop> v) {surface_interop = v;}

    std::shared_ptr<VideoSurfaceInterop> get_surface_interop() const {return surface_interop;}

    void set_target_is_rect(bool v) {targetToRect = v;}

    bool get_target_is_rect() const {return targetToRect;}

    void set_timestamp(double v) {timestamp = v;}

    double get_timestamp() const {return timestamp;}

    virtual void setTs(uint64_t ts) override { timestamp = ts / 1000; }
    virtual uint64_t getTs() const override {return timestamp*1000;}

    void setPixelFormat(NVPixelFormat fmt) { sw_pixel_format_ = fmt; }
    void setHWPixelFormat(NVPixelFormat fmt) { hw_pixel_format_ = fmt; }

protected:
    NVPixelFormatEx fmt;
    NVPixelFormat sw_pixel_format_ = NV_FMT_NONE;
    NVVideoSize vSize;
    std::shared_ptr<VideoSurfaceInterop> surface_interop;
    std::vector<int> line_sizes; //stride
    std::vector<byte*> planes; //planes. only use planes.size() here. never use its byte* here (but can use in NVHardwareVideoImageBufferCopiedToHost)
    int data_align = 1;
    double timestamp;
    float m_displayAspectRatio = 0;
    bool targetToRect = false;
};

#endif // NVVIDEOIMAGEBUFFEREX_H
