#pragma once

#include "Critical.h"
#include "OperatingSystem.h"
#include "ReferenceCounter.h"

#ifdef WIN32
struct ID3D11Buffer;
struct D3D11_BUFFER_DESC;
struct ID3D11ShaderResourceView;
struct ID3D11Device;
struct ID3D11DeviceContext;
#endif

namespace Framework
{
    class DX12CopyCommandQueue;
    class DX12DirectCommandQueue;

    //! An interface between main memory and graphics memory
    class DXBuffer : public virtual ReferenceCounter
    {
    protected:
        void* data;
        bool changed;
        int len;
        int elLen;

    public:
        //! Constructor
        //! \param bind The usage of the buffer. Example:
        //! D3D11_BIND_INDEX_BUFFER, D3D11_BIND_VERTEX_BUFFER. \param eLen
        //! Length of a single element in bytes
        DLLEXPORT DXBuffer(int eLen);
        //! Destructor
        DLLEXPORT virtual ~DXBuffer();
        //! Sets the changed flag so that the data is copied again on the
        //! next call of 'copy'
        DLLEXPORT void setChanged();
        //! Changes the length of the buffer on the next call of 'copy'
        //! \param len The length in bytes
        DLLEXPORT void setLength(int len);
        //! Sets what will be copied on the next call of 'copy'
        //! \param data A pointer to the data
        DLLEXPORT void setData(void* data);
        //! Copies the data into the buffer if it has changed
        DLLEXPORT virtual void copyToGPU(int byteCount = -1) = 0;
        //! Returns the length of an element in bytes
        DLLEXPORT int getElementLength() const;
        //! Returns the number of elements in the buffer
        DLLEXPORT int getElementCount() const;
    };

#ifdef WIN32
    //! A buffer with data in graphics memory
    class DX11Buffer : public DXBuffer
    {
    protected:
        D3D11_BUFFER_DESC* description;
        ID3D11Buffer* buffer;
        ID3D11Device* device;
        ID3D11DeviceContext* context;
        Critical& deviceLock;

    public:
        //! Constructor
        //! eSize: The length of an element in bytes
        DLLEXPORT DX11Buffer(int eSize,
            ID3D11Device* device,
            ID3D11DeviceContext* context,
            int bindFlags,
            Critical& deviceLock);
        //! Destructor
        DLLEXPORT virtual ~DX11Buffer();
        //! Copies the data into the buffer if it has changed
        DLLEXPORT virtual void copyToGPU(int byteCount = -1) override;
        //! Returns the buffer
        DLLEXPORT ID3D11Buffer* zBuffer() const;
    };

    //! A buffer of indices from the vertex buffer, where every three
    //! form a triangle that is drawn
    class DX11StructuredBuffer : public DX11Buffer
    {
    private:
        ID3D11ShaderResourceView* view;

    public:
        //! Constructor
        //! eSize: The length of an element in bytes
        DLLEXPORT DX11StructuredBuffer(int eSize,
            ID3D11Device* device,
            ID3D11DeviceContext* context,
            Critical& deviceLock);
        //! Destructor
        DLLEXPORT virtual ~DX11StructuredBuffer();
        //! Copies the data into the buffer if it has changed
        DLLEXPORT void copyToGPU(int byteCount = -1) override;
        //! Returns the used shader resource view
        DLLEXPORT operator ID3D11ShaderResourceView*() const;
    };
#endif
} // namespace Framework