//! **san-debug** — Plugin Bevy pour le mode play out-of-process de SanBevyEditor.
//!
//! Ajoutez ce plugin à votre jeu pour permettre à l'éditeur de le contrôler :
//!
//! ```no_run
//! # use bevy::prelude::*;
//! # use san_debug::SanDebugPlugin;
//! App::new()
//!     .add_plugins(DefaultPlugins)
//!     .add_plugins(SanDebugPlugin::default()) // port 9090, écoute sur 0.0.0.0
//!     .run();
//! ```
//!
//! L'éditeur se connecte via WebSocket sur `ws://127.0.0.1:9090` et envoie des
//! commandes JSON (Play, Pause, Stop, GetEntities). Le jeu répond avec son état
//! et la liste de ses entités.
//!
//! Le jeu écoute sur `0.0.0.0` → accessible depuis l'éditeur Android via Wi-Fi.

pub mod entities;
pub mod handler;
pub mod listener;
pub mod live;
pub mod screenshot;

use bevy::prelude::*;
pub use san_motor::debug_protocol::{DEBUG_ADDR, DEBUG_PORT};

/// Ressource contenant les canaux de communication entre le thread TCP et Bevy.
///
/// `rx` est protégé par `Mutex` pour satisfaire `Sync` (requis par `Resource`).
#[derive(Resource)]
pub struct DebugChannel {
    /// Commandes reçues de l'éditeur (à consommer par les systèmes Bevy).
    pub rx: std::sync::Mutex<std::sync::mpsc::Receiver<san_motor::debug_protocol::EditorToGame>>,
    /// Réponses à envoyer à l'éditeur (le thread TCP les lit et les écrit sur le socket).
    pub tx: std::sync::mpsc::Sender<san_motor::debug_protocol::GameToEditor>,
    /// Thread TCP maintenu en vie.
    _thread: std::thread::JoinHandle<()>,
}

impl DebugChannel {
    pub fn new(host: &str, port: u16) -> Self {
        listener::start_listener(host, port)
    }
}

/// Plugin SanDebug — à inclure dans le jeu en mode debug/play.
pub struct SanDebugPlugin {
    /// Port WebSocket d'écoute (défaut : [`DEBUG_PORT`] = 9090).
    pub port: u16,
    /// Adresse d'écoute (défaut : `"0.0.0.0"` — joignable en Wi-Fi depuis Android).
    pub host: String,
}

impl Default for SanDebugPlugin {
    fn default() -> Self {
        Self {
            port: DEBUG_PORT,
            host: "0.0.0.0".to_string(),
        }
    }
}

/// Valeurs live du CameraFollow envoyées par l'éditeur sans recompilation.
/// `None` = pas encore reçu depuis le lancement (le composant de scène fait foi).
#[derive(Resource, Default)]
pub struct CameraFollowLive {
    pub offset: Option<[f32; 3]>,
    pub smooth: Option<f32>,
    pub look_at_target: Option<bool>,
}

/// Valeurs live du PlayerController envoyées par l'éditeur sans recompilation.
/// `None` = pas encore reçu (les valeurs de `PlayerMarker` font foi).
#[derive(Resource, Default)]
pub struct PlayerControllerLive {
    pub speed: Option<f32>,
    pub sprint_speed: Option<f32>,
    pub jump_height: Option<f32>,
    pub gravity: Option<f32>,
}

/// Flag de capture d'écran demandée par l'éditeur.
#[derive(Resource, Default)]
pub struct PendingScreenshot(pub bool);

/// File des mises à jour d'entités reçues depuis l'éditeur, à appliquer
/// dans le système exclusif [`live::apply_live_entity_properties`].
#[derive(Resource, Default)]
pub struct PendingEntityProps(
    pub Vec<(String, Vec<san_motor::debug_protocol::LiveProp>)>,
);

impl Plugin for SanDebugPlugin {
    fn build(&self, app: &mut App) {
        app.insert_resource(DebugChannel::new(&self.host, self.port));
        app.init_resource::<entities::PendingGetEntities>();
        app.init_resource::<CameraFollowLive>();
        app.init_resource::<PlayerControllerLive>();
        app.init_resource::<PendingEntityProps>();
        app.init_resource::<PendingScreenshot>();
        // handle_debug_messages : Update — lit les commandes, pose flags/resources
        app.add_systems(Update, handler::handle_debug_messages);
        // capture_game_screenshot : Update — spawn Screenshot si demandé
        app.add_systems(Update, screenshot::capture_game_screenshot);
        // send_entity_list : Last, exclusif — répond GetEntities avec le vrai World
        app.add_systems(Last, entities::send_entity_list);
        // apply_live_entity_properties : Last, exclusif — applique Transform/Visibility/etc.
        app.add_systems(Last, live::apply_live_entity_properties
            .after(entities::send_entity_list));
    }
}
