#ifndef RENDERING_H
#define RENDERING_H

#include "math3d.h"
#include "tier0/platform.h"
#include "tier1/utlbuffer.h"
#include "baseentity.h"
#include "tier1/utlstring.h"
#include "tier1/utlvector.h"

extern mat4 g_cameraView;


enum EImageFormat
{
	IMAGE_FORMAT_R8,
	IMAGE_FORMAT_R8G8,
	IMAGE_FORMAT_R8G8B8,
	IMAGE_FORMAT_R8G8B8A8,
	IMAGE_FORMAT_R16,
	IMAGE_FORMAT_R16G16,
	IMAGE_FORMAT_R16G16B16,
	IMAGE_FORMAT_R16G16B16A16,
	IMAGE_FORMAT_R32,
	IMAGE_FORMAT_R32G32,
	IMAGE_FORMAT_R32G32B32,
	IMAGE_FORMAT_R32G32B32A32,
	IMAGE_FORMAT_DEPTH,

	IMAGE_FORMAT_RENDERING = IMAGE_FORMAT_R8G8B8A8,
};

enum EImageUsage
{
	IMAGE_USAGE_COLOR_ATTACHMENT = 0x1,
	IMAGE_USAGE_DEPTH_ATTACHMENT = 0x2,
	IMAGE_USAGE_STORAGE = 0x4,
};

enum EVertexFormat
{
	VERTEX_FORMAT_X16,
	VERTEX_FORMAT_X16Y16,
	VERTEX_FORMAT_X16Y16Z16,
	VERTEX_FORMAT_X16Y16Z16W16,
	VERTEX_FORMAT_X32,
	VERTEX_FORMAT_X32Y32,
	VERTEX_FORMAT_X32Y32Z32,
	VERTEX_FORMAT_X32Y32Z32W32,
};

enum EMSAAMode
{
	MSAA_MODE_DISABLED,
	MSAA_MODE_MIN,
	MSAA_MODE_MAX,
	MSAA_MODE_AVERAGE,
};

enum EDepthMode
{
	DEPTH_MODE_DISABLED,
	DEPTH_MODE_EQUAL,
	DEPTH_MODE_NOT_EQUAL,
	DEPTH_MODE_LESS,
	DEPTH_MODE_LESS_EQUAL,
	DEPTH_MODE_GREATER,
	DEPTH_MODE_GREATER_EQUAL,
};

enum EShaderInputType
{
	SHADER_INPUT_TYPE_STORAGE_BUFFER,
	SHADER_INPUT_TYPE_UNIFORM_BUFFER,
	SHADER_INPUT_TYPE_IMAGE,
	SHADER_INPUT_TYPE_TLAS,
	
	// All available textures are binded
	SHADER_INPUT_TYPE_TEXTURES,
};

enum EShaderType
{
	SHADER_TYPE_VERTEX,
	SHADER_TYPE_GEOMETRY,
	SHADER_TYPE_FRAGMENT,
	SHADER_TYPE_COMPUTE,
	SHADER_TYPE_RAY_GEN,
	SHADER_TYPE_CLOSEST_HIT,
	SHADER_TYPE_MISS,
	SHADER_TYPE_INTERSECTION,
	SHADER_TYPE_ANY_HIT,
};

enum EBarrierMemoryPermissions
{
	BARRIER_MEMORY_PERMISSIONS_VERTEX_BUFFER_READ = 0x1,
	BARRIER_MEMORY_PERMISSIONS_INDEX_BUFFER_READ = 0x2,
	BARRIER_MEMORY_PERMISSIONS_UNIFORM_BUFFER_READ = 0x4,
	BARRIER_MEMORY_PERMISSIONS_SHADER_READ = 0x8,
	BARRIER_MEMORY_PERMISSIONS_SHADER_WRITE = 0x10,
	BARRIER_MEMORY_PERMISSIONS_COLOR_READ = 0x20,
	BARRIER_MEMORY_PERMISSIONS_COLOR_WRITE = 0x40,
	BARRIER_MEMORY_PERMISSIONS_DEPTH_READ = 0x80,
	BARRIER_MEMORY_PERMISSIONS_DEPTH_WRITE = 0x100,
	BARRIER_MEMORY_PERMISSIONS_COPY_READ = 0x200,
	BARRIER_MEMORY_PERMISSIONS_COPY_WRITE = 0x400,
};
enum EBarrierStage
{
	BARRIER_STAGE_TOP,
	BARRIER_STAGE_VERTEX_INPUT,
	BARRIER_STAGE_VERTEX_SHADER,
	BARRIER_STAGE_GEOMETRY_SHADER,
	BARRIER_STAGE_FRAGMENT_SHADER,
	BARRIER_STAGE_COMPUTE_SHADER,
	BARRIER_STAGE_RAY_TRACING_SHADER,
	BARRIER_STAGE_BOTTOM,
};

enum EAttachmentLoadMode
{
	ATTACHMENT_LOAD_MODE_DONT_CARE,
	ATTACHMENT_LOAD_MODE_CLEAR,
	ATTACHMENT_LOAD_MODE_LOAD,
};

enum EAttachmentStoreMode
{
	ATTACHMENT_STORE_MODE_DONT_CARE,
	ATTACHMENT_STORE_MODE_STORE,
};

enum EPipelineType
{
	PIPELINE_TYPE_RASTERIZATION,
	PIPELINE_TYPE_COMPUTE,
	PIPELINE_TYPE_RAY_TRACING,
};



interface IVideo 
{
public:
	static void Init();
	static void CreatePipelines( void );
	static void Frame( float fDelta );
};

abstract_class IBuffer
{
public:
	virtual void *Map() = 0;
	virtual void Unmap() = 0;
};

typedef IBuffer IStorageBuffer;
typedef IBuffer IUniformBuffer;
typedef IBuffer IVertexBuffer;
typedef IBuffer IIndexBuffer;

abstract_class IImage
{
public:
	EImageFormat format;
};

//----------------------------------------------------------------------------
// Bottom level acceleration structure for ray tracing. 
//----------------------------------------------------------------------------
abstract_class IBLAS
{
public:
	virtual void Build() = 0;
	virtual void Update() = 0;
};



//----------------------------------------------------------------------------
// Bottom level acceleration structure with triangle geometry.
//----------------------------------------------------------------------------
abstract_class IBLASMesh: public IBLAS
{
public:
	virtual void SetMesh( IVertexBuffer *pVertex, IIndexBuffer *pIndex ) = 0;
};

//----------------------------------------------------------------------------
// Bottom level acceleration structure with AABB geometry.
//----------------------------------------------------------------------------
abstract_class IBLASAABB: public IBLAS
{
public:
	virtual void SetBoundaries( vec4 size );
};

//----------------------------------------------------------------------------
// Top level acceleration structure for handling worlds
//----------------------------------------------------------------------------
abstract_class ITLAS
{
public:
	virtual void AddInstance( IBLAS *pBLAS, mat4 matrix, uint32_t data ) = 0;
	virtual void ResetInstances() = 0;
};


struct ShaderInput_t
{
	EShaderInputType type;
	uint32_t binding;
};

struct Shader_t
{
	const char *szPath;
	EShaderType type;
};

abstract_class IPipeline
{
public:
	EPipelineType type;
};
typedef IPipeline IGraphicsPipeline;
typedef IPipeline IComputePipeline;
typedef IPipeline IRayTracingPipeline;



//----------------------------------------------------------------------------
// Texture handle
//----------------------------------------------------------------------------
abstract_class ITexture
{
public:
	const char *szName;
	uint32_t id;
};

//----------------------------------------------------------------------------
// Manages loaded textures
//----------------------------------------------------------------------------
interface ITextureManager
{
public:
	static uint32_t GetTextureID(ITexture *pTexture);
	static ITexture *LoadTexture( void *pData, uint32_t X, uint32_t Y, uint32_t numChannels );
	static ITexture *LoadTexture( const char *szName );
};

struct BufferBarrier_t
{
	EBarrierMemoryPermissions in;
	EBarrierMemoryPermissions out;
	IBuffer *pBuffer;
};
struct ImageBarrier_t
{
	EBarrierMemoryPermissions in;
	EBarrierMemoryPermissions out;
	IBuffer *pBuffer;
};

struct VertexAttribute_t
{
	uint32_t offset;
	uint32_t binding;
	EVertexFormat format;
};

struct RenderingColorAttachment_t
{
	IImage *pOutput;
	IImage *pTemporary;
	EAttachmentLoadMode loadMode;
	EAttachmentStoreMode storeMode;
	EMSAAMode msaaMode;
	vec4 clearColor;
};

struct RenderingDepthAttachment_t
{
	IImage *pOutput;
	IImage *pTemporary;
	EAttachmentLoadMode loadMode;
	EAttachmentStoreMode storeMode;
	EMSAAMode msaaMode;
	float clearValue;
};


//----------------------------------------------------------------------------
// Manages all buffers and pipelines.
// It is meant to be used in render pipelines
//----------------------------------------------------------------------------
interface IRenderer
{
public:

	static IStorageBuffer *CreateStorageBuffer( uint32_t uSize );
	static IUniformBuffer *CreateUniformBuffer( uint32_t uSize );
	static IVertexBuffer *CreateVertexBuffer( uint32_t uSize );
	static IIndexBuffer *CreateIndexBuffer( uint32_t uSize );
	static IImage *CreateImage( EImageFormat format, uint32_t usage, uint32_t nWidth, uint32_t nHeight, uint32_t nSamples = 1 );

	static void DestroyBuffer( IBuffer *pBuffer );
	static void DestroyImage( IImage *pImage );

	static void SetConstants( uint32_t nSize, uint32_t nOffset, void *pData );
	static void Barrier( EBarrierStage stageIn, uint32_t stageOut, CUtlVector<BufferBarrier_t> buffers, CUtlVector<ImageBarrier_t> images );
	static void BindData( uint32_t binding, IBuffer *pBuffer, IImage* pImage);
	static void BindPipeline( IPipeline *pPipeline );


	static void Begin( uint32_t nWidth, uint32_t nHeight, CUtlVector<RenderingColorAttachment_t> attachments, RenderingDepthAttachment_t depth );
	static void ResetState();
	static void SetDepthMode( EDepthMode mode );
	static void Draw( IVertexBuffer *pVertex, IIndexBuffer *pIndex );
	static void Dispatch( uint32_t x, uint32_t y, uint32_t z );
	static void TraceRays( uint32_t x, uint32_t y, uint32_t z );
	static void End();

	static IGraphicsPipeline *CreateGraphicsPipeline(
				CUtlVector<Shader_t> shaders,
				CUtlVector<ShaderInput_t> inputs,
				uint32_t nConstantsSize,
				CUtlVector<EImageFormat> outputFormats
			);
	static IComputePipeline *CreateComputePipeline(
				Shader_t szShader,
				CUtlVector<ShaderInput_t> inputs,
				uint32_t nConstantsSize
			);

	static IRayTracingPipeline *CreateRayTracingPipeline(
				CUtlVector<Shader_t> shaders,
				CUtlVector<ShaderInput_t> inputs,
				uint32_t nConstantsSize
			);
};

abstract_class IRenderingPipelineStep
{
public:
	virtual void Init() = 0;
	virtual void Frame( float fDelta ) = 0;
	virtual void Deinit() = 0;
};

struct RenderingStep_t
{
	IRenderingPipelineStep *pPipeline;
	const char *szName;
};

extern CUtlVector<RenderingStep_t> g_StepPrepass;
extern CUtlVector<RenderingStep_t> g_StepMeshRendering;
extern CUtlVector<RenderingStep_t> g_StepShading;
extern CUtlVector<RenderingStep_t> g_StepPostProcessing;
extern CUtlVector<RenderingStep_t> g_StepUI;


typedef IRenderingPipelineStep*(*CreateRenderStepFn)();
class CRenderingStep
{
public:
	CRenderingStep();
	CRenderingStep(const char *szStepName, CreateRenderStepFn pfn);
};

class CPrepassRenderingStep: public CRenderingStep
{
public:
	CPrepassRenderingStep(const char *szStepName, CreateRenderStepFn pfn);
};
class CMeshRenderingStep: public CRenderingStep
{
public:
	CMeshRenderingStep(const char *szStepName, CreateRenderStepFn pfn);
};

class CShadingRenderingStep: public CRenderingStep
{
public:
	CShadingRenderingStep(const char *szStepName, CreateRenderStepFn pfn);
};

class CPostProcessingRenderingStep: public CRenderingStep
{
public:
	CPostProcessingRenderingStep(const char *szStepName, CreateRenderStepFn pfn);
};

class CUIRenderingStep: public CRenderingStep
{
public:
	CUIRenderingStep(const char *szStepName, CreateRenderStepFn pfn);
};

#define DECLARE_MESH_PREPASS_STAGE(class, name) \
IRenderingPipelineStep *__rendering_stage_##name() \
{ \
	return new class; \
}; \
CPrepassRenderingStep __renering_##name##_registry(#name, __rendering_stage_##name);
#define DECLARE_MESH_RENDERING_STAGE(class, name) \
IRenderingPipelineStep *__rendering_stage_##name() \
{ \
	return new class; \
}; \
CMeshRenderingStep __renering_##name##_registry(#name, __rendering_stage_##name);
#define DECLARE_MESH_SHADING_STAGE(class, name) \
IRenderingPipelineStep *__rendering_stage_##name() \
{ \
	return new class; \
}; \
CShadingRenderingStep __renering_##name##_registry(#name, __rendering_stage_##name);
#define DECLARE_POST_PROCESSING_STAGE(class, name) \
IRenderingPipelineStep *__rendering_stage_##name() \
{ \
	return new class; \
}; \
CPostProcessingRenderingStep __renering_##name##_registry(#name, __rendering_stage_##name);
#define DECLARE_UI_RENDERING_STAGE(class, name) \
IRenderingPipelineStep *__rendering_stage_##name() \
{ \
	return new class; \
}; \
CUIRenderingStep __renering_##name##_registry(#name, __rendering_stage_##name);

#endif
