use super::super::super::material_gpu;
use super::SkinnedMeshPass;

impl SkinnedMeshPass {
    pub fn apply_material_textures(
        &mut self,
        device: &wgpu::Device,
        arrays: &crate::render::wgpu::material_texture_arrays::MaterialTextureArrays,
    ) {
        if self.material_bindless_max.is_some() && arrays.is_bindless() {
            let views = arrays.bindless_view_refs();
            self.material_bind_group = Some(material_gpu::bindless_material_bind_group(
                device,
                &self.texture_bind_group_layout,
                &views,
                arrays.sampler(),
                "Skinned Mesh Material Bindless Bind Group",
            ));
            return;
        }

        self.material_array_srgb_view = Some(arrays.srgb_view().clone());
        self.material_array_linear_view = Some(arrays.linear_view().clone());
        self.material_array_sampler = Some(arrays.sampler().clone());
        self.rebuild_material_bind_group(device);
    }

    pub(in super::super) fn rebuild_material_bind_group(&mut self, device: &wgpu::Device) {
        let (Some(srgb), Some(linear), Some(sampler)) = (
            self.material_array_srgb_view.as_ref(),
            self.material_array_linear_view.as_ref(),
            self.material_array_sampler.as_ref(),
        ) else {
            self.material_bind_group = None;
            return;
        };

        self.material_bind_group = Some(material_gpu::array_material_bind_group(
            device,
            &self.texture_bind_group_layout,
            srgb,
            linear,
            sampler,
            "Skinned Mesh Material Texture Bind Group",
        ));
    }

    pub(in super::super) fn rebuild_instance_bind_group(&mut self, device: &wgpu::Device) {
        let world_state = self.world_states[self.current_world_id as usize]
            .as_mut()
            .unwrap();
        if let Some(gpu) = world_state.gpu_buffers.as_mut() {
            gpu.instance_bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
                label: Some("Skinned Mesh Instance Bind Group (Per-World, Rebuilt)"),
                layout: &self.instance_bind_group_layout,
                entries: &[
                    wgpu::BindGroupEntry {
                        binding: 0,
                        resource: gpu.materials_buffer.as_entire_binding(),
                    },
                    wgpu::BindGroupEntry {
                        binding: 1,
                        resource: gpu.object_buffer.as_entire_binding(),
                    },
                    wgpu::BindGroupEntry {
                        binding: 2,
                        resource: gpu.lights_buffer.as_entire_binding(),
                    },
                    wgpu::BindGroupEntry {
                        binding: 3,
                        resource: self.joint_matrices_buffer.as_entire_binding(),
                    },
                    wgpu::BindGroupEntry {
                        binding: 4,
                        resource: gpu.custom_data_buffer.as_entire_binding(),
                    },
                    wgpu::BindGroupEntry {
                        binding: 5,
                        resource: self.morph_displacement_buffer.as_entire_binding(),
                    },
                    wgpu::BindGroupEntry {
                        binding: 6,
                        resource: gpu.visible_indices_buffer.as_entire_binding(),
                    },
                    wgpu::BindGroupEntry {
                        binding: 7,
                        resource: gpu.light_grid_buffer.as_entire_binding(),
                    },
                    wgpu::BindGroupEntry {
                        binding: 8,
                        resource: gpu.light_indices_buffer.as_entire_binding(),
                    },
                    wgpu::BindGroupEntry {
                        binding: 9,
                        resource: gpu.cluster_uniforms_buffer.as_entire_binding(),
                    },
                    wgpu::BindGroupEntry {
                        binding: 10,
                        resource: gpu.area_lights_buffer.as_entire_binding(),
                    },
                    wgpu::BindGroupEntry {
                        binding: 11,
                        resource: gpu.area_uniforms_buffer.as_entire_binding(),
                    },
                ],
            });
        }
    }

    pub(in super::super) fn rebuild_cull_bind_group(&mut self, device: &wgpu::Device) {
        let world_state = self.world_states[self.current_world_id as usize]
            .as_mut()
            .unwrap();
        if let Some(gpu) = world_state.gpu_buffers.as_mut() {
            gpu.cull_bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
                label: Some("Skinned Mesh Cull Bind Group (Per-World, Rebuilt)"),
                layout: &self.cull_bind_group_layout,
                entries: &[
                    wgpu::BindGroupEntry {
                        binding: 0,
                        resource: gpu.cull_objects_buffer.as_entire_binding(),
                    },
                    wgpu::BindGroupEntry {
                        binding: 1,
                        resource: gpu.indirect_commands_buffer.as_entire_binding(),
                    },
                    wgpu::BindGroupEntry {
                        binding: 2,
                        resource: gpu.visible_indices_buffer.as_entire_binding(),
                    },
                    wgpu::BindGroupEntry {
                        binding: 3,
                        resource: gpu.cull_uniforms_buffer.as_entire_binding(),
                    },
                ],
            });
        }
    }

    pub(in super::super) fn rebuild_skinning_compute_bind_group(&mut self, device: &wgpu::Device) {
        self.skinning_compute_bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
            label: Some("Skinning Compute Bind Group"),
            layout: &self._skinning_compute_bind_group_layout,
            entries: &[
                wgpu::BindGroupEntry {
                    binding: 0,
                    resource: self.bone_transforms_buffer.as_entire_binding(),
                },
                wgpu::BindGroupEntry {
                    binding: 1,
                    resource: self.inverse_bind_matrices_buffer.as_entire_binding(),
                },
                wgpu::BindGroupEntry {
                    binding: 2,
                    resource: self.skin_data_buffer.as_entire_binding(),
                },
                wgpu::BindGroupEntry {
                    binding: 3,
                    resource: self.joint_matrices_buffer.as_entire_binding(),
                },
            ],
        });
    }

    pub fn update_shadow_bind_group(
        &mut self,
        device: &wgpu::Device,
        shadow_view: &wgpu::TextureView,
        spotlight_shadow_view: &wgpu::TextureView,
    ) {
        let brdf_view = self
            .brdf_lut_view
            .as_ref()
            .unwrap_or(&self.dummy_white_view);
        let irradiance_view = self
            .irradiance_map_view
            .as_ref()
            .unwrap_or(&self.dummy_cube_view);
        let prefiltered_view = self
            .prefiltered_env_view
            .as_ref()
            .unwrap_or(&self.dummy_cube_view);

        self.shadow_bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
            label: Some("Skinned Mesh Shadow Bind Group"),
            layout: &self.shadow_bind_group_layout,
            entries: &[
                wgpu::BindGroupEntry {
                    binding: 0,
                    resource: wgpu::BindingResource::TextureView(shadow_view),
                },
                wgpu::BindGroupEntry {
                    binding: 1,
                    resource: wgpu::BindingResource::Sampler(&self.shadow_sampler),
                },
                wgpu::BindGroupEntry {
                    binding: 2,
                    resource: wgpu::BindingResource::TextureView(spotlight_shadow_view),
                },
                wgpu::BindGroupEntry {
                    binding: 3,
                    resource: wgpu::BindingResource::Sampler(&self.shadow_sampler),
                },
                wgpu::BindGroupEntry {
                    binding: 4,
                    resource: self.spotlight_shadow_buffer.as_entire_binding(),
                },
                wgpu::BindGroupEntry {
                    binding: 5,
                    resource: wgpu::BindingResource::TextureView(brdf_view),
                },
                wgpu::BindGroupEntry {
                    binding: 6,
                    resource: wgpu::BindingResource::Sampler(&self.ibl_sampler),
                },
                wgpu::BindGroupEntry {
                    binding: 7,
                    resource: wgpu::BindingResource::TextureView(irradiance_view),
                },
                wgpu::BindGroupEntry {
                    binding: 8,
                    resource: wgpu::BindingResource::Sampler(&self.ibl_sampler),
                },
                wgpu::BindGroupEntry {
                    binding: 9,
                    resource: wgpu::BindingResource::TextureView(prefiltered_view),
                },
                wgpu::BindGroupEntry {
                    binding: 10,
                    resource: wgpu::BindingResource::Sampler(&self.ibl_sampler),
                },
                wgpu::BindGroupEntry {
                    binding: 11,
                    resource: wgpu::BindingResource::TextureView(&self.point_shadow_cubemap_view),
                },
                wgpu::BindGroupEntry {
                    binding: 12,
                    resource: wgpu::BindingResource::Sampler(&self.point_shadow_sampler),
                },
                wgpu::BindGroupEntry {
                    binding: 13,
                    resource: self.point_shadow_buffer.as_entire_binding(),
                },
            ],
        });
    }
}
