use crate::{
	activities::{house, overworld, Activity},
	assets::Assets,
	State
};
use comfy::{EngineContext, Vec2};
use std::{
	ops::{Add, 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 {
	HumanLayer = -8,
	MagneticLayer = -5,
	ElectricLayer = -2,
	MapMax = -1,
	Human = 0,
	Ghost = 1,
	UI = 100,
	GameOver = 1000,
}

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
	}
}

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

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

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

	overworld::setup(state, ctx);
	//house::setup(state, ctx);

	ctx.load_texture_from_bytes(
		"ghost_house",
		include_bytes!(concat!(
			env!("CARGO_MANIFEST_DIR"),
			"/assets/entities/ghost.png"
		))
	);

	ctx.load_texture_from_bytes(
		"human_house",
		include_bytes!(concat!(
			env!("CARGO_MANIFEST_DIR"),
			"/assets/entities/human_captured.png"
		))
	);
}

/// The amount of energy a ghost consumes idle.
pub const GHOST_DISCHARGE_RATE: f32 = 70.0;
/// The amount of energy additionally consumed by a moving ghost.
pub const GHOST_DISCHARGE_RATE_MOVEMENT: f32 = 70.0;
/// The amount of energy a house consumes idle.
pub const HOUSE_DISCHARGE_RATE: f32 = 30.0;
/// The amount of energy a ghost can charge when inside a house.
pub const GHOST_CHARGE_RATE: f32 = 200.0;

pub fn update(state: &mut State, ctx: &mut EngineContext<'_>) {

	if state.ghost.charge > 0.0 {
	// Update the score. It's based on time.
	state.score += ctx.delta * 10.0;

	// Update the currently active activity.
	match state.activity {
		Activity::House(_) => house::update(state, ctx),
		Activity::Overworld => overworld::update(state, ctx)
	}

	// We update the charge of houses here - the charge will always decrease, even if the
	// house is not the current activity.
	for (house_pos, house) in &mut state.houses {
		house.charge -= ctx.delta * HOUSE_DISCHARGE_RATE;

		match state.activity {
			Activity::House(pos) if *house_pos == pos => {
				// The ghost is currently inside the house. Increase its discarge rate.
				house.charge -= ctx.delta * GHOST_DISCHARGE_RATE;
				if house.charge < 0.0 {
					state.ghost.charge += house.charge;
					house.charge = 0.0;
				}

				// And possibly also charge the ghost when inside a house.
				if state.ghost.charge < state.ghost.max_charge {
					let charge_transfer = (ctx.delta * GHOST_CHARGE_RATE)
						.min(state.ghost.max_charge - state.ghost.charge)
						.min(house.charge);
					state.ghost.charge += charge_transfer;
					house.charge -= charge_transfer;
				}
			},
			_ => {}
		}
	}}
	else {
		crate::game_over::update(state, ctx);
	}

	// Make sure the ghost's charge never drops below 0.
	state.ghost.charge = state.ghost.charge.max(0.0);
}

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);
	if state.ghost.charge <= 0.0 {
		crate::game_over::draw(state, engine);
	}
}
