#pragma once
#include <Graphics/Shader.h>
#include <Graphics/Texture.h>

namespace Juliet
{
    // Forward Declare
    struct ColorTargetDescription;

    enum class FillMode : uint8
    {
        Solid,
        Wireframe,
        Count
    };

    enum class CullMode : uint8
    {
        None,
        Front,
        Back,
        Count
    };

    enum class FrontFace : uint8
    {
        CounterClockwise,
        Clockwise,
        Count
    };

    enum class PrimitiveType : uint8
    {
        TriangleList,
        TriangleStrip,
        LineList,
        LineStrip,
        PointList,
        Count
    };

    struct RasterizerState
    {
        FillMode  FillMode;
        CullMode  CullMode;
        FrontFace FrontFace;

        float DepthBiasConstantFactor; // How much depth value is added to each fragment
        float DepthBiasClamp;          // Maximum depth bias
        float DepthBiasSlopeFactor;    // Scalar applied to Fragment's slope
        bool  EnableDepthBias;         // Bias fragment depth values
        bool  EnableDepthClip;         // True to clip, false to clamp
    };

    enum class VertexInputRate : uint8
    {
        Vertex,   // Use vertex index
        Instance, // Use instance index
        Count
    };

    struct VertexBufferDescription
    {
        uint32          Slot;         // Binding Slot
        uint32          PitchInBytes; // Pitch between two elements
        VertexInputRate InputRate;
        uint32 InstanceStepRate; // Only used when input rate == Instance. Number of instances to draw before advancing in the instance buffer by 1
    };

    enum class VertexElementFormat : uint8
    {
        Invalid,

        /* 32-bit Signed Integers */
        Int,
        Int2,
        Int3,
        Int4,

        /* 32-bit Unsigned Integers */
        UInt,
        UInt2,
        UInt3,
        UInt4,

        /* 32-bit Floats */
        Float,
        Float2,
        Float3,
        Float4,

        /* 8-bit Signed Integers */
        Byte2,
        Byte4,

        /* 8-bit Unsigned Integers */
        UByte2,
        UByte4,

        /* 8-bit Signed Normalized */
        Byte2_Norm,
        Byte4_Norm,

        /* 8-bit Unsigned Normalized */
        UByte2_Norm,
        UByte4_Norm,

        /* 16-bit Signed Integers */
        Short2,
        Short4,

        /* 16-bit Unsigned Integers */
        UShort2,
        UShort4,

        /* 16-bit Signed Normalized */
        Short2_Norm,
        Short4_Norm,

        /* 16-bit Unsigned Normalized */
        UShort2_Norm,
        UShort4_Norm,

        /* 16-bit Floats */
        Half2,
        Half4,

        //
        Count
    };

    struct VertexAttribute
    {
        uint32              Location;   // Shader input location index
        uint32              BufferSlot; // Binding slot of associated vertex buffer
        VertexElementFormat Format;     // Size and type of attribute
        uint32              Offset;     // Offset of this attribute relative to the start of the vertex element
    };

    struct VertexInputState
    {
        const VertexBufferDescription* VertexBufferDescriptions;
        uint32                         NumVertexBufferDescriptions;
        const VertexAttribute*         VertexAttributes;
        uint32                         NumVertexAttributes;
    };

    struct GraphicsPipelineTargetInfo
    {
        const ColorTargetDescription* ColorTargetDescriptions;
        size_t                        NumColorTargets;
        TextureFormat                 DepthStencilFormat;
        bool                          HasDepthStencilTarget;
    };

    enum class CompareOperation : uint8
    {
        Invalid,
        Never,          // The comparison always evaluates false.
        Less,           // The comparison evaluates reference < test.
        Equal,          // The comparison evaluates reference == test.
        LessOrEqual,    // The comparison evaluates reference <= test.
        Greater,        // The comparison evaluates reference > test.
        NotEqual,       // The comparison evaluates reference != test.
        GreaterOrEqual, // The comparison evalutes reference >= test.
        Always,         // The comparison always evaluates true.
        Count
    };

    enum class StencilOperation : uint8
    {
        Invalid,
        Keep,              // Keeps the current value.
        Zero,              // Sets the value to 0.
        Replace,           // Sets the value to reference.
        IncrementAndClamp, // Increments the current value and clamps to the maximum value.
        DecrementAndClamp, // Decrements the current value and clamps to 0.
        Invert,            // Bitwise-inverts the current value.
        IncrementAndWrap,  // Increments the current value and wraps back to 0.
        DecrementAndWrap,  // Decrements the current value and wraps to the maximum value.
        Count
    };

    struct StencilOperationState
    {
        StencilOperation FailOperation; // The action performed on samples that fail the stencil test.
        StencilOperation PassOperation; // The action performed on samples that pass the depth and stencil tests.
        StencilOperation DepthFailOperation; // The action performed on samples that pass the stencil test and fail the depth test.
        StencilOperation CompareOperation; // The comparison operator used in the stencil test.
    };

    struct DepthStencilState
    {
        CompareOperation      CompareOperation;  // The comparison operator used for depth testing.
        StencilOperationState BackStencilState;  // The stencil op state for back-facing triangles.
        StencilOperationState FrontStencilState; // The stencil op state for front-facing triangles.
        uint8                 CompareMask; // Selects the bits of the stencil values participating in the stencil test.
        uint8                 WriteMask;   // Selects the bits of the stencil values updated by the stencil test.
        bool                  EnableDepthTest : 1; // true enables the depth test.
        bool EnableDepthWrite : 1; // true enables depth writes. Depth writes are always disabled when enable_depth_test is false.
        bool EnableStencilTest : 1; // true enables the stencil test.
    };

    struct MultisampleState
    {
        TextureSampleCount SampleCount;
        uint32             SampleMask; // Which sample should be updated. If Enabled mask == false -> 0xFFFFFFFF
        bool               EnableMask;
    };

    struct GraphicsPipelineCreateInfo
    {
        Shader*                    VertexShader;
        Shader*                    FragmentShader;
        PrimitiveType              PrimitiveType;
        GraphicsPipelineTargetInfo TargetInfo;
        RasterizerState            RasterizerState;
        MultisampleState           MultisampleState;
        VertexInputState           VertexInputState;
        DepthStencilState          DepthStencilState;
    };

    // Opaque type
    struct GraphicsPipeline;
} // namespace Juliet
