#ifndef COMMONS_INDIRECT_H
#define COMMONS_INDIRECT_H

#include <shaders/materials/commons_instancing_buffers.h>

// NOTE: Add more defines here for utility functions
#if defined(MAIN_BUILD_DRAW_ELEMENTS_INDIRECT_FROM_BUFFER) || defined(MAIN_BUILD_DRAW_ARRAYS_INDIRECT_FROM_BUFFER) || defined(MAIN_BUILD_DISPATCH_INDIRECT_FROM_BUFFER)
#version 430
layout (local_size_x = 32, local_size_y = 1) in;
#endif

struct DrawElementsIndirectParams
{
	uint  count;
	uint  instance_count;
	uint  first_index;
	uint  base_vertex;
	uint  base_instance;
};

struct DrawArraysIndirectParams
{
	uint  count;
	uint  instance_count;
	uint  first_index;
	uint  base_instance;
};

struct DispatchIndirectParams
{
	uint x;
	uint y;
	uint z;
};

// DrawElementsIndirect support

#ifdef MAIN_BUILD_DRAW_ELEMENTS_INDIRECT_FROM_BUFFER

// Take data from some arbitrary buffer location, transform it a bit and build
// DrawIndirect call from it. For now only single instance

layout (std430) buffer DrawElementsIndirectParamsBuffer {
	DrawElementsIndirectParams draw_indirect_params[];
};

layout (std430) buffer DrawIndirectSourceBuffer {
	uint draw_indirect_src_data[];
};

layout (std140, row_major) uniform DrawIndirectInstanceParamsBuffer {
	InstanceParams draw_indirect_instance_params;
};

struct BuildDrawIndirectFromBufferParams
{
	uint indirect_buffer_idx;
	uint src_buffer_offset;
	uint src_buffer_data_numerator;
	uint src_buffer_data_denominator;
};

layout(std140, row_major) uniform BuildDrawIndirectFromBufferParamsBuffer {
	BuildDrawIndirectFromBufferParams params;
};

void MAIN_BUILD_DRAW_ELEMENTS_INDIRECT_FROM_BUFFER()
{
	uint idx = gl_GlobalInvocationID.x;

	if (idx == 0)
	{
		draw_indirect_params[params.indirect_buffer_idx].count = 
			draw_indirect_src_data[params.src_buffer_offset] * params.src_buffer_data_numerator / params.src_buffer_data_denominator;

		draw_indirect_params[params.indirect_buffer_idx].instance_count = min(draw_indirect_instance_params.buffer_capacity, draw_indirect_instance_params.instance_count);
		draw_indirect_params[params.indirect_buffer_idx].first_index = 0;
		draw_indirect_params[params.indirect_buffer_idx].base_vertex = 0;
		draw_indirect_params[params.indirect_buffer_idx].base_instance = 0;
	}
}

#endif



// DrawArraysIndirect support

#ifdef MAIN_BUILD_DRAW_ARRAYS_INDIRECT_FROM_BUFFER

// Take data from some arbitrary buffer location, transform it a bit and build
// DrawIndirect call from it. For now only single instance

layout (std430) buffer DrawArraysIndirectParamsBuffer {
	DrawArraysIndirectParams draw_indirect_params[];
};

layout (std430) buffer DrawIndirectSourceBuffer {
	uint draw_indirect_src_data[];
};

layout (std140, row_major) uniform DrawIndirectInstanceParamsBuffer {
	InstanceParams draw_indirect_instance_params;
};

struct BuildDrawIndirectFromBufferParams
{
	uint indirect_buffer_idx;
	uint src_buffer_offset;
	uint src_buffer_data_numerator;
	uint src_buffer_data_denominator;
};

layout(std140, row_major) uniform BuildDrawIndirectFromBufferParamsBuffer {
	BuildDrawIndirectFromBufferParams params;
};

void MAIN_BUILD_DRAW_ARRAYS_INDIRECT_FROM_BUFFER()
{
	uint idx = gl_GlobalInvocationID.x;

	if (idx == 0)
	{
		draw_indirect_params[params.indirect_buffer_idx].count = 
			draw_indirect_src_data[params.src_buffer_offset] * params.src_buffer_data_numerator / params.src_buffer_data_denominator;

		draw_indirect_params[params.indirect_buffer_idx].instance_count = min(draw_indirect_instance_params.buffer_capacity, draw_indirect_instance_params.instance_count);
		draw_indirect_params[params.indirect_buffer_idx].first_index = 0;
		draw_indirect_params[params.indirect_buffer_idx].base_instance = 0;
	}
}

#endif



// DispatchIndirect support

#ifdef MAIN_BUILD_DISPATCH_INDIRECT_FROM_BUFFER

// Take data from some arbitrary buffer location, transform it a bit and build
// DrawIndirect call from it. For now only single instance

layout (std430) buffer DispatchIndirectParamsBuffer {
	DispatchIndirectParams dispatch_indirect_params[];
};

layout (std430) buffer DispatchIndirectSourceBuffer {
	uint dispatch_indirect_src_data[];
};

layout (std140, row_major) uniform DispatchIndirectInstanceParamsBuffer {
	InstanceParams dispatch_indirect_instance_params;
};

struct BuildDispatchIndirectFromBufferParams
{
	uint indirect_buffer_idx;
	uint src_buffer_offset_x;
	uint src_buffer_data_numerator_x;
	uint src_buffer_data_denominator_x;
	uint src_buffer_offset_y;
	uint src_buffer_data_numerator_y;		// not evaluated if 0. outputs 1 into dispatch
	uint src_buffer_data_denominator_y;
	uint src_buffer_offset_z;
	uint src_buffer_data_numerator_z;		// not evaluated if 0. outputs 1 into dispatch
	uint src_buffer_data_denominator_z;
};

layout(std140, row_major) uniform BuildDispatchIndirectFromBufferParamsBuffer {
	BuildDispatchIndirectFromBufferParams params;
};

void MAIN_BUILD_DISPATCH_INDIRECT_FROM_BUFFER()
{
	uint idx = gl_GlobalInvocationID.x;

	if (idx == 0)
	{
		dispatch_indirect_params[params.indirect_buffer_idx].x = 
			(
			    dispatch_indirect_src_data[params.src_buffer_offset_x]
			  * params.src_buffer_data_numerator_x
			  * min(dispatch_indirect_instance_params.buffer_capacity, dispatch_indirect_instance_params.instance_count)
			  + params.src_buffer_data_denominator_x - 1
			) / params.src_buffer_data_denominator_x;

		dispatch_indirect_params[params.indirect_buffer_idx].y = 1;
		dispatch_indirect_params[params.indirect_buffer_idx].z = 1;

		if( params.src_buffer_data_numerator_y > 0)
		{
			dispatch_indirect_params[params.indirect_buffer_idx].y = 
				(dispatch_indirect_src_data[params.src_buffer_offset_y] * params.src_buffer_data_numerator_y + params.src_buffer_data_denominator_y - 1) / params.src_buffer_data_denominator_y;
		}

		if (params.src_buffer_data_numerator_z > 0)
		{
			dispatch_indirect_params[params.indirect_buffer_idx].z = 
				(dispatch_indirect_src_data[params.src_buffer_offset_z] * params.src_buffer_data_numerator_z + params.src_buffer_data_denominator_z - 1) / params.src_buffer_data_denominator_z;
		}
	}
}

#endif


#endif
