use crate::timer::{TimerFixed,Realtime,Paused,Unpaused};

use crate::physics::{TimeInner as PhysicsTimeInner,Time as PhysicsTime};

#[derive(Clone,Copy,Hash,Eq,PartialEq,PartialOrd,Debug)]
pub enum TimeInner{}
pub type Time=crate::integer::Time<TimeInner>;

#[derive(Clone,Copy,Debug)]
pub enum FlagReason{
	Anticheat,
	StyleChange,
	Clock,
	Pause,
	Flying,
	Gravity,
	Timescale,
	TimeTravel,
	Teleport,
}
impl ToString for FlagReason{
	fn to_string(&self)->String{
		self.as_ref().to_owned()
	}
}
impl AsRef<str> for FlagReason{
	fn as_ref(&self)->&str{
		match self{
			FlagReason::Anticheat=>"Passed through anticheat zone.",
			FlagReason::StyleChange=>"Changed style.",
			FlagReason::Clock=>"Incorrect clock.  (This can be caused by internet hiccups)",
			FlagReason::Pause=>"Pausing is not allowed in this style.",
			FlagReason::Flying=>"Flying is not allowed in this style.",
			FlagReason::Gravity=>"Gravity modification is not allowed in this style.",
			FlagReason::Timescale=>"Timescale is not allowed in this style.",
			FlagReason::TimeTravel=>"Time travel is not allowed in this style.",
			FlagReason::Teleport=>"Illegal teleport.",
		}
	}
}

#[derive(Debug)]
pub enum Error{
	NotStarted,
	AlreadyStarted,
	AlreadyFinished,
}
impl std::fmt::Display for Error{
	fn fmt(&self,f:&mut std::fmt::Formatter<'_>)->std::fmt::Result{
		write!(f,"{self:?}")
	}
}
impl std::error::Error for Error{}

#[derive(Clone,Copy,Debug)]
enum RunState{
	Created,
	Started{timer:TimerFixed<Realtime<PhysicsTimeInner,TimeInner>,Unpaused>},
	Finished{timer:TimerFixed<Realtime<PhysicsTimeInner,TimeInner>,Paused>},
}

#[derive(Clone,Copy,Debug)]
pub struct Run{
	state:RunState,
	flagged:Option<FlagReason>,
}

impl Run{
	pub fn new()->Self{
		Self{
			state:RunState::Created,
			flagged:None,
		}
	}
	pub fn time(&self,time:PhysicsTime)->Time{
		match &self.state{
			RunState::Created=>Time::ZERO,
			RunState::Started{timer}=>timer.time(time),
			RunState::Finished{timer}=>timer.time(),
		}
	}
	pub fn start(&mut self,time:PhysicsTime)->Result<(),Error>{
		match &self.state{
			RunState::Created=>{
				self.state=RunState::Started{
					timer:TimerFixed::new(time,Time::ZERO),
				};
				Ok(())
			},
			RunState::Started{..}=>Err(Error::AlreadyStarted),
			RunState::Finished{..}=>Err(Error::AlreadyFinished),
		}
	}
	pub fn finish(&mut self,time:PhysicsTime)->Result<(),Error>{
		//this uses Copy
		match &self.state{
			RunState::Created=>Err(Error::NotStarted),
			RunState::Started{timer}=>{
				self.state=RunState::Finished{
					timer:timer.into_paused(time),
				};
				Ok(())
			},
			RunState::Finished{..}=>Err(Error::AlreadyFinished),
		}
	}
	pub fn flag(&mut self,flag_reason:FlagReason){
		//don't replace the first reason the run was flagged
		if self.flagged.is_none(){
			self.flagged=Some(flag_reason);
		}
	}
	pub fn get_finish_time(&self)->Option<Time>{
		match &self.state{
			RunState::Finished{timer}=>Some(timer.time()),
			_=>None,
		}
	}
}