//! Shared game/protocol types that are independent of rendering, audio, and editor code.
//!
//! This crate is intentionally small in M2. It provides stable tick/input/event
//! shapes that local standalone play can use now and future networking can build on.

use bevy::prelude::*;
use serde::{Deserialize, Serialize};

/// Fixed simulation tick index.
#[derive(
    Debug,
    Clone,
    Copy,
    Default,
    PartialEq,
    Eq,
    PartialOrd,
    Ord,
    Hash,
    Reflect,
    Serialize,
    Deserialize,
)]
#[reflect(Default, Debug, Hash, Serialize, Deserialize)]
pub struct SimTick(pub u64);

impl SimTick {
    pub const ZERO: Self = Self(0);

    pub const fn next(self) -> Self {
        Self(self.0 + 1)
    }
}

/// The target fixed simulation rate for gameplay systems.
pub const SIM_TICK_RATE_HZ: u32 = 64;

/// The target fixed simulation delta in seconds.
pub const SIM_DELTA_SECS: f32 = 1.0 / SIM_TICK_RATE_HZ as f32;

/// Stable identifier for a player in protocol messages.
///
/// M2 only has a local player, but keeping the ID explicit avoids baking local
/// single-player assumptions into the simulation layer.
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Hash, Reflect, Serialize, Deserialize)]
#[reflect(Default, Debug, Hash, Serialize, Deserialize)]
pub struct PlayerId(pub u64);

/// Player input expressed as gameplay intent, not device state.
#[derive(Component, Debug, Clone, Copy, PartialEq, Reflect, Serialize, Deserialize)]
#[reflect(Component, Default, Debug, PartialEq, Serialize, Deserialize)]
pub struct PlayerInputIntent {
    /// Local-space movement intent: X is right, Y is forward.
    pub movement: Vec2,
    /// Accumulated look delta in device pixels since the sim/client last consumed it.
    pub look_delta: Vec2,
    /// Edge-triggered jump command, consumed by the simulation.
    pub jump_pressed: bool,
    /// Level-triggered sprint modifier.
    pub sprint_held: bool,
    /// Level-triggered crouch modifier.
    pub crouch_held: bool,
}

impl Default for PlayerInputIntent {
    fn default() -> Self {
        Self {
            movement: Vec2::ZERO,
            look_delta: Vec2::ZERO,
            jump_pressed: false,
            sprint_held: false,
            crouch_held: false,
        }
    }
}

/// Command envelope suitable for local replay or future transport.
#[derive(Debug, Clone, Copy, PartialEq, Reflect, Serialize, Deserialize)]
#[reflect(Debug, PartialEq, Serialize, Deserialize)]
pub struct PlayerCommand {
    pub tick: SimTick,
    pub player: PlayerId,
    pub intent: PlayerInputIntent,
}

/// Minimal client-to-server message vocabulary reserved for M3 networking.
#[derive(Debug, Clone, PartialEq, Reflect, Serialize, Deserialize)]
#[reflect(Debug, PartialEq, Serialize, Deserialize)]
pub enum ClientMessage {
    Input(PlayerCommand),
    Join { requested_player: PlayerId },
}

/// Minimal server-to-client event vocabulary reserved for M3 networking.
#[derive(Debug, Clone, PartialEq, Reflect, Serialize, Deserialize)]
#[reflect(Debug, PartialEq, Serialize, Deserialize)]
pub enum ServerEvent {
    Accepted { player: PlayerId, tick: SimTick },
    PlayerSpawned { player: PlayerId },
    PlayerDespawned { player: PlayerId },
}

/// Registers protocol types used in reflected scenes/resources and future messages.
pub struct ProtocolPlugin;

impl Plugin for ProtocolPlugin {
    fn build(&self, app: &mut App) {
        app.register_type::<SimTick>()
            .register_type::<PlayerId>()
            .register_type::<PlayerInputIntent>()
            .register_type::<PlayerCommand>()
            .register_type::<ClientMessage>()
            .register_type::<ServerEvent>();
    }
}
