//! Header definition and operations for TACO file format
//!
//! The header contains critical metadata about the trajectory including:
//! - Format version
//! - Number of atoms and frames
//! - Compression settings
//! - Simulation parameters
//! - Atom metadata

use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
use serde::{Deserialize, Serialize};
use std::io::{Read, Write};

use crate::compression::CompressionSettings;
use crate::metadata::{AtomMetadata, SimulationMetadata};
use crate::{Error, MAGIC, Result, VERSION};

/// File format header containing metadata about the trajectory
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Header {
    /// The TACO format version
    pub version: String,

    /// Number of atoms in the trajectory
    pub num_atoms: u32,

    /// Total number of frames in the trajectory
    pub num_frames: u64,

    /// Time step between frames in picoseconds
    pub time_step: f64,

    /// Periodic boundary conditions along [x, y, z]
    pub pbc: [bool; 3],

    /// Interval between full (non-delta) frames
    pub full_frame_interval: u32,

    /// Simulation metadata (temperature, ensemble, etc.)
    pub simulation_metadata: SimulationMetadata,

    /// Atom-specific metadata (masses, names, elements, etc.)
    pub atom_metadata: AtomMetadata,

    /// Compression settings used for the trajectory
    pub compression_settings: CompressionSettings,

    /// Offset to the start of the frame index table (in bytes)
    pub frame_index_offset: u64,

    /// Flags indicating which data components are present
    /// Bit 0: Positions
    /// Bit 1: Velocities
    /// Bit 2: Forces
    /// Bit 3: Box dimensions
    /// Bits 4-31: Reserved for future use
    pub data_flags: u32,
}

impl Header {
    /// Create a new header with default values
    pub fn new(
        num_atoms: u32,
        time_step: f64,
        simulation_metadata: SimulationMetadata,
        atom_metadata: AtomMetadata,
        compression_settings: CompressionSettings,
    ) -> Self {
        Header {
            version: VERSION.to_string(),
            num_atoms,
            num_frames: 0, // Will be updated during writing
            time_step,
            pbc: [true; 3],           // Default: periodic in all directions
            full_frame_interval: 100, // Default: store a full frame every 100 frames
            simulation_metadata,
            atom_metadata,
            compression_settings,
            frame_index_offset: 0, // Will be updated during writing
            data_flags: 0x0F,      // Default: include all data types
        }
    }

    /// Write the header to a writer
    pub fn write<W: Write>(&self, writer: &mut W) -> Result<()> {
        // Write magic number to identify the file format
        writer.write_all(&MAGIC)?;

        // Serialize header to JSON for maximum compatibility and extensibility
        let header_json = serde_json::to_vec(self)?;

        // Write length of header data as u32
        writer.write_u32::<LittleEndian>(header_json.len() as u32)?;

        // Write the serialized header
        writer.write_all(&header_json)?;

        Ok(())
    }

    /// Read the header from a reader
    pub fn read<R: Read>(reader: &mut R) -> Result<Self> {
        // Check magic number
        let mut magic = [0u8; 4];
        reader.read_exact(&mut magic)?;

        if magic != MAGIC {
            return Err(Error::InvalidFormat(format!(
                "Invalid file format. Expected TACO magic number, found: {magic:?}"
            )));
        }

        // Read the length of the header data
        let header_length = reader.read_u32::<LittleEndian>()?;

        // Read the serialized header
        let mut header_data = vec![0u8; header_length as usize];
        reader.read_exact(&mut header_data)?;

        // Deserialize the header
        let header: Header = serde_json::from_slice(&header_data)
            .map_err(|e| Error::Deserialization(format!("Failed to deserialize header: {e}")))?;

        // Validate version
        if !is_compatible_version(&header.version) {
            return Err(Error::InvalidFormat(format!(
                "Incompatible format version: {}. This library supports: {}",
                header.version, VERSION
            )));
        }

        Ok(header)
    }
}

/// Check if the given version is compatible with the current version
fn is_compatible_version(version: &str) -> bool {
    // For now, only exact version matches are supported
    // In the future, this could be more sophisticated with major/minor versioning
    version == VERSION
}
