use super::integer::Time;
use super::common::{bool_from_u8,bool_into_u8};

type TimedPhysicsInstruction=strafesnet_common::instruction::TimedInstruction<strafesnet_common::physics::Instruction,strafesnet_common::physics::Time>;

#[binrw::binrw]
#[brw(little)]
pub struct TimedInstruction{
	pub time:Time,
	pub instruction:Instruction,
}

impl TryInto<TimedPhysicsInstruction> for TimedInstruction{
	type Error=super::integer::RatioError;
	fn try_into(self)->Result<TimedPhysicsInstruction,Self::Error>{
		Ok(strafesnet_common::instruction::TimedInstruction{
			time:strafesnet_common::integer::Time::raw(self.time),
			instruction:self.instruction.try_into()?,
		})
	}
}
impl TryFrom<TimedPhysicsInstruction> for TimedInstruction{
	type Error=InstructionConvert;
	fn try_from(value:TimedPhysicsInstruction)->Result<Self,Self::Error>{
		Ok(Self{
			time:value.time.get(),
			instruction:value.instruction.try_into()?,
		})
	}
}
#[binrw::binrw]
#[brw(little)]
pub enum Instruction{
	#[brw(magic=0u8)]
	ReplaceMouse{
		m0:super::mouse::MouseState,
		m1:super::mouse::MouseState
	},
	#[brw(magic=1u8)]
	SetNextMouse(super::mouse::MouseState),
	#[brw(magic=2u8)]
	SetMoveRight(
		#[br(map=bool_from_u8)]
		#[bw(map=bool_into_u8)]
		bool),
	#[brw(magic=3u8)]
	SetMoveUp(
		#[br(map=bool_from_u8)]
		#[bw(map=bool_into_u8)]
		bool),
	#[brw(magic=4u8)]
	SetMoveBack(
		#[br(map=bool_from_u8)]
		#[bw(map=bool_into_u8)]
		bool),
	#[brw(magic=5u8)]
	SetMoveLeft(
		#[br(map=bool_from_u8)]
		#[bw(map=bool_into_u8)]
		bool),
	#[brw(magic=6u8)]
	SetMoveDown(
		#[br(map=bool_from_u8)]
		#[bw(map=bool_into_u8)]
		bool),
	#[brw(magic=7u8)]
	SetMoveForward(
		#[br(map=bool_from_u8)]
		#[bw(map=bool_into_u8)]
		bool),
	#[brw(magic=8u8)]
	SetJump(
		#[br(map=bool_from_u8)]
		#[bw(map=bool_into_u8)]
		bool),
	#[brw(magic=9u8)]
	SetZoom(
		#[br(map=bool_from_u8)]
		#[bw(map=bool_into_u8)]
		bool),
	#[brw(magic=10u8)]
	Reset,
	#[brw(magic=11u8)]
	Restart(super::gameplay_modes::ModeId),
	#[brw(magic=12u8)]
	Spawn(super::gameplay_modes::ModeId,super::gameplay_modes::StageId),
	#[brw(magic=13u8)]
	PracticeFly,
	#[brw(magic=14u8)]
	SetSensitivity(super::integer::Ratio64Vec2),
	#[brw(magic=15u8)]
	SetSprint(
		#[br(map=bool_from_u8)]
		#[bw(map=bool_into_u8)]
		bool),
	#[brw(magic=255u8)]
	Idle,
}
#[derive(Debug)]
pub enum InstructionConvert{
	/// This is an instruction that can be dropped when serializing
	DropInstruction,
}
impl std::fmt::Display for InstructionConvert{
	fn fmt(&self,f:&mut std::fmt::Formatter<'_>)->std::fmt::Result{
		write!(f,"{self:?}")
	}
}
impl std::error::Error for InstructionConvert{}
impl TryInto<strafesnet_common::physics::Instruction> for Instruction{
	type Error=super::integer::RatioError;
	fn try_into(self)->Result<strafesnet_common::physics::Instruction,Self::Error>{
		Ok(match self{
			Instruction::ReplaceMouse{m0,m1}=>strafesnet_common::physics::Instruction::Mouse(strafesnet_common::physics::MouseInstruction::ReplaceMouse{m0:m0.into(),m1:m1.into()}),
			Instruction::SetNextMouse(m)=>strafesnet_common::physics::Instruction::Mouse(strafesnet_common::physics::MouseInstruction::SetNextMouse(m.into())),
			Instruction::SetMoveRight(state)=>strafesnet_common::physics::Instruction::SetControl(strafesnet_common::physics::SetControlInstruction::SetMoveRight(state.into())),
			Instruction::SetMoveUp(state)=>strafesnet_common::physics::Instruction::SetControl(strafesnet_common::physics::SetControlInstruction::SetMoveUp(state.into())),
			Instruction::SetMoveBack(state)=>strafesnet_common::physics::Instruction::SetControl(strafesnet_common::physics::SetControlInstruction::SetMoveBack(state.into())),
			Instruction::SetMoveLeft(state)=>strafesnet_common::physics::Instruction::SetControl(strafesnet_common::physics::SetControlInstruction::SetMoveLeft(state.into())),
			Instruction::SetMoveDown(state)=>strafesnet_common::physics::Instruction::SetControl(strafesnet_common::physics::SetControlInstruction::SetMoveDown(state.into())),
			Instruction::SetMoveForward(state)=>strafesnet_common::physics::Instruction::SetControl(strafesnet_common::physics::SetControlInstruction::SetMoveForward(state.into())),
			Instruction::SetJump(state)=>strafesnet_common::physics::Instruction::SetControl(strafesnet_common::physics::SetControlInstruction::SetJump(state.into())),
			Instruction::SetZoom(state)=>strafesnet_common::physics::Instruction::SetControl(strafesnet_common::physics::SetControlInstruction::SetZoom(state.into())),
			Instruction::SetSprint(state)=>strafesnet_common::physics::Instruction::SetControl(strafesnet_common::physics::SetControlInstruction::SetSprint(state.into())),
			Instruction::Reset=>strafesnet_common::physics::Instruction::Mode(strafesnet_common::physics::ModeInstruction::Reset),
			Instruction::Restart(mode_id)=>strafesnet_common::physics::Instruction::Mode(strafesnet_common::physics::ModeInstruction::Restart(strafesnet_common::gameplay_modes::ModeId::new(mode_id))),
			Instruction::Spawn(mode_id,stage_id)=>strafesnet_common::physics::Instruction::Mode(strafesnet_common::physics::ModeInstruction::Spawn(
				strafesnet_common::gameplay_modes::ModeId::new(mode_id),
				strafesnet_common::gameplay_modes::StageId::new(stage_id),
			)),
			Instruction::PracticeFly=>strafesnet_common::physics::Instruction::Misc(strafesnet_common::physics::MiscInstruction::PracticeFly),
			Instruction::SetSensitivity(sensitivity)=>strafesnet_common::physics::Instruction::Misc(strafesnet_common::physics::MiscInstruction::SetSensitivity(sensitivity.try_into()?)),
			Instruction::Idle=>strafesnet_common::physics::Instruction::Idle,
		})
	}
}
impl TryFrom<strafesnet_common::physics::Instruction> for Instruction{
	type Error=InstructionConvert;
	fn try_from(value:strafesnet_common::physics::Instruction)->Result<Self,Self::Error>{
		match value{
			strafesnet_common::physics::Instruction::Mouse(strafesnet_common::physics::MouseInstruction::ReplaceMouse{m0,m1})=>Ok(Instruction::ReplaceMouse{m0:m0.into(),m1:m1.into()}),
			strafesnet_common::physics::Instruction::Mouse(strafesnet_common::physics::MouseInstruction::SetNextMouse(m))=>Ok(Instruction::SetNextMouse(m.into())),
			strafesnet_common::physics::Instruction::SetControl(strafesnet_common::physics::SetControlInstruction::SetMoveRight(state))=>Ok(Instruction::SetMoveRight(state.into())),
			strafesnet_common::physics::Instruction::SetControl(strafesnet_common::physics::SetControlInstruction::SetMoveUp(state))=>Ok(Instruction::SetMoveUp(state.into())),
			strafesnet_common::physics::Instruction::SetControl(strafesnet_common::physics::SetControlInstruction::SetMoveBack(state))=>Ok(Instruction::SetMoveBack(state.into())),
			strafesnet_common::physics::Instruction::SetControl(strafesnet_common::physics::SetControlInstruction::SetMoveLeft(state))=>Ok(Instruction::SetMoveLeft(state.into())),
			strafesnet_common::physics::Instruction::SetControl(strafesnet_common::physics::SetControlInstruction::SetMoveDown(state))=>Ok(Instruction::SetMoveDown(state.into())),
			strafesnet_common::physics::Instruction::SetControl(strafesnet_common::physics::SetControlInstruction::SetMoveForward(state))=>Ok(Instruction::SetMoveForward(state.into())),
			strafesnet_common::physics::Instruction::SetControl(strafesnet_common::physics::SetControlInstruction::SetJump(state))=>Ok(Instruction::SetJump(state.into())),
			strafesnet_common::physics::Instruction::SetControl(strafesnet_common::physics::SetControlInstruction::SetZoom(state))=>Ok(Instruction::SetZoom(state.into())),
			strafesnet_common::physics::Instruction::SetControl(strafesnet_common::physics::SetControlInstruction::SetSprint(state))=>Ok(Instruction::SetSprint(state.into())),
			strafesnet_common::physics::Instruction::Mode(strafesnet_common::physics::ModeInstruction::Reset)=>Ok(Instruction::Reset),
			strafesnet_common::physics::Instruction::Mode(strafesnet_common::physics::ModeInstruction::Restart(mode_id))=>Ok(Instruction::Restart(mode_id.get())),
			strafesnet_common::physics::Instruction::Mode(strafesnet_common::physics::ModeInstruction::Spawn(mode_id,stage_id))=>Ok(Instruction::Spawn(
				mode_id.get(),
				stage_id.get(),
			)),
			strafesnet_common::physics::Instruction::Misc(strafesnet_common::physics::MiscInstruction::PracticeFly)=>Ok(Instruction::PracticeFly),
			strafesnet_common::physics::Instruction::Misc(strafesnet_common::physics::MiscInstruction::SetSensitivity(sensitivity))=>Ok(Instruction::SetSensitivity(sensitivity.into())),
			strafesnet_common::physics::Instruction::Idle=>Ok(Instruction::Idle),
		}
	}
}
