use std::hash::Hash;

use crate::quantity::Scalar;
use crate::quantity::Quantity;
use super::WholeUnit;
use super::Prefix;


#[derive(Debug)]
#[derive(Hash)]
#[derive(Copy, Clone)]
#[derive(PartialEq, Eq)]
pub struct FreeUnit {
	pub (in super) whole: WholeUnit,
	pub (in super) prefix: Prefix
}


impl ToString for FreeUnit {
	fn to_string(&self) -> String {

		let s = self.whole.to_string();
		let p = self.prefix.to_string();

		format!("{p}{s}")
	}
}

impl FreeUnit {
	pub fn from_whole(whole: WholeUnit) -> FreeUnit {
		return FreeUnit { whole, prefix: Prefix::None }
	}

	pub fn from_whole_prefix(whole: WholeUnit, prefix: Prefix) -> FreeUnit { FreeUnit {whole, prefix} }
	pub fn set_prefix(&mut self, prefix: Prefix) { self.prefix = prefix; }
	pub fn get_prefix(&self) -> Prefix { self.prefix }

	/// Returns a quantity q, so that self * q
	/// gives a quantity in base units.
	pub fn to_base_factor(&self) -> Quantity {

		let q = self.whole.base_factor();
		let mut q = q.unwrap_or(Quantity::new_rational_from_string("1").unwrap());

		let mut p = self.prefix.to_ratio();
		p.insert_unit(FreeUnit::from_whole(self.whole), Scalar::new_rational(1f64).unwrap());
		p.insert_unit(FreeUnit::from_whole_prefix(self.whole, self.prefix), Scalar::new_rational(-1f64).unwrap());
		q.mul_assign_no_convert(p);

		return q;
	}

	// Get this unit in terms of base units
	pub fn to_base(&self) -> Quantity {
		let f = self.to_base_factor();

		let mut q = Quantity::new_rational_from_string("1").unwrap();
		q.insert_unit(FreeUnit::from_whole_prefix(self.whole, self.prefix), Scalar::new_rational(1f64).unwrap());
		q.mul_assign_no_convert(f);

		return q;
	}
}


