//! NV-diamond magnetometer pipeline simulator — deterministic, no hidden mocks.
//!
//! # WebAssembly compatibility
//!
//! `nvsim` is **WASM-ready by construction**: zero `std::time`, `std::fs`,
//! `std::env`, `std::process`, `std::thread`, `Mutex`, or `RwLock` in the
//! crate's source. The shot-noise PRNG seeds from a caller-supplied `u64`
//! (no OS entropy), serialisation is via `serde_json`, hashing is via
//! `sha2` — all dependencies work on `wasm32-unknown-unknown`. To ship
//! `nvsim` to a browser or Cloudflare Worker, build with
//! `cargo build -p nvsim --target wasm32-unknown-unknown --no-default-features`
//! (the `wasm32` target needs `rustup target add wasm32-unknown-unknown`
//! once on the developer machine).
//!
//! `nvsim` is a standalone leaf crate. It models a forward-only magnetic
//! sensing path — scene → source synthesis → material attenuation → NV
//! ensemble → digitiser → binary frames + SHA-256 witness — using explicit
//! physics approximations validated against published primary sources.
//!
//! It is **not** a hardware-control stack, microscope simulator, full
//! Hamiltonian solver, or claim of fT-level sensitivity. This crate does
//! not control lasers, microwave sources, ADC hardware, or real NV sensors.
//!
//! # Implementation plan
//!
//! See `docs/research/quantum-sensing/15-nvsim-implementation-plan.md` for
//! the six-pass build spec. This release ships **Pass 1 only**: crate
//! scaffold, [`scene`] types, and the [`frame::MagFrame`] binary record.
//!
//! # Pass 1 surface
//!
//! - [`scene::Scene`], [`scene::DipoleSource`], [`scene::CurrentLoop`],
//!   [`scene::FerrousObject`], [`scene::EddyCurrent`]
//! - [`frame::MagFrame`] + [`frame::MAG_FRAME_MAGIC`] (`0xC51A_6E70`)
//! - [`NvsimError`] — top-level error type for parse / serialisation failures
//!
//! Subsequent passes add `source`, `propagation`, `sensor`, `digitiser`,
//! `pipeline`, and `proof` modules.

#![warn(missing_docs)]

pub mod digitiser;
pub mod frame;
pub mod pipeline;
pub mod proof;
pub mod propagation;
pub mod scene;
pub mod sensor;
pub mod source;

#[cfg(all(feature = "wasm", target_arch = "wasm32"))]
pub mod wasm;

pub use proof::Proof;

pub use digitiser::{
    adc_dequantise, adc_quantise, DigitiserConfig, Lockin, LowPass, ADC_BITS, ADC_FULL_SCALE_T,
    ADC_LSB_T,
};
pub use frame::{MagFrame, MAG_FRAME_MAGIC, MAG_FRAME_VERSION};
pub use pipeline::{Pipeline, PipelineConfig};
pub use propagation::{
    attenuate, material_is_heavy, material_loss_db_per_m, LosSegment, Material, Propagator,
};
pub use scene::{CurrentLoop, DipoleSource, EddyCurrent, FerrousObject, Scene};
pub use sensor::{nv_axes, NvReading, NvSensor, NvSensorConfig};
pub use source::{
    current_loop_field, dipole_field, ferrous_field, scene_field_at, scene_field_at_sensors,
    R_MIN_M,
};

/// Top-level simulator error type.
#[derive(Debug, thiserror::Error)]
pub enum NvsimError {
    /// JSON serialisation / parsing failed for a scene or frame.
    #[error("serde error: {0}")]
    Serde(#[from] serde_json::Error),

    /// Magic-number mismatch on frame parse.
    #[error("magic mismatch: got 0x{got:08X}, expected 0x{expected:08X}")]
    MagicMismatch {
        /// Magic value received.
        got: u32,
        /// Magic value expected.
        expected: u32,
    },

    /// Frame buffer length disagrees with the fixed v1 layout.
    #[error("frame length mismatch: got {got} bytes, expected {expected}")]
    FrameLengthMismatch {
        /// Bytes received.
        got: usize,
        /// Bytes expected for this version.
        expected: usize,
    },

    /// Frame version is not supported by this build.
    #[error("unsupported frame version: got {got}, this build supports {supported}")]
    UnsupportedVersion {
        /// Version received.
        got: u16,
        /// Highest version this build understands.
        supported: u16,
    },

    /// A configuration value is out of the supported range.
    #[error("invalid config: {0}")]
    InvalidConfig(String),
}

/// Permeability of free space (T·m/A). Jackson 3e §5.6.
pub const MU_0: f64 = 4.0 * std::f64::consts::PI * 1.0e-7;

/// NV electronic gyromagnetic ratio (Hz/T). Doherty 2013 §3.
pub const GAMMA_E: f64 = 28.0e9;

/// NV zero-field-splitting transition (Hz). Doherty 2013 §3.
pub const D_GS: f64 = 2.87e9;
