use crate::graphics_worker::Instruction as GraphicsInstruction;
use strafesnet_settings::{directories::Directories,settings};
use strafesnet_session::session::{
	Session,Simulation,SessionInputInstruction,SessionControlInstruction,SessionPlaybackInstruction,ImplicitModeInstruction,
	Instruction as SessionInstruction,
};
use strafesnet_common::instruction::{TimedInstruction,InstructionConsumer};
use strafesnet_common::physics::Time as PhysicsTime;
use strafesnet_common::session::Time as SessionTime;
use strafesnet_common::timer::Timer;

pub enum Instruction{
	SessionInput(SessionInputInstruction),
	SessionControl(SessionControlInstruction),
	SessionPlayback(SessionPlaybackInstruction),
	Render,
	Resize(winit::dpi::PhysicalSize<u32>),
	ChangeMap(strafesnet_common::map::CompleteMap),
	LoadReplay(strafesnet_snf::bot::Segment),
}

pub fn new<'a>(
	mut graphics_worker:crate::compat_worker::INWorker<'a,crate::graphics_worker::Instruction>,
	directories:Directories,
	user_settings:settings::UserSettings,
)->crate::compat_worker::QNWorker<'a,TimedInstruction<Instruction,SessionTime>>{
	let physics=strafesnet_physics::physics::PhysicsState::default();
	let timer=Timer::unpaused(SessionTime::ZERO,PhysicsTime::ZERO);
	let simulation=Simulation::new(timer,physics);
	let mut session=Session::new(
		user_settings,
		directories,
		simulation,
	);
	crate::compat_worker::QNWorker::new(move |ins:TimedInstruction<Instruction,SessionTime>|{
		// excruciating pain
		macro_rules! run_session_instruction{
			($time:expr,$instruction:expr)=>{
				session.process_instruction(TimedInstruction{
					time:$time,
					instruction:$instruction,
				});
			};
		}
		macro_rules! run_graphics_worker_instruction{
			($instruction:expr)=>{
				graphics_worker.send($instruction).unwrap();
			};
		}
		match ins.instruction{
			Instruction::SessionInput(unbuffered_instruction)=>{
				run_session_instruction!(ins.time,SessionInstruction::Input(unbuffered_instruction));
			},
			Instruction::SessionControl(unbuffered_instruction)=>{
				run_session_instruction!(ins.time,SessionInstruction::Control(unbuffered_instruction));
			},
			Instruction::SessionPlayback(unbuffered_instruction)=>{
				run_session_instruction!(ins.time,SessionInstruction::Playback(unbuffered_instruction));
			},
			Instruction::Render=>{
				run_session_instruction!(ins.time,SessionInstruction::Idle);
				if let Some(frame_state)=session.get_frame_state(ins.time){
					run_graphics_worker_instruction!(GraphicsInstruction::Render(frame_state));
				}
			},
			Instruction::Resize(physical_size)=>{
				run_session_instruction!(ins.time,SessionInstruction::Idle);
				let user_settings=session.user_settings().clone();
				run_graphics_worker_instruction!(GraphicsInstruction::Resize(physical_size,user_settings));
			},
			Instruction::ChangeMap(complete_map)=>{
				run_session_instruction!(ins.time,SessionInstruction::ChangeMap(&complete_map));
				run_session_instruction!(ins.time,SessionInstruction::Input(SessionInputInstruction::Mode(ImplicitModeInstruction::ResetAndSpawn(strafesnet_common::gameplay_modes::ModeId::MAIN,strafesnet_common::gameplay_modes::StageId::FIRST))));
				run_graphics_worker_instruction!(GraphicsInstruction::ChangeMap(complete_map));
			},
			Instruction::LoadReplay(bot)=>{
				run_session_instruction!(ins.time,SessionInstruction::LoadReplay(bot));
			}
		}
	})
}
