use super::common::flag;
use super::integer::{Time,Planar64,Planar64Vec3};

#[binrw::binrw]
#[brw(little)]
pub struct ContactingLadder{
	#[br(map=|paused:u8|paused!=0)]
	#[bw(map=|paused:&bool|*paused as u8)]
	pub sticky:bool,
}
impl Into<strafesnet_common::gameplay_attributes::ContactingLadder> for ContactingLadder{
	fn into(self)->strafesnet_common::gameplay_attributes::ContactingLadder{
		strafesnet_common::gameplay_attributes::ContactingLadder{
			sticky:self.sticky.into(),
		}
	}
}
impl From<strafesnet_common::gameplay_attributes::ContactingLadder> for ContactingLadder{
	fn from(value:strafesnet_common::gameplay_attributes::ContactingLadder)->Self{
		Self{
			sticky:value.sticky.into(),
		}
	}
}

#[binrw::binrw]
#[brw(little)]
pub enum ContactingBehaviour{
	#[brw(magic=0u8)]
	Surf,
	#[brw(magic=1u8)]
	Ladder(ContactingLadder),
	#[brw(magic=2u8)]
	NoJump,
	#[brw(magic=3u8)]
	Cling,
	#[brw(magic=4u8)]
	Elastic(u32),
}
impl Into<strafesnet_common::gameplay_attributes::ContactingBehaviour> for ContactingBehaviour{
	fn into(self)->strafesnet_common::gameplay_attributes::ContactingBehaviour{
		match self{
			ContactingBehaviour::Surf=>
			strafesnet_common::gameplay_attributes::ContactingBehaviour::Surf,
			ContactingBehaviour::Ladder(contacting_ladder)=>
			strafesnet_common::gameplay_attributes::ContactingBehaviour::Ladder(
				contacting_ladder.into(),
			),
			ContactingBehaviour::NoJump=>
			strafesnet_common::gameplay_attributes::ContactingBehaviour::NoJump,
			ContactingBehaviour::Cling=>
			strafesnet_common::gameplay_attributes::ContactingBehaviour::Cling,
			ContactingBehaviour::Elastic(elasticity)=>
			strafesnet_common::gameplay_attributes::ContactingBehaviour::Elastic(elasticity),
		}
	}
}
impl From<strafesnet_common::gameplay_attributes::ContactingBehaviour> for ContactingBehaviour{
	fn from(value:strafesnet_common::gameplay_attributes::ContactingBehaviour)->Self{
		match value{
			strafesnet_common::gameplay_attributes::ContactingBehaviour::Surf=>
			ContactingBehaviour::Surf,
			strafesnet_common::gameplay_attributes::ContactingBehaviour::Ladder(contacting_ladder)=>
			ContactingBehaviour::Ladder(
				contacting_ladder.into()
			),
			strafesnet_common::gameplay_attributes::ContactingBehaviour::NoJump=>
			ContactingBehaviour::NoJump,
			strafesnet_common::gameplay_attributes::ContactingBehaviour::Cling=>
			ContactingBehaviour::Cling,
			strafesnet_common::gameplay_attributes::ContactingBehaviour::Elastic(elasticity)=>
			ContactingBehaviour::Elastic(elasticity),
		}
	}
}

#[binrw::binrw]
#[brw(little)]
pub struct IntersectingWater{
	pub viscosity:Planar64,
	pub density:Planar64,
	pub velocity:Planar64Vec3,
}
impl Into<strafesnet_common::gameplay_attributes::IntersectingWater> for IntersectingWater{
	fn into(self)->strafesnet_common::gameplay_attributes::IntersectingWater{
		strafesnet_common::gameplay_attributes::IntersectingWater{
			viscosity:strafesnet_common::integer::Planar64::raw(self.viscosity),
			density:strafesnet_common::integer::Planar64::raw(self.density),
			velocity:strafesnet_common::integer::vec3::raw_array(self.velocity),
		}
	}
}
impl From<strafesnet_common::gameplay_attributes::IntersectingWater> for IntersectingWater{
	fn from(value:strafesnet_common::gameplay_attributes::IntersectingWater)->Self{
		Self{
			viscosity:value.viscosity.to_raw(),
			density:value.density.to_raw(),
			velocity:value.velocity.map(|t|t.to_raw()).to_array(),
		}
	}
}

#[binrw::binrw]
#[brw(little)]
pub struct Accelerator{
	pub acceleration:Planar64Vec3
}
impl Into<strafesnet_common::gameplay_attributes::Accelerator> for Accelerator{
	fn into(self)->strafesnet_common::gameplay_attributes::Accelerator{
		strafesnet_common::gameplay_attributes::Accelerator{
			acceleration:strafesnet_common::integer::vec3::raw_array(self.acceleration)
		}
	}
}
impl From<strafesnet_common::gameplay_attributes::Accelerator> for Accelerator{
	fn from(value:strafesnet_common::gameplay_attributes::Accelerator)->Self{
		Self{
			acceleration:value.acceleration.map(|t|t.to_raw()).to_array(),
		}
	}
}

#[binrw::binrw]
#[brw(little)]
pub enum Booster{
	#[brw(magic=0u8)]
	Velocity(Planar64Vec3),
	#[brw(magic=1u8)]
	Energy{direction:Planar64Vec3,energy:Planar64},
	#[brw(magic=2u8)]
	AirTime(Time),
	#[brw(magic=3u8)]
	Height(Planar64),

}
impl Into<strafesnet_common::gameplay_attributes::Booster> for Booster{
	fn into(self)->strafesnet_common::gameplay_attributes::Booster{
		match self{
			Booster::Velocity(velocity)=>
			strafesnet_common::gameplay_attributes::Booster::Velocity(
				strafesnet_common::integer::vec3::raw_array(velocity)
			),
			Booster::Energy{direction,energy}=>
			strafesnet_common::gameplay_attributes::Booster::Energy{
				direction:strafesnet_common::integer::vec3::raw_array(direction),
				energy:strafesnet_common::integer::Planar64::raw(energy)
			},
			Booster::AirTime(time)=>
			strafesnet_common::gameplay_attributes::Booster::AirTime(
				strafesnet_common::integer::Time::raw(time)
			),
			Booster::Height(height)=>
			strafesnet_common::gameplay_attributes::Booster::Height(
				strafesnet_common::integer::Planar64::raw(height)
			),
		}
	}
}
impl From<strafesnet_common::gameplay_attributes::Booster> for Booster{
	fn from(value:strafesnet_common::gameplay_attributes::Booster)->Self{
		match value{
			strafesnet_common::gameplay_attributes::Booster::Velocity(velocity)=>
			Booster::Velocity(velocity.map(|t|t.to_raw()).to_array()),
			strafesnet_common::gameplay_attributes::Booster::Energy{direction,energy}=>
			Booster::Energy{
				direction:direction.map(|t|t.to_raw()).to_array(),
				energy:energy.to_raw(),
			},
			strafesnet_common::gameplay_attributes::Booster::AirTime(time)=>
			Booster::AirTime(time.get()),
			strafesnet_common::gameplay_attributes::Booster::Height(height)=>
			Booster::Height(height.to_raw()),
		}
	}
}

#[binrw::binrw]
#[brw(little,repr=u8)]
pub enum TrajectoryChoice{
	HighArcLongDuration,
	LowArcShortDuration,
}
impl Into<strafesnet_common::gameplay_attributes::TrajectoryChoice> for TrajectoryChoice{
	fn into(self)->strafesnet_common::gameplay_attributes::TrajectoryChoice{
		match self{
			TrajectoryChoice::HighArcLongDuration=>
			strafesnet_common::gameplay_attributes::TrajectoryChoice::HighArcLongDuration,
			TrajectoryChoice::LowArcShortDuration=>
			strafesnet_common::gameplay_attributes::TrajectoryChoice::LowArcShortDuration,
		}
	}
}
impl From<strafesnet_common::gameplay_attributes::TrajectoryChoice> for TrajectoryChoice{
	fn from(value:strafesnet_common::gameplay_attributes::TrajectoryChoice)->Self{
		match value{
			strafesnet_common::gameplay_attributes::TrajectoryChoice::HighArcLongDuration=>
			TrajectoryChoice::HighArcLongDuration,
			strafesnet_common::gameplay_attributes::TrajectoryChoice::LowArcShortDuration=>
			TrajectoryChoice::LowArcShortDuration,
		}
	}
}

#[binrw::binrw]
#[brw(little)]
pub enum SetTrajectory{
	#[brw(magic=0u8)]
	AirTime(Time),
	#[brw(magic=1u8)]
	Height(Planar64),
	#[brw(magic=2u8)]
	DotVelocity{direction:Planar64Vec3,dot:Planar64},
	#[brw(magic=3u8)]
	TargetPointTime{
		target_point:Planar64Vec3,
		time:Time,
	},
	#[brw(magic=4u8)]
	TargetPointSpeed{
		target_point:Planar64Vec3,
		speed:Planar64,
		trajectory_choice:TrajectoryChoice,
	},
	#[brw(magic=5u8)]
	Velocity(Planar64Vec3),
}
impl Into<strafesnet_common::gameplay_attributes::SetTrajectory> for SetTrajectory{
	fn into(self)->strafesnet_common::gameplay_attributes::SetTrajectory{
		match self{
			SetTrajectory::AirTime(time)=>
			strafesnet_common::gameplay_attributes::SetTrajectory::AirTime(
				strafesnet_common::integer::Time::raw(time)
			),
			SetTrajectory::Height(height)=>
			strafesnet_common::gameplay_attributes::SetTrajectory::Height(
				strafesnet_common::integer::Planar64::raw(height)
			),
			SetTrajectory::DotVelocity{direction,dot}=>
			strafesnet_common::gameplay_attributes::SetTrajectory::DotVelocity{
				direction:strafesnet_common::integer::vec3::raw_array(direction),
				dot:strafesnet_common::integer::Planar64::raw(dot),
			},
			SetTrajectory::TargetPointTime{target_point,time}=>
			strafesnet_common::gameplay_attributes::SetTrajectory::TargetPointTime{
				target_point:strafesnet_common::integer::vec3::raw_array(target_point),
				time:strafesnet_common::integer::Time::raw(time),
			},
			SetTrajectory::TargetPointSpeed{target_point,speed,trajectory_choice}=>
			strafesnet_common::gameplay_attributes::SetTrajectory::TargetPointSpeed{
				target_point:strafesnet_common::integer::vec3::raw_array(target_point),
				speed:strafesnet_common::integer::Planar64::raw(speed),
				trajectory_choice:trajectory_choice.into(),
			},
			SetTrajectory::Velocity(velocity)=>
			strafesnet_common::gameplay_attributes::SetTrajectory::Velocity(
				strafesnet_common::integer::vec3::raw_array(velocity)
			),
		}
	}
}
impl From<strafesnet_common::gameplay_attributes::SetTrajectory> for SetTrajectory{
	fn from(value:strafesnet_common::gameplay_attributes::SetTrajectory)->Self{
		match value{
			strafesnet_common::gameplay_attributes::SetTrajectory::AirTime(time)=>
			SetTrajectory::AirTime(
				time.get()
			),
			strafesnet_common::gameplay_attributes::SetTrajectory::Height(height)=>
			SetTrajectory::Height(
				height.to_raw()
			),
			strafesnet_common::gameplay_attributes::SetTrajectory::DotVelocity{direction,dot}=>
			SetTrajectory::DotVelocity{
				direction:direction.map(|t|t.to_raw()).to_array(),
				dot:dot.to_raw(),
			},
			strafesnet_common::gameplay_attributes::SetTrajectory::TargetPointTime{target_point,time}=>
			SetTrajectory::TargetPointTime{
				target_point:target_point.map(|t|t.to_raw()).to_array(),
				time:time.get(),
			},
			strafesnet_common::gameplay_attributes::SetTrajectory::TargetPointSpeed{target_point,speed,trajectory_choice}=>
			SetTrajectory::TargetPointSpeed{
				target_point:target_point.map(|t|t.to_raw()).to_array(),
				speed:speed.to_raw(),
				trajectory_choice:trajectory_choice.into(),
			},
			strafesnet_common::gameplay_attributes::SetTrajectory::Velocity(velocity)=>
			SetTrajectory::Velocity(
				velocity.map(|t|t.to_raw()).to_array()
			),
		}
	}
}

#[binrw::binrw]
#[brw(little)]
pub struct Wormhole{
	pub destination_model:u32,
}
impl Into<strafesnet_common::gameplay_attributes::Wormhole> for Wormhole{
	fn into(self)->strafesnet_common::gameplay_attributes::Wormhole{
		strafesnet_common::gameplay_attributes::Wormhole{
			destination_model:strafesnet_common::model::ModelId::new(self.destination_model),
		}
	}
}
impl From<strafesnet_common::gameplay_attributes::Wormhole> for Wormhole{
	fn from(value:strafesnet_common::gameplay_attributes::Wormhole)->Self{
		Self{
			destination_model:value.destination_model.get(),
		}
	}
}

#[binrw::binrw]
#[brw(little)]
pub struct GeneralAttributes{
	pub header:u8,
	#[br(if(header&Self::BOOSTER!=0))]
	pub booster:Option<Booster>,
	#[br(if(header&Self::TRAJECTORY!=0))]
	pub trajectory:Option<SetTrajectory>,
	#[br(if(header&Self::WORMHOLE!=0))]
	pub wormhole:Option<Wormhole>,
	#[br(if(header&Self::ACCELERATOR!=0))]
	pub accelerator:Option<Accelerator>,
}
impl GeneralAttributes{
	const BOOSTER:u8=1<<0;
	const TRAJECTORY:u8=1<<1;
	const WORMHOLE:u8=1<<2;
	const ACCELERATOR:u8=1<<3;
}
impl Into<strafesnet_common::gameplay_attributes::GeneralAttributes> for GeneralAttributes{
	fn into(self)->strafesnet_common::gameplay_attributes::GeneralAttributes{
		strafesnet_common::gameplay_attributes::GeneralAttributes{
			booster:self.booster.map(Into::into),
			trajectory:self.trajectory.map(Into::into),
			wormhole:self.wormhole.map(Into::into),
			accelerator:self.accelerator.map(Into::into),
		}
	}
}
impl From<strafesnet_common::gameplay_attributes::GeneralAttributes> for GeneralAttributes{
	fn from(value:strafesnet_common::gameplay_attributes::GeneralAttributes)->Self{
		let header=
		flag(value.booster.is_some(),GeneralAttributes::BOOSTER)
		|flag(value.trajectory.is_some(),GeneralAttributes::TRAJECTORY)
		|flag(value.wormhole.is_some(),GeneralAttributes::WORMHOLE)
		|flag(value.accelerator.is_some(),GeneralAttributes::ACCELERATOR);
		Self{
			header,
			booster:value.booster.map(Into::into),
			trajectory:value.trajectory.map(Into::into),
			wormhole:value.wormhole.map(Into::into),
			accelerator:value.accelerator.map(Into::into),
		}
	}
}

#[binrw::binrw]
#[brw(little)]
pub struct ContactingAttributes{
	pub header:u8,
	#[br(if(header&Self::CONTACTING_BEHAVIOUR!=0))]
	pub contact_behaviour:Option<ContactingBehaviour>,
}
impl ContactingAttributes{
	const CONTACTING_BEHAVIOUR:u8=1<<0;
}
impl Into<strafesnet_common::gameplay_attributes::ContactingAttributes> for ContactingAttributes{
	fn into(self)->strafesnet_common::gameplay_attributes::ContactingAttributes{
		strafesnet_common::gameplay_attributes::ContactingAttributes{
			contact_behaviour:self.contact_behaviour.map(Into::into),
		}
	}
}
impl From<strafesnet_common::gameplay_attributes::ContactingAttributes> for ContactingAttributes{
	fn from(value:strafesnet_common::gameplay_attributes::ContactingAttributes)->Self{
		Self{
			header:flag(value.contact_behaviour.is_some(),ContactingAttributes::CONTACTING_BEHAVIOUR),
			contact_behaviour:value.contact_behaviour.map(Into::into),
		}
	}
}

#[binrw::binrw]
#[brw(little)]
pub struct IntersectingAttributes{
	pub header:u8,
	#[br(if(header&Self::INTERSECTING_WATER!=0))]
	pub water:Option<IntersectingWater>,
}
impl IntersectingAttributes{
	const INTERSECTING_WATER:u8=1<<0;
}
impl Into<strafesnet_common::gameplay_attributes::IntersectingAttributes> for IntersectingAttributes{
	fn into(self)->strafesnet_common::gameplay_attributes::IntersectingAttributes{
		strafesnet_common::gameplay_attributes::IntersectingAttributes{
			water:self.water.map(Into::into),
		}
	}
}
impl From<strafesnet_common::gameplay_attributes::IntersectingAttributes> for IntersectingAttributes{
	fn from(value:strafesnet_common::gameplay_attributes::IntersectingAttributes)->Self{
		Self{
			header:flag(value.water.is_some(),IntersectingAttributes::INTERSECTING_WATER),
			water:value.water.map(Into::into),
		}
	}
}

#[binrw::binrw]
#[brw(little)]
pub struct ContactAttributes{
	contacting:ContactingAttributes,
	general:GeneralAttributes,
}
impl Into<strafesnet_common::gameplay_attributes::ContactAttributes> for ContactAttributes{
	fn into(self)->strafesnet_common::gameplay_attributes::ContactAttributes{
		strafesnet_common::gameplay_attributes::ContactAttributes{
			contacting:self.contacting.into(),
			general:self.general.into(),
		}
	}
}
impl From<strafesnet_common::gameplay_attributes::ContactAttributes> for ContactAttributes{
	fn from(value:strafesnet_common::gameplay_attributes::ContactAttributes)->Self{
		Self{
			contacting:value.contacting.into(),
			general:value.general.into(),
		}
	}
}

#[binrw::binrw]
#[brw(little)]
pub struct IntersectAttributes{
	intersecting:IntersectingAttributes,
	general:GeneralAttributes,
}
impl Into<strafesnet_common::gameplay_attributes::IntersectAttributes> for IntersectAttributes{
	fn into(self)->strafesnet_common::gameplay_attributes::IntersectAttributes{
		strafesnet_common::gameplay_attributes::IntersectAttributes{
			intersecting:self.intersecting.into(),
			general:self.general.into(),
		}
	}
}
impl From<strafesnet_common::gameplay_attributes::IntersectAttributes> for IntersectAttributes{
	fn from(value:strafesnet_common::gameplay_attributes::IntersectAttributes)->Self{
		Self{
			intersecting:value.intersecting.into(),
			general:value.general.into(),
		}
	}
}

#[binrw::binrw]
#[brw(little)]
pub enum CollisionAttributes{
	#[brw(magic=0u8)]
	Decoration,
	#[brw(magic=1u8)]
	Contact(ContactAttributes),
	#[brw(magic=2u8)]
	Intersect(IntersectAttributes),
}
impl Into<strafesnet_common::gameplay_attributes::CollisionAttributes> for CollisionAttributes{
	fn into(self)->strafesnet_common::gameplay_attributes::CollisionAttributes{
		match self{
			CollisionAttributes::Decoration=>
			strafesnet_common::gameplay_attributes::CollisionAttributes::Decoration,
			CollisionAttributes::Contact(attr)=>
			strafesnet_common::gameplay_attributes::CollisionAttributes::Contact(attr.into()),
			CollisionAttributes::Intersect(attr)=>
			strafesnet_common::gameplay_attributes::CollisionAttributes::Intersect(attr.into()),
		}
	}
}
impl From<strafesnet_common::gameplay_attributes::CollisionAttributes> for CollisionAttributes{
	fn from(value:strafesnet_common::gameplay_attributes::CollisionAttributes)->Self{
		match value{
			strafesnet_common::gameplay_attributes::CollisionAttributes::Decoration=>
			CollisionAttributes::Decoration,
			strafesnet_common::gameplay_attributes::CollisionAttributes::Contact(attr)=>
			CollisionAttributes::Contact(attr.into()),
			strafesnet_common::gameplay_attributes::CollisionAttributes::Intersect(attr)=>
			CollisionAttributes::Intersect(attr.into()),
		}
	}
}
