use super::*;

#[derive(ShaderType, Reflect, Debug, Clone, Copy, PartialEq)]
pub struct TerrainLayerUniform {
    pub base_colors: [Vec4; TERRAIN_MATERIAL_LAYER_LIMIT],
    /// Metallic, perceptual roughness, normal-map enabled, layer enabled.
    pub properties: [Vec4; TERRAIN_MATERIAL_LAYER_LIMIT],
    pub uv_scales: Vec4,
    pub base_texture_enabled: Vec4,
}

impl Default for TerrainLayerUniform {
    fn default() -> Self {
        Self {
            base_colors: [Vec4::ONE; TERRAIN_MATERIAL_LAYER_LIMIT],
            properties: [Vec4::new(0.0, 0.9, 0.0, 0.0); TERRAIN_MATERIAL_LAYER_LIMIT],
            uv_scales: Vec4::splat(8.0),
            base_texture_enabled: Vec4::ZERO,
        }
    }
}

#[derive(Asset, AsBindGroup, Reflect, Debug, Clone, Default)]
pub struct TerrainLayerExtension {
    #[uniform(100)]
    pub uniform: TerrainLayerUniform,
    #[texture(101)]
    #[sampler(102)]
    pub base_color0: Option<Handle<Image>>,
    #[texture(103)]
    #[sampler(104)]
    pub normal0: Option<Handle<Image>>,
    #[texture(105)]
    #[sampler(106)]
    pub base_color1: Option<Handle<Image>>,
    #[texture(107)]
    #[sampler(108)]
    pub normal1: Option<Handle<Image>>,
    #[texture(109)]
    #[sampler(110)]
    pub base_color2: Option<Handle<Image>>,
    #[texture(111)]
    #[sampler(112)]
    pub normal2: Option<Handle<Image>>,
    #[texture(113)]
    #[sampler(114)]
    pub base_color3: Option<Handle<Image>>,
    #[texture(115)]
    #[sampler(116)]
    pub normal3: Option<Handle<Image>>,
}

impl MaterialExtension for TerrainLayerExtension {
    fn fragment_shader() -> ShaderRef {
        TERRAIN_LAYER_SHADER_PATH.into()
    }

    fn deferred_fragment_shader() -> ShaderRef {
        TERRAIN_LAYER_SHADER_PATH.into()
    }
}

pub type TerrainLayerMaterial = ExtendedMaterial<StandardMaterial, TerrainLayerExtension>;

#[derive(Debug, Clone)]
pub(super) struct TerrainLayerCacheEntry {
    pub(super) layers: Vec<TerrainMaterialLayer>,
    pub(super) handle: Handle<TerrainLayerMaterial>,
    pub(super) revision: u64,
}

#[derive(Resource, Default)]
pub(super) struct TerrainLayerMaterialCache(pub(super) HashMap<Entity, TerrainLayerCacheEntry>);

pub(super) type TerrainMaterialBindings<'w, 's> =
    Query<'w, 's, (Entity, &'static HydratedTerrainMaterialBinding)>;
