// Copyright (C) 2025 Christian Mauduit <ufoot@ufoot.org>

//! Trait for reading 3D cell data from various sources.

use super::cell_type::*;
use crate::errors::*;

/// Trait for reading 3D volume data from various sources.
///
/// This trait provides an abstraction for reading cell types (free/wall)
/// from different data sources for 3D volumes.
///
/// # Example
///
/// ```
/// use shortestpath::mesh_source::{Source3D, CellType};
///
/// struct SimpleVolume {
///     width: usize,
///     height: usize,
///     depth: usize,
/// }
///
/// impl Source3D for SimpleVolume {
///     fn get(&self, x: usize, y: usize, z: usize) -> Result<CellType, shortestpath::Error> {
///         if x >= self.width || y >= self.height || z >= self.depth {
///             return Err(shortestpath::Error::invalid_xyz(x, y, z));
///         }
///         // Alternating pattern
///         Ok(if (x + y + z) % 2 == 0 { CellType::FLOOR } else { CellType::WALL })
///     }
///
///     fn width(&self) -> usize { self.width }
///     fn height(&self) -> usize { self.height }
///     fn depth(&self) -> usize { self.depth }
/// }
///
/// let volume = SimpleVolume { width: 4, height: 3, depth: 2 };
/// assert_eq!(volume.get(0, 0, 0).unwrap(), CellType::FLOOR);
/// assert_eq!(volume.get(1, 0, 0).unwrap(), CellType::WALL);
/// ```
pub trait Source3D {
    /// Gets the cell type at the given coordinates.
    ///
    /// # Arguments
    ///
    /// * `x` - The column coordinate (0-based)
    /// * `y` - The row coordinate (0-based)
    /// * `z` - The depth/layer coordinate (0-based)
    ///
    /// # Returns
    ///
    /// The cell type at the given position, or an error if out of bounds.
    fn get(&self, x: usize, y: usize, z: usize) -> Result<CellType>;

    /// Returns the width (number of columns) of the 3D volume.
    fn width(&self) -> usize;

    /// Returns the height (number of rows) of the 3D volume.
    fn height(&self) -> usize;

    /// Returns the depth (number of layers) of the 3D volume.
    fn depth(&self) -> usize;

    /// Returns the dimensions as a tuple (width, height, depth).
    fn dimensions(&self) -> (usize, usize, usize) {
        (self.width(), self.height(), self.depth())
    }
}
