//! Blacksite Surface ABI v1.
//!
//! The authoring layer persists named values. This crate validates and packs them into a fixed GPU
//! contract and supplies the same surface evaluator source to raster and Solari integrations.

use std::collections::{HashMap, HashSet};
use std::fs;
use std::hash::{DefaultHasher, Hash, Hasher};
use std::path::Path;

use bevy::material::OpaqueRendererMethod;
use bevy::mesh::MeshVertexBufferLayoutRef;
use bevy::pbr::{
    ExtendedMaterial, MaterialExtension, MaterialExtensionKey, MaterialExtensionPipeline,
};
use bevy::prelude::*;
use bevy::render::extract_resource::ExtractResource;
use bevy::render::render_resource::{
    AsBindGroup, RenderPipelineDescriptor, ShaderType, SpecializedMeshPipelineError,
};
use bevy::shader::{Shader, ShaderDefVal, ShaderRef};
use shared::{
    asset_server_path, HydratedMaterialSelection, HydratedMaterialSlotBinding,
    HydratedTerrainMaterialBinding, MaterialAsset, MaterialDesc, MaterialInputSet,
    MaterialInstanceAsset, MaterialParameterValue, MaterialPropertyBlock, MaterialPropertyBlocks,
    MaterialRef, RuntimeContentCatalog, ShaderPropertyType, ShaderSchemaAsset,
    TerrainMaterialLayer, MATERIAL_INSTANCE_SCHEMA_VERSION, TERRAIN_MATERIAL_LAYER_LIMIT,
};

mod live_documents;
pub use live_documents::*;

pub const SURFACE_ABI_VERSION: u32 = 1;
pub const SURFACE_PARAMETER_LANES: usize = 16;
pub const SURFACE_TEXTURE_SLOTS: usize = 8;
pub const DEFAULT_SURFACE_SHADER_PATH: &str = "shaders/blacksite_surface.wgsl";
pub const TERRAIN_LAYER_SHADER_PATH: &str = "shaders/terrain_layers.wgsl";
pub const DEFAULT_GRID_SHADER_ID: u32 = 0xffff_ff01;
pub const DEFAULT_GRID_LABEL: &str = "Default Grid — Engine Built-in";
const DEFAULT_GRID_SHADER_UUID: uuid::Uuid =
    uuid::Uuid::from_u128(0x3420_f5ad_551c_4bd6_a902_5f8a_5ce5_9f01);
const DEFAULT_GRID_EVALUATOR: &str = r#"
fn grid_axis(position: vec2<f32>) -> f32 {
    let cell = floor(position / 0.5);
    let checker = (cell.x + cell.y) - 2.0 * floor((cell.x + cell.y) * 0.5);
    let major_cell = floor(position / 4.0);
    let major = (major_cell.x + major_cell.y) - 2.0 * floor((major_cell.x + major_cell.y) * 0.5);
    return checker * 0.72 + major * 0.28;
}

fn evaluate(
    input: SurfaceInput,
    params: SurfaceParams,
    samples: SurfaceSamples,
) -> Surface {
    var surface = surface_default();
    let weights = abs(normalize(input.world_normal));
    let normalized_weights = weights / max(weights.x + weights.y + weights.z, 0.0001);
    let grid = grid_axis(input.world_position.yz) * normalized_weights.x
        + grid_axis(input.world_position.xz) * normalized_weights.y
        + grid_axis(input.world_position.xy) * normalized_weights.z;
    let shade = mix(0.32, 0.56, grid);
    surface.base_color = vec4<f32>(vec3<f32>(shade), 1.0);
    surface.perceptual_roughness = 0.86;
    surface.metallic = 0.0;
    surface.emissive = vec3<f32>(shade * 0.035);
    surface.reflectance = 0.35;
    surface.occlusion = 1.0;
    return surface;
}
"#;

#[path = "surface/cache_plugin.rs"]
mod cache_plugin;
#[path = "surface/packing.rs"]
mod packing;
#[path = "surface/property_blocks.rs"]
mod property_blocks;
#[path = "surface/resolver.rs"]
mod resolver;
#[path = "surface/surface_abi.rs"]
mod surface_abi;
#[path = "surface/terrain.rs"]
mod terrain;
#[path = "surface/terrain_types.rs"]
mod terrain_types;
#[path = "surface/validation.rs"]
mod validation;

pub use cache_plugin::{
    DefaultGridMaterialHandles, SurfaceDiagnostics, SurfaceEvaluatorRecord,
    SurfaceEvaluatorRegistry, SurfaceMaterialCache, SurfaceMaterialPlugin,
};
pub use property_blocks::validate_property_block_for_promotion;
pub use surface_abi::{SurfaceExtension, SurfaceExtensionKey, SurfaceMaterial, SurfaceUniform};
pub use terrain_types::{TerrainLayerExtension, TerrainLayerMaterial, TerrainLayerUniform};
pub use validation::{compose_surface_shader, validate_surface_evaluator};

use cache_plugin::*;
use packing::*;
use property_blocks::*;
use resolver::*;
use terrain::*;
use terrain_types::*;

#[cfg(test)]
#[path = "surface/tests.rs"]
mod tests;
