//! Primary editor viewport render-to-texture target and editor camera binding.

use bevy::camera::RenderTarget;
use bevy::prelude::*;
use bevy::window::{PrimaryWindow, WindowRef};

use crate::camera::EditorCamera;
use crate::render_target::{ensure_hdr_panel_target, ViewportRenderTarget, ViewportTarget};
use crate::state::{EditorMode, PlayPossession};
use crate::ui::UiState;
use crate::viewport::panel_physical_size;

pub struct SceneViewPlugin;

impl Plugin for SceneViewPlugin {
    fn build(&self, app: &mut App) {
        app.init_resource::<ViewportRenderTarget>().add_systems(
            PostUpdate,
            (
                ensure_viewport_render_target,
                sync_editor_camera_viewport_target,
            )
                .chain()
                .after(crate::ui::show_ui_system),
        );
    }
}

pub(crate) fn ensure_viewport_render_target(
    ui_state: Res<UiState>,
    window: Single<&Window, With<PrimaryWindow>>,
    mut images: ResMut<Assets<Image>>,
    mut target: ResMut<ViewportRenderTarget>,
    mut last_size: Local<Option<UVec2>>,
) {
    let Some(physical_size) = panel_physical_size(&window, ui_state.viewport_rect) else {
        return;
    };

    if let Some(handle) = ensure_hdr_panel_target(physical_size, &mut last_size, &mut images) {
        target.0 = Some(ViewportTarget { image: handle });
    }
}

pub(crate) fn sync_editor_camera_viewport_target(
    mode: Res<State<EditorMode>>,
    possession: Res<PlayPossession>,
    target: Res<ViewportRenderTarget>,
    mut cameras: Query<(Entity, &mut Camera), With<EditorCamera>>,
    mut commands: Commands,
) {
    let scene_active = matches!(*mode.get(), EditorMode::Editing)
        || (*mode.get() == EditorMode::Playing && *possession == PlayPossession::Ejected);

    for (entity, mut camera) in &mut cameras {
        if scene_active {
            let Some(viewport_target) = target.0.as_ref() else {
                camera.viewport = None;
                camera.is_active = false;
                continue;
            };
            // Render the full offscreen target. A panel-sized
            // viewport on `RenderTarget::Image` can exceed the texture for a frame after
            // resize (wgpu: scissor rect not contained in render target).
            camera.viewport = None;
            camera.is_active = true;
            commands
                .entity(entity)
                .insert(RenderTarget::Image(viewport_target.image.clone().into()));
        } else {
            camera.viewport = None;
            camera.is_active = false;
            commands
                .entity(entity)
                .insert(RenderTarget::Window(WindowRef::Primary));
        }
    }
}
