/* Copyright (c) 2017-2026 Hans-Kristian Arntzen
 *
 * Permission is hereby granted, free of charge, to any person obtaining
 * a copy of this software and associated documentation files (the
 * "Software"), to deal in the Software without restriction, including
 * without limitation the rights to use, copy, modify, merge, publish,
 * distribute, sublicense, and/or sell copies of the Software, and to
 * permit persons to whom the Software is furnished to do so, subject to
 * the following conditions:
 *
 * The above copyright notice and this permission notice shall be
 * included in all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
 * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */

#pragma once

#include "math.hpp"
#include "image.hpp"
#include "lights/light_info.hpp"
#include "limits.hpp"

namespace Granite
{
class LightClusterer;
class VolumetricFog;
class VolumetricDiffuseLightManager;
enum { NumShadowCascades = 4 };

struct RenderParameters
{
	mat4 projection;
	mat4 view;
	mat4 view_projection;
	mat4 inv_projection;
	mat4 inv_view;
	mat4 inv_view_projection;
	mat4 local_view_projection;
	mat4 inv_local_view_projection;

	mat4 unjittered_view_projection;
	mat4 unjittered_inv_view_projection;
	mat4 unjittered_prev_view_projection;

	alignas(16) vec3 camera_position;
	alignas(16) vec3 camera_front;
	alignas(16) vec3 camera_right;
	alignas(16) vec3 camera_up;

	float z_near;
	float z_far;
};

struct ResolutionParameters
{
	alignas(8) vec2 resolution;
	alignas(8) vec2 inv_resolution;
};

struct VolumetricFogParameters
{
	float slice_z_log2_scale;
};

struct FogParameters
{
	alignas(16) vec3 color;
	float falloff;
};

struct DirectionalParameters
{
	alignas(16) vec3 color;
	alignas(16) vec3 direction;
};

struct ShadowParameters
{
	alignas(16) mat4 transforms[NumShadowCascades];
	float cascade_log_bias;
};

struct ClustererParametersBindless
{
	alignas(16) mat4 transform;
	alignas(16) vec4 clip_scale;
	alignas(16) vec3 camera_base;
	alignas(16) vec3 camera_front;

	alignas(8) vec2 xy_scale;
	alignas(8) ivec2 resolution_xy;
	alignas(8) vec2 inv_resolution_xy;

	uint32_t num_lights;
	uint32_t num_lights_32;
	uint32_t num_decals;
	uint32_t num_decals_32;
	uint32_t decals_texture_offset;
	uint32_t z_max_index;
	float z_scale;
};

struct DiffuseVolumeParameters
{
	mat_affine world_to_texture;
	vec4 world_lo;
	vec4 world_hi;
	float lo_tex_coord_x;
	float hi_tex_coord_x;
	float guard_band_factor;
	float guard_band_sharpen;
};

#define CLUSTERER_MAX_VOLUMES 128
struct ClustererParametersVolumetric
{
	alignas(16) muglm::vec3 sun_direction;
	uint32_t bindless_index_offset;
	alignas(16) muglm::vec3 sun_color;
	uint32_t num_volumes;
	alignas(16) DiffuseVolumeParameters volumes[CLUSTERER_MAX_VOLUMES];
};

struct FogRegionParameters
{
	mat_affine world_to_texture;
	vec4 world_lo;
	vec4 world_hi;
};

#define CLUSTERER_MAX_FOG_REGIONS 128
struct ClustererParametersFogRegions
{
	uint32_t bindless_index_offset;
	uint32_t num_regions;
	alignas(16) FogRegionParameters regions[CLUSTERER_MAX_FOG_REGIONS];
};

#define CLUSTERER_MAX_LIGHTS_BINDLESS 4096
#define CLUSTERER_MAX_DECALS_BINDLESS 4096
#define CLUSTERER_MAX_LIGHTS_GLOBAL 32

struct BindlessDecalTransform
{
	mat_affine world_to_texture;
};

struct ClustererBindlessTransforms
{
	PositionalFragmentInfo lights[CLUSTERER_MAX_LIGHTS_BINDLESS];
	mat4 shadow[CLUSTERER_MAX_LIGHTS_BINDLESS];
	mat_affine model[CLUSTERER_MAX_LIGHTS_BINDLESS];
	uint32_t type_mask[CLUSTERER_MAX_LIGHTS_BINDLESS / 32];
	BindlessDecalTransform decals[CLUSTERER_MAX_DECALS_BINDLESS];
};

struct ClustererGlobalTransforms
{
	alignas(16) PositionalFragmentInfo lights[CLUSTERER_MAX_LIGHTS_GLOBAL];
	alignas(16) mat4 shadow[CLUSTERER_MAX_LIGHTS_GLOBAL];
	alignas(16) uint32_t type_mask[CLUSTERER_MAX_LIGHTS_GLOBAL / 32];
	uint32_t descriptor_offset;
	uint32_t num_lights;
};
static_assert(sizeof(ClustererGlobalTransforms) <= Vulkan::VULKAN_MAX_UBO_SIZE, "Global transforms is too large.");

struct CombinedRenderParameters
{
	alignas(16) FogParameters fog;
	alignas(16) ShadowParameters shadow;
	alignas(16) VolumetricFogParameters volumetric_fog;
	alignas(16) DirectionalParameters directional;
	alignas(16) ResolutionParameters resolution;
};
static_assert(sizeof(CombinedRenderParameters) <= Vulkan::VULKAN_MAX_UBO_SIZE, "CombinedRenderParameters cannot fit in min-spec.");

struct LightingParameters
{
	FogParameters fog = {};
	DirectionalParameters directional;
	ShadowParameters shadow;

	Vulkan::ImageView *shadows = nullptr;
	Vulkan::ImageView *ambient_occlusion = nullptr;
	const LightClusterer *cluster = nullptr;
	const VolumetricFog *volumetric_fog = nullptr;
	const VolumetricDiffuseLightManager *volumetric_diffuse = nullptr;
};
}
