//! Texture store -- manages GPU textures via `TextureHandle`.

use crate::handles::TextureHandle;
use crate::texture::{GpuTexture, TextureData};

/// GPU texture store. Insert returns a handle; lookup by handle.
pub struct TextureStore {
    textures: Vec<Option<GpuTexture>>,
    /// Generation counter per slot for dangling reference detection.
    generations: Vec<u32>,
    next_id: u32,
}

impl TextureStore {
    /// Create an empty texture store.
    pub fn new() -> Self {
        Self {
            textures: Vec::new(),
            generations: Vec::new(),
            next_id: 0,
        }
    }

    /// Upload texture data to GPU and return a handle.
    pub fn insert(
        &mut self,
        device: &wgpu::Device,
        queue: &wgpu::Queue,
        data: &TextureData,
        label: &str,
    ) -> TextureHandle {
        let gpu_tex = crate::texture::upload_texture(device, queue, data, label);
        let idx = self.next_id;
        self.next_id += 1;

        if (idx as usize) < self.textures.len() {
            self.textures[idx as usize] = Some(gpu_tex);
        } else {
            // Extend to fit.
            while self.textures.len() < idx as usize {
                self.textures.push(None);
                self.generations.push(0);
            }
            self.textures.push(Some(gpu_tex));
            self.generations.push(0);
        }
        let gen = self.generations[idx as usize];
        TextureHandle(idx, gen)
    }

    /// Get a reference to a texture by handle. Returns None if generation mismatches.
    pub fn get(&self, handle: TextureHandle) -> Option<&GpuTexture> {
        let idx = handle.0 as usize;
        if self.generations.get(idx).copied() != Some(handle.1) {
            return None;
        }
        self.textures.get(idx).and_then(|slot| slot.as_ref())
    }

    /// Remove a texture. The GPU resource is dropped. Increments generation.
    pub fn remove(&mut self, handle: TextureHandle) -> bool {
        let idx = handle.0 as usize;
        if idx < self.textures.len() && self.textures[idx].is_some() {
            self.textures[idx] = None;
            self.generations[idx] += 1;
            true
        } else {
            false
        }
    }

    /// Check if a handle is valid (index and generation match).
    pub fn contains(&self, handle: TextureHandle) -> bool {
        let idx = handle.0 as usize;
        self.generations.get(idx).copied() == Some(handle.1) && self.textures.get(idx).is_some_and(|s| s.is_some())
    }

    /// Number of loaded textures.
    pub fn len(&self) -> usize {
        self.textures.iter().filter(|s| s.is_some()).count()
    }

    /// Whether the store is empty.
    pub fn is_empty(&self) -> bool {
        self.len() == 0
    }
}

impl Default for TextureStore {
    fn default() -> Self {
        Self::new()
    }
}
