// SPDX-License-Identifier: LGPL-2.1-only
// Copyright (C) 2026 Liberux Labs, S. L. <info@liberux.net>

use crate::app::App;
use crate::event_loop::{ AppData, SurfaceFocus };

use crate::input::gesture::{ MoveOutcome, ReleaseEvent };

impl<A: App> AppData<A>
{
	/// Apply the outcome of a motion event. Non-blocking side-effects
	/// only: push messages, call app callbacks, set redraw flags.
	pub( crate ) fn apply_move_outcome
	(
		&mut self,
		focus:   SurfaceFocus,
		outcome: MoveOutcome<A::Message>,
	)
	{
		match outcome
		{
			MoveOutcome::Idle => {}
			MoveOutcome::Drag { pos } =>
			{
				let ( ox, oy ) = self.surface_offset_for( focus );
				self.app.on_drag_move( pos.x + ox, pos.y + oy );
				self.dirty_caches();
				self.surface_mut( focus ).request_redraw();
				self.main.request_redraw();
				for ss in self.overlays.values_mut()
				{
					ss.request_redraw();
				}
			}
			MoveOutcome::Slider { msg } =>
			{
				if let Some( m ) = msg
				{
					self.pending_msgs.push( m );
				}
				self.surface_mut( focus ).request_redraw();
			}
			MoveOutcome::Scroll =>
			{
				self.surface_mut( focus ).request_redraw();
			}
			MoveOutcome::OverscrollDown { progress } =>
			{
				self.app.on_overscroll_down_progress( progress );
				// Like a vertical swipe, the dismiss rides an overlay panel
				// (subsurface) over a static main, so refresh the overlays but
				// leave the main alone — re-rastering it every motion is waste.
				self.overlays_dirty = true;
				for ss in self.overlays.values_mut()
				{
					ss.request_redraw();
				}
			}
			MoveOutcome::Swipe { up, down, horizontal } =>
			{
				if let Some( v ) = up         { self.app.on_swipe_progress( v ); }
				if let Some( v ) = down       { self.app.on_swipe_down_progress( v ); }
				if let Some( v ) = horizontal { self.app.on_swipe_horizontal_progress( v ); }
				// Swipe-progress callbacks mutate app state outside `update`,
				// so the cached trees are stale and the affected surfaces must
				// redraw. Only the HORIZONTAL pager moves the main surface,
				// though — vertical swipes drive overlay panels and leave the
				// main static. Re-rastering the (full-screen) main on every
				// motion event of a vertical swipe is pure waste and, on a slow
				// GPU, stalls the event loop so the gesture itself feels laggy
				// to start. So refresh the overlays always, but the main only
				// for a horizontal swipe.
				// ...unless the app is sliding the pager on subsurfaces
				// (`subsurface_motion_only`): then the main is unchanged and the
				// slide is a per-iteration subsurface reposition, so re-rastering
				// the full main every motion event is the same waste the vertical
				// panels already avoid — and, with child subsurfaces, it flickers.
				if horizontal.is_some() && !self.app.subsurface_motion_only()
				{
					self.view_dirty = true;
					if let SurfaceFocus::Main = focus
					{
						self.main.request_redraw();
					}
				}
				self.overlays_dirty = true;
				for ss in self.overlays.values_mut()
				{
					ss.request_redraw();
				}
			}
		}
	}

	/// Apply the ordered list of events from a release. A single
	/// release can emit more than one event (e.g. horizontal
	/// fall-through followed by a vertical commit or fall-through), so
	/// we walk the list and run each event's side-effects in order.
	pub( crate ) fn apply_release_events
	(
		&mut self,
		focus:  SurfaceFocus,
		events: Vec<ReleaseEvent<A::Message>>,
	)
	{
		for event in events
		{
			self.apply_release_event( focus, event );
		}
	}

	fn apply_release_event
	(
		&mut self,
		focus: SurfaceFocus,
		event: ReleaseEvent<A::Message>,
	)
	{
		match event
		{
			ReleaseEvent::Drop { pos } =>
			{
				let ( ox, oy ) = self.surface_offset_for( focus );
				if let Some( msg ) = self.app.on_drop( pos.x + ox, pos.y + oy )
				{
					self.pending_msgs.push( msg );
				}
				self.clear_long_press_drag();
				self.dirty_caches();
				self.main.request_redraw();
				self.main.frame_pending = false;
				for ss in self.overlays.values_mut()
				{
					ss.request_redraw();
				}
			}
			ReleaseEvent::SwipeLeft =>
			{
				if let Some( msg ) = self.app.on_swipe_left()
				{
					self.pending_msgs.push( msg );
				}
				// When the app handles the release internally (settle
				// animation, state-only mutation) it returns no
				// Message, so the update-driven redraw never fires.
				// Kick the main surface here so `is_animating()` has a
				// frame to latch onto.
				self.dirty_caches();
				self.main.request_redraw();
				self.main.frame_pending = false;
			}
			ReleaseEvent::SwipeRight =>
			{
				if let Some( msg ) = self.app.on_swipe_right()
				{
					self.pending_msgs.push( msg );
				}
				self.dirty_caches();
				self.main.request_redraw();
				self.main.frame_pending = false;
			}
			ReleaseEvent::SwipeUp =>
			{
				if let Some( msg ) = self.app.on_swipe_up()
				{
					self.pending_msgs.push( msg );
				}
				self.dirty_caches();
				self.main.request_redraw();
				self.main.frame_pending = false;
				for ss in self.overlays.values_mut()
				{
					ss.request_redraw();
					ss.frame_pending = false;
				}
			}
			ReleaseEvent::SwipeDown =>
			{
				if let Some( msg ) = self.app.on_swipe_down()
				{
					self.pending_msgs.push( msg );
				}
				self.dirty_caches();
				self.main.request_redraw();
				self.main.frame_pending = false;
				for ss in self.overlays.values_mut()
				{
					ss.request_redraw();
					ss.frame_pending = false;
				}
			}
			ReleaseEvent::OverscrollDown { committed } =>
			{
				if let Some( msg ) = self.app.on_overscroll_down( committed )
				{
					self.pending_msgs.push( msg );
				}
				self.dirty_caches();
				self.main.request_redraw();
				self.main.frame_pending = false;
				for ss in self.overlays.values_mut()
				{
					ss.request_redraw();
					ss.frame_pending = false;
				}
			}
			ReleaseEvent::HorizontalFellThrough =>
			{
				// Below threshold: pulse horizontal 0 so a
				// vertical-dominant gesture that drifted laterally
				// does not leave the app stuck on a stale horizontal
				// progress.
				self.app.on_swipe_horizontal_progress( 0.0 );
				self.dirty_caches();
				self.main.request_redraw();
				self.main.frame_pending = false;
			}
			ReleaseEvent::VerticalFellThrough =>
			{
				self.app.on_swipe_progress( 0.0 );
				self.app.on_swipe_down_progress( 0.0 );
				self.dirty_caches();
			}
			ReleaseEvent::PushMsg( msg ) =>
			{
				self.pending_msgs.push( msg );
			}
			ReleaseEvent::EmptyRelease =>
			{
				match focus
				{
					SurfaceFocus::Main =>
					{
						if let Some( msg ) = self.app.on_tap()
						{
							self.pending_msgs.push( msg );
						}
					}
					SurfaceFocus::Overlay( id ) =>
					{
						if let Some( msg ) = self.overlay_dismiss_msg( id )
						{
							self.pending_msgs.push( msg );
						}
					}
				}
			}
		}
	}
}
