#version 440

#ifdef GPU_MODE
#extension GL_EXT_nonuniform_qualifier : require
#endif

#include "structures.glsl"

layout(location = 0) in vec4 i_view_position;
layout(location = 1) in vec3 i_normal;
layout(location = 2) in vec3 i_tangent;
layout(location = 3) in vec2 i_coords0;
layout(location = 4) in vec2 i_coords1;
layout(location = 5) in vec4 i_color;
layout(location = 6) flat in uint i_material;

layout(location = 0) out vec4 o_color;

layout(set = 0, binding = 0) uniform sampler primary_sampler;
layout(set = 0, binding = 1) uniform sampler nearest_sampler;
layout(set = 0, binding = 2) uniform samplerShadow shadow_sampler;
layout(set = 2, binding = 0) restrict readonly buffer DirectionalLightBuffer {
    DirectionalLightBufferHeader directional_light_header;
    DirectionalLight directional_lights[];
};
layout(set = 2, binding = 1) uniform texture2DArray shadow;
layout(set = 3, binding = 0) uniform UniformBuffer {
    UniformData uniforms;
};
#ifdef GPU_MODE
layout(set = 4, binding = 0, std430) restrict readonly buffer MaterialBuffer {
    GPUMaterialData materials[];
};
layout(set = 5, binding = 0) uniform texture2D textures[];
#endif
#ifdef CPU_MODE
layout(set = 4, binding = 0) uniform texture2D albedo_tex;
layout(set = 4, binding = 1) uniform texture2D normal_tex;
layout(set = 4, binding = 2) uniform texture2D roughness_tex;
layout(set = 4, binding = 3) uniform texture2D metallic_tex;
layout(set = 4, binding = 4) uniform texture2D reflectance_tex;
layout(set = 4, binding = 5) uniform texture2D clear_coat_tex;
layout(set = 4, binding = 6) uniform texture2D clear_coat_roughness_tex;
layout(set = 4, binding = 7) uniform texture2D emissive_tex;
layout(set = 4, binding = 8) uniform texture2D anisotropy_tex;
layout(set = 4, binding = 9) uniform texture2D ambient_occlusion_tex;
layout(set = 4, binding = 10) uniform TextureData {
    CPUMaterialData material;
};
#endif

#include "lighting/surface.glsl"

void main() {
    #ifdef GPU_MODE
    GPUMaterialData material = materials[i_material];
    #endif

    PixelData pixel = get_per_pixel_data(material);

    if (MATERIAL_FLAG(FLAGS_UNLIT)) {
        o_color = pixel.albedo;
        // o_normal = vec4(i_normal, 0.0);
    } else {
        vec3 v = -normalize(i_view_position.xyz);

        vec3 color = vec3(pixel.emissive);
        for (uint i = 0; i < directional_light_header.total_lights; ++i) {
            DirectionalLight light = directional_lights[i];

            vec3 shadow_ndc = (directional_lights[i].view_proj * uniforms.inv_view * i_view_position).xyz;
            vec2 shadow_flipped = (shadow_ndc.xy * 0.5) + 0.5;
            vec4 shadow_shadow_coords = vec4(shadow_flipped.x, 1 - shadow_flipped.y, float(i), shadow_ndc.z);

            float shadow_value;
            if (shadow_shadow_coords.x < 0 || shadow_shadow_coords.x > 1 || shadow_shadow_coords.y < 0 || shadow_shadow_coords.y > 1 || shadow_ndc.z < -1 || shadow_ndc.z > 1) {
                shadow_value = 1.0;
            } else {
                shadow_value = textureGrad(sampler2DArrayShadow(shadow, shadow_sampler), shadow_shadow_coords, vec2(0), vec2(0));
            }

            color += surface_shading(directional_lights[i], pixel, v, shadow_value * pixel.ambient_occlusion);
        }

        o_color = max(vec4(color, pixel.albedo.a), uniforms.ambient * pixel.albedo);
        // o_normal = vec4(pixel.normal, 0.0);
    }
}
