use crate::TileStatus;

pub trait IntoTile {
	fn into_tile(self) -> i32;
}

// Implementations for unsigned integer types
impl IntoTile for u8 {
	fn into_tile(self) -> i32 {
		self as i32
	}
}

impl IntoTile for u16 {
	fn into_tile(self) -> i32 {
		self as i32
	}
}

impl IntoTile for u32 {
	fn into_tile(self) -> i32 {
		self as i32
	}
}

impl IntoTile for u64 {
	fn into_tile(self) -> i32 {
		self as i32
	}
}

impl IntoTile for u128 {
	fn into_tile(self) -> i32 {
		self as i32
	}
}

impl IntoTile for usize {
	fn into_tile(self) -> i32 {
		self as i32
	}
}

// Implementations for signed integer types
impl IntoTile for i8 {
	fn into_tile(self) -> i32 {
		self as i32
	}
}

impl IntoTile for i16 {
	fn into_tile(self) -> i32 {
		self as i32
	}
}

impl IntoTile for i32 {
	fn into_tile(self) -> i32 {
		self
	}
}

impl IntoTile for i64 {
	fn into_tile(self) -> i32 {
		self as i32
	}
}

impl IntoTile for i128 {
	fn into_tile(self) -> i32 {
		self as i32
	}
}

impl IntoTile for isize {
	fn into_tile(self) -> i32 {
		self as i32
	}
}

// We don't actually implement IntoTile for TileStatus, as this would conflict with a later From impl
impl TileStatus {
	pub fn into_tile(self) -> i32 {
		match self {
			Self::Ignore => 0,
			Self::Nothing => -1000001,
			Self::Anything => 1000001,
			Self::Is(value) => value,
			Self::IsNot(value) => -(value),
		}
	}
}

impl From<TileStatus> for i32 {
	fn from(value: TileStatus) -> Self {
		value.into_tile()
	}
}

impl From<TileStatus> for i64 {
	fn from(value: TileStatus) -> Self {
		value.into_tile() as i64
	}
}

impl<T: IntoTile> IntoTile for Option<T> {
	fn into_tile(self) -> i32 {
		match self {
			Some(value) => value.into_tile(),
			None => 0,
		}
	}
}

impl<T: IntoTile, E> IntoTile for Result<T, E> {
	fn into_tile(self) -> i32 {
		match self {
			Ok(value) => value.into_tile(),
			Err(_) => 0,
		}
	}
}

impl<I: IntoTile> From<I> for TileStatus {
	fn from(value: I) -> Self {
		let value = value.into_tile();
		match value {
			0 => Self::Ignore,
			1000001 => Self::Anything,
			-1000001 => Self::Nothing,
			other => {
				if other > 0 {
					Self::Is(other.into_tile())
				} else {
					Self::IsNot(other.abs().into_tile())
				}
			}
		}
	}
}
