// Copyright Marcus Del Favero 2025
// Licensed under the GNU AGPLv3 with an exception, see `README.md` for details
pub use super::action_manager::ActionManager;

use std::fmt::{Display, Formatter, Result as FmtResult};

use serde::{Serialize, Deserialize};
use winit::{keyboard::{KeyCode, PhysicalKey}, event::{MouseButton, WindowEvent, KeyEvent}};
use strum_macros::{EnumCount as EnumCountMacro, EnumIter};

#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, EnumCountMacro, EnumIter, Serialize, Deserialize)]
pub enum Action {
	// UI
	GuiLeave,
	PlayingLeave,
	ToggleFullscreen,
	#[serde(rename = "ChatOpen")] /* Backward compatibility */ TeamChatOpen,
	GlobalChatOpen,
	CommandOpen,
	ToggleHud,
	StoryTextClose,

	// Player
	MoveLeft,
	MoveRight,
	MoveDown,
	MoveUp,
	Shoot,
	DropFlag,

	// Zoom
	ZoomIn,
	ZoomOut,
	ZoomReset,

	// Spectating
	Drag,
	FollowPlayer,
	PrevPlayer,
	NextPlayer,

	// Debugging
	PrintPos,
}

#[derive(Clone, Copy)]
pub enum ActionEvent {
	Action(Action, ActionState),
	AllReleased, // When the window is unfocused
}

#[derive(Clone, Copy, PartialEq, Eq)]
pub enum ActionState {
	Released,
	Pressed,
}

impl From<bool> for ActionState {
	fn from(pressed: bool) -> ActionState {
		if pressed { ActionState::Pressed } else { ActionState::Released }
	}
}

impl ActionState {
	pub fn pressed(self) -> bool {
		self == ActionState::Pressed
	}
}

#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
pub enum Input {
	Key(KeyCode),
	MouseButton(MouseButton),
}

impl Input {
	pub fn from_event(event: &WindowEvent) -> Option<(Input, ActionState)> {
		match event {
			WindowEvent::KeyboardInput { event: KeyEvent { physical_key: PhysicalKey::Code(key), state, repeat: false, .. }, .. } => Some((Input::Key(*key), ActionState::from(state.is_pressed()))),
			WindowEvent::MouseInput { button, state, .. } => Some((Input::MouseButton(*button), ActionState::from(state.is_pressed()))),
			_ => None,
		}
	}
}

impl Display for Input {
	fn fmt(&self, fmt: &mut Formatter<'_>) -> FmtResult {
		/*
		 * Hacky using the Debug trait derived on the winit types, but I don't
		 * want to manually do this for every single option.
		 */
		match self {
			Input::Key(key) => {
				let mut out = format!("{key:?}");
				if &out[..3] == "Key" {
					out = out[3..].to_string();
				}

				write!(fmt, "Key {out}")
			},
			Input::MouseButton(button) => write!(fmt, "Mouse {button:?}"),
		}
	}
}
