// Copyright Marcus Del Favero 2025
// Licensed under the GNU AGPLv3 with an exception, see `README.md` for details
use std::any::Any;

use winit::event::Event;
use glium::Frame;
use strum::EnumCount;
use strum_macros::EnumCount as EnumCountMacro;

use super::{App, config::action::ActionEvent};

pub enum Status {
	Ok,
	PushState(GameStateId, Option<Box<dyn Any>>),
	PopState,
}

pub trait GameState {
	/** Enables the game state. */
	fn enable(&mut self, app: &mut App);

	/** Called when the game state is pushed. */
	fn push(&mut self, app: &mut App, msg: Option<Box<dyn Any>>);

	/** Disables the game state. */
	fn disable(&mut self, app: &mut App);

	fn event(&mut self, app: &mut App, event: &Event<()>);
	fn action(&mut self, action: ActionEvent);
	fn loop_iter(&mut self, app: &mut App, frame: &mut Frame) -> Status;
}

#[derive(Clone, Copy, PartialEq, Eq, EnumCountMacro)]
pub enum GameStateId {
	Title,
	PlaySelection,
	PsOnline,
	PsLocal,
	PsEnterAddress,
	PsCreateLocal,
	PsStoryMode,
	Playing,
	Settings,
	About,
}

#[derive(Default)]
pub(super) struct StateManager {
	states: [Option<Box<dyn GameState>>; GameStateId::COUNT],
}

impl StateManager {
	pub fn new() -> StateManager {
		StateManager::default()
	}

	pub fn get(&mut self, id: GameStateId, fallback: impl FnMut() -> Box<dyn GameState>) -> &mut dyn GameState {
		self.states[id as usize].get_or_insert_with(fallback).as_mut()
	}

	pub fn finish(mut self, id: GameStateId, app: &mut App) {
		if let Some(state) = &mut self.states[id as usize] {
			state.disable(app);
		}
	}
}
