#pragma once

#include "Array.h"

struct ID3D10Blob;
struct ID3D11PixelShader;
struct ID3D11VertexShader;
struct ID3D11Device;
struct ID3D11DeviceContext;
struct D3D11_INPUT_ELEMENT_DESC;
struct ID3D11Buffer;
struct ID3D11InputLayout;

namespace Framework
{
    class Text;
    class DXBuffer;
    class DX12CopyCommandQueue;
    class DX12DirectCommandQueue;

    enum ShaderType
    {
        UNBEKANNT,
        VERTEX,
        PIXEL
    };

    //! Eine Shader Klasse, die alle Constanten Buffers eines Shaders verwaltet
    class Shader : public virtual ReferenceCounter
    {
    protected:
        ShaderType type;
        RCArray<DXBuffer>* constBuffers;

    public:
        //! Konstruktor
        DLLEXPORT Shader();
        //! Destruktor
        DLLEXPORT virtual ~Shader();
        //! Setzt den Compilierten Shader
        //!  zD3d11Device: Das Device, mit welchem der Shader erstellt werden
        //!  soll
        //! \param bytes Die Bytes des compilierten codes
        //! \param length die Länge des bytearrays
        //! \return true, wenn bytes gültig ist, false sonst
        virtual bool setCompiledByteArray(unsigned char* bytes, int length) = 0;
        //! Nach dem Aufruf dieser Funktion wird dieser Shader als Pixel Shader
        //! benutzt
        //!  zD3d11Context: Das Context Objekt, mit dem der Shader verwendet
        //!  werden soll
        virtual void benutzeShader() = 0;
        //! erstellt ein constanten Buffer, der constante daten an den Shader
        //! übergibt es können maximal 14 Buffer erstellt werden
        //!  zD3d11Device: Das Device, mit dem der Buffer erstellt werden soll
        //! \param size Die größe des buffers in byte
        //! \param index Die position des Buffers im Buffer Array. Bereits
        //! vorhanderner Buffer wird ersetzt. Buffer 1 kann nicht erstellt
        //! werden, wenn Buffer 0 noch nicht erstellt wurde usw.
        virtual bool erstelleConstBuffer(int size, int index) = 0;
        //! Löscht einen constanten Buffer
        //! \param index der Index des Buffers, der gelöscht werden soll. Buffer
        //! 0 kann nicht gelöscht werden, solange Buffer 1 noch existiert usw.
        DLLEXPORT bool removeConstBuffer(int index);
        //! Kopiert daten in einen constanten buffer
        //!  zD3d11Context: Das Context Objekt, das zum kopieren verwendt werden
        //!  soll
        //! \param data Einen zeiger auf en byte Array der größe des Buffers
        //! \param index Der Index des Buffers
        //! \param len Die Länge der Daten in Bytes (-1 für die maximale größe
        //! des Buffers)
        DLLEXPORT bool füllConstBuffer(char* data, int index, int len = -1);
        //! Gibt die Länge eines constanten Buffers zurück
        //! \param index Der Index des Buffers
        DLLEXPORT int getConstBufferLänge(int index) const;
        //! Gibt den Shadertyp zurück
        DLLEXPORT ShaderType getType() const;
        //! Gibt den index des ersten nicht initialisierten buffers zurück
        DLLEXPORT int getFirstUninitializedBufferIndex() const;
    };

    class DX11Shader : public Shader
    {
    protected:
        ID3D11Device* device;
        ID3D11DeviceContext* context;

    public:
        DLLEXPORT DX11Shader(
            ID3D11Device* device, ID3D11DeviceContext* context);
        DLLEXPORT virtual ~DX11Shader();
        //! erstellt ein constanten Buffer, der constante daten an den Shader
        //! übergibt es können maximal 14 Buffer erstellt werden
        //!  zD3d11Device: Das Device, mit dem der Buffer erstellt werden soll
        //! \param size Die größe des buffers in byte
        //! \param index Die position des Buffers im Buffer Array. Bereits
        //! vorhanderner Buffer wird ersetzt. Buffer 1 kann nicht erstellt
        //! werden, wenn Buffer 0 noch nicht erstellt wurde usw.
        DLLEXPORT virtual bool erstelleConstBuffer(
            int size, int index) override;
    };

    //! Verwaltet einen Pixel Shader
    class DX11PixelShader : public DX11Shader
    {
    private:
        ID3D11PixelShader* pixelShader;

    public:
        //! Konstruktor
        DLLEXPORT DX11PixelShader(
            ID3D11Device* device, ID3D11DeviceContext* context);
        //! Destruktor
        DLLEXPORT ~DX11PixelShader();
        //! Setzt den Compilierten Shader
        //!  zD3d11Device: Das Device, mit welchem der Shader erstellt werden
        //!  soll
        //! \param bytes Die Bytes des compilierten codes
        //! \param length die Länge des bytearrays
        //! \return true, wenn bytes gültig ist, false sonst
        DLLEXPORT bool setCompiledByteArray(
            unsigned char* bytes, int length) override;
        //! Nach dem Aufruf dieser Funktion wird dieser Shader als Pixel Shader
        //! benutzt
        //!  zD3d11Context: Das Context Objekt, mit dem der Shader verwendet
        //!  werden soll
        DLLEXPORT void benutzeShader() override;
    };

    //! Verwaltet einen Vertex Shader
    class DX11VertexShader : public DX11Shader
    {
    private:
        ID3D11VertexShader* vertexShader;
        ID3D11InputLayout* inputLayout;
        unsigned char* shaderByteBuffer;
        int byteBufferSize;

    public:
        //! Konstruktor
        DLLEXPORT DX11VertexShader(
            ID3D11Device* device, ID3D11DeviceContext* context);
        //! Destruktor
        DLLEXPORT ~DX11VertexShader();
        //! Setzt den Compilierten Shader
        //!  zD3d11Device: Das Device, mit welchem der Shader erstellt werden
        //!  soll
        //! \param bytes Die Bytes des compilierten codes
        //! \param length die Länge des bytearrays
        //! \return true, wenn bytes gültig ist, false sonst
        DLLEXPORT bool setCompiledByteArray(
            unsigned char* bytes, int length) override;
        //! erstellt ein InputLayout für den Shader
        //! Darf erst nach compile aufgerufen werden
        //!  zD3d11Device: Das Device, mit dem das Layout erstellt werden soll
        //! \param descArray Ein Array mit initialisierungsdaten
        //! \param anz Die Anzahl der Elemente im Array
        DLLEXPORT bool erstelleInputLayout(
            D3D11_INPUT_ELEMENT_DESC* descArray, int anz);
        //! Nach dem Aufruf dieser Funktion wird dieser Shader als Vertex Shader
        //! benutzt
        //!  zD3d11Context: Das Context Objekt, mit dem der Shader verwendet
        //!  werden soll
        DLLEXPORT void benutzeShader() override;
    };
} // namespace Framework