// GPU feature detection and capability reporting.
// Queries adapter for standard and advanced feature support.
//
// Dreamwell treats ray tracing and mesh shaders as first-class features.
// The wgpu fork's "EXPERIMENTAL_" prefix is an upstream naming convention;
// Dreamwell's RT pipeline is production-grade and not gated behind any
// experimental flag from the engine's perspective.

/// GPU capabilities detected at startup.
#[derive(Debug, Clone)]
pub struct GpuCapabilities {
    pub backend: wgpu::Backend,
    pub adapter_name: String,
    pub driver_info: String,

    // Standard features
    pub depth_clip_control: bool,
    pub timestamp_query: bool,
    pub indirect_first_instance: bool,
    pub immediates: bool,
    pub max_immediate_size: u32,

    // Advanced GPU features (ray tracing, mesh shaders)
    pub ray_tracing: bool,
    pub mesh_shader: bool,
    pub mesh_shader_multiview: bool,

    // Ray tracing limits
    pub max_blas_primitive_count: u32,
    pub max_blas_geometry_count: u32,
    pub max_tlas_instance_count: u32,

    // Mesh shader limits
    pub max_mesh_output_layers: u32,
    pub max_mesh_multiview_view_count: u32,
}

impl GpuCapabilities {
    /// Query capabilities from an adapter.
    pub fn from_adapter(adapter: &wgpu::Adapter) -> Self {
        let info = adapter.get_info();
        let features = adapter.features();
        let limits = adapter.limits();

        Self {
            backend: info.backend,
            adapter_name: info.name.clone(),
            driver_info: info.driver_info.clone(),

            depth_clip_control: features.contains(wgpu::Features::DEPTH_CLIP_CONTROL),
            timestamp_query: features.contains(wgpu::Features::TIMESTAMP_QUERY),
            indirect_first_instance: features.contains(wgpu::Features::INDIRECT_FIRST_INSTANCE),
            immediates: features.contains(wgpu::Features::IMMEDIATES),
            max_immediate_size: limits.max_immediate_size,

            ray_tracing: features.contains(wgpu::Features::EXPERIMENTAL_RAY_QUERY),
            mesh_shader: features.contains(wgpu::Features::EXPERIMENTAL_MESH_SHADER),
            mesh_shader_multiview: features.contains(wgpu::Features::EXPERIMENTAL_MESH_SHADER_MULTIVIEW),

            max_blas_primitive_count: limits.max_blas_primitive_count,
            max_blas_geometry_count: limits.max_blas_geometry_count,
            max_tlas_instance_count: limits.max_tlas_instance_count,

            max_mesh_output_layers: limits.max_mesh_output_layers,
            max_mesh_multiview_view_count: limits.max_mesh_multiview_view_count,
        }
    }

    /// Request the best standard (non-experimental) feature set the adapter supports.
    pub fn desired_features(adapter: &wgpu::Adapter) -> wgpu::Features {
        let available = adapter.features();
        let mut desired = wgpu::Features::empty();

        let standard = [
            wgpu::Features::DEPTH_CLIP_CONTROL,
            wgpu::Features::TIMESTAMP_QUERY,
            wgpu::Features::INDIRECT_FIRST_INSTANCE,
            wgpu::Features::IMMEDIATES,
        ];
        for f in standard {
            if available.contains(f) {
                desired |= f;
            }
        }

        desired
    }

    /// Request all advanced features the adapter supports (ray tracing, mesh shaders).
    /// These must be combined with `desired_features()` into `required_features`,
    /// and `experimental_features` must be set to `ExperimentalFeatures::enabled()`.
    pub fn desired_advanced_features(adapter: &wgpu::Adapter) -> wgpu::Features {
        let available = adapter.features();
        let mut desired = wgpu::Features::empty();

        let advanced = [
            wgpu::Features::EXPERIMENTAL_RAY_QUERY,
            wgpu::Features::EXPERIMENTAL_MESH_SHADER,
            wgpu::Features::EXPERIMENTAL_MESH_SHADER_MULTIVIEW,
        ];
        for f in advanced {
            if available.contains(f) {
                desired |= f;
            }
        }

        desired
    }

    /// Backward-compatible alias for `desired_advanced_features`.
    pub fn desired_experimental_features(adapter: &wgpu::Adapter) -> wgpu::Features {
        Self::desired_advanced_features(adapter)
    }

    /// Build a complete `DeviceDescriptor` with all features the adapter supports.
    /// This is the canonical way to create a Dreamwell GPU device.
    ///
    /// Returns `(required_features, required_limits, experimental_features)` tuple
    /// suitable for passing directly to `adapter.request_device()`.
    pub fn device_descriptor_parts(
        adapter: &wgpu::Adapter,
    ) -> (wgpu::Features, wgpu::Limits, wgpu::ExperimentalFeatures) {
        let standard = Self::desired_features(adapter);
        let advanced = Self::desired_advanced_features(adapter);
        let all_features = standard | advanced;

        let has_advanced = !advanced.is_empty();

        let limits = if has_advanced && adapter.features().contains(wgpu::Features::EXPERIMENTAL_RAY_QUERY) {
            wgpu::Limits::default().using_minimum_supported_acceleration_structure_values()
        } else {
            wgpu::Limits::default()
        };

        let experimental = if has_advanced {
            // SAFETY: We only request features the adapter reports as available.
            // This gate is required by wgpu for any Features with EXPERIMENTAL_ prefix.
            unsafe { wgpu::ExperimentalFeatures::enabled() }
        } else {
            wgpu::ExperimentalFeatures::default()
        };

        (all_features, limits, experimental)
    }

    /// Summary string for display in status bar / inspector.
    pub fn summary(&self) -> String {
        let mut parts = Vec::with_capacity(4);
        parts.push(format!("{:?} — {}", self.backend, self.adapter_name));
        if self.ray_tracing {
            parts.push("RT".into());
        }
        if self.mesh_shader {
            parts.push("Mesh".into());
        }
        parts.join(" | ")
    }
}

#[cfg(test)]
mod tests {
    #[test]
    fn summary_format() {
        let cap = super::GpuCapabilities {
            backend: wgpu::Backend::Vulkan,
            adapter_name: "Test GPU".into(),
            driver_info: String::new(),
            depth_clip_control: false,
            timestamp_query: false,
            indirect_first_instance: false,
            immediates: false,
            max_immediate_size: 0,
            ray_tracing: true,
            mesh_shader: false,
            mesh_shader_multiview: false,
            max_blas_primitive_count: 0,
            max_blas_geometry_count: 0,
            max_tlas_instance_count: 0,
            max_mesh_output_layers: 0,
            max_mesh_multiview_view_count: 0,
        };
        let s = cap.summary();
        assert!(s.contains("Test GPU"));
        assert!(s.contains("RT"));
    }
}
