use crate::{
	activities::{house, overworld, Activity},
	assets::Assets,
	State
};
use comfy::{EngineContext, Vec2};
use std::{ops::Sub, time::Instant};

#[derive(Debug)]
pub struct Ghost {
	/// Current electric charge of the Ghost.
	pub charge: f32,
	/// Max electric charge of the Ghost.
	pub max_charge: f32,

	/// The position of the ghost in the overworld. Expressed in tile coordinates, but
	/// as a float as a ghost takes more than one tick to move.
	pub overworld_pos: Vec2,
	/// Pending movement of the ghost in the overworld.
	pub overworld_movement_pending: Vec2,
	/// The current movement speed of the ghost in tiles/sec.
	pub overworld_movement_speed: f32,
	/// The timestamp of the last overworld position update.
	pub overworld_pos_last_update: Instant
}

impl Ghost {
	pub fn update_overworld_pos(&mut self, now: Instant) {
		// This calculation is extremely simplistic. It will not work properly if both
		// x and y of movement_pending are non-zero. But we only move left,right,up,down
		// so that should never happen!
		let secs = now
			.duration_since(self.overworld_pos_last_update)
			.as_secs_f32();
		let mut movement = self.overworld_movement_pending.signum()
			* self.overworld_movement_speed
			* secs;

		// limit the movement to the remaining movement
		if self.overworld_movement_pending.x.abs() < movement.x.abs() {
			movement.x = self.overworld_movement_pending.x;
		}
		if self.overworld_movement_pending.y.abs() < movement.y.abs() {
			movement.y = self.overworld_movement_pending.y;
		}

		// execute the movement
		self.overworld_pos += movement;
		self.overworld_movement_pending -= movement;
		self.overworld_pos_last_update = now;
	}
}

impl Default for Ghost {
	fn default() -> Self {
		Self {
			charge: 1000.0,
			max_charge: 1000.0,

			overworld_pos: Vec2::ZERO,
			overworld_movement_pending: Vec2::ZERO,
			overworld_movement_speed: 0.0,
			overworld_pos_last_update: Instant::now()
		}
	}
}
#[repr(i32)]
pub enum ZLayer {
	MapMax = -1,
	Human = 0,
	Ghost = 1
}

impl From<ZLayer> for i32 {
	fn from(value: ZLayer) -> Self {
		// safe because #[repr(i32)]
		value as i32
	}
}

impl Sub<i32> for ZLayer {
	type Output = i32;

	fn sub(self, other: i32) -> Self::Output {
		i32::from(self) - other
	}
}

pub fn setup(_state: &mut State, ctx: &mut EngineContext<'_>) {
	Assets::load(ctx);
}

pub fn update(state: &mut State, engine: &mut EngineContext<'_>) {
	state.score += engine.delta * 10.0;
	match state.activity {
		Activity::House(_) => house::update(state, engine),
		Activity::Overworld => overworld::update(state, engine)
	}
}

pub fn draw(state: &State, engine: &EngineContext<'_>) {
	match state.activity {
		Activity::House(_) => house::draw(state, engine),
		Activity::Overworld => overworld::draw(state, engine)
	}
	crate::ui::draw(state, engine);
}
