# TODO: [Refactor] Remove/merge useless functions

class FrequencyScale(object):
	"""A convenient class to handle frequency and velocity scale

	Usage:
		fScale = FrequencyScale(vlsr, freqRef)

	Example:
		>>> fScale = FrequencyScale(0.0, 100)
		>>> print(fScale)
		FrequencyScale(vlsr=0.0 km/s, freqRef=100.000 MHz)

	Inputs
	------
	vlsr   : the vlsr in km/s
	freqRef: the reference frequency in MHz

	Note
	----
	Make sure the values provided are in the good units!


	"""

	DEFAULT_FROM_UNIT = "MHz"
	DEFAULT_TO_UNIT   = "km/s"

	# 1 m equals ...
	TO_M         = {"m": 1, "micrometer": 1E-6, "km": 1E3, "cm": 1E-2}
	# 1 m-1 equals ...
	TO_M_MINUS_1 = {"m-1": 1, "cm-1": 1E2}
	# 1 Hz equals ...
	TO_Hz        = {"Hz": 1, "kHz": 1E3, "MHz": 1E6, "GHz": 1E9}

	C = 299792.458  # km/s

	def __init__(self, vlsr, freqRef):
		super(FrequencyScale, self).__init__()
		self.freqRef      = freqRef
		self.vlsr         = vlsr
		self.fromUnit	  = self.DEFAULT_FROM_UNIT
		self.toUnit	      = self.DEFAULT_TO_UNIT

	def convertToMicrometer(self, value, fromUnit):
		self.handleWavelengthAndFrequency(value, fromUnit=fromUnit, toUnit="micrometer")

	def convertToMeter(self, value, fromUnit):
		self.handleWavelengthAndFrequency(value, fromUnit=fromUnit, toUnit="m")


	def handleWavelengthAndFrequency(self, value, fromUnit, toUnit):
		"""Return the conversion from wavelength to frequency or from frequency to wavelength
		
		h = handleWavelengthAndFrequency(x, fromUnit="Hz", toUnit="m")
		"""
		# frequency (Hz) -> wavelength (m)
		if fromUnit in self.TO_Hz.keys() and toUnit in self.TO_M.keys():
			nu = value * self.TO_Hz[fromUnit]
			_lambda = self.C * 1E3 / nu / self.TO_M[toUnit]
			return _lambda

		# wavelength (m) -> frequency (Hz)
		elif fromUnit in self.TO_M.keys() and toUnit in self.TO_Hz.keys():
			_lambda = value * self.TO_M[fromUnit]
			nu = self.C * 1E3 / _lambda / self.TO_Hz[toUnit]
			return nu
		else:
			raise ValueError("Invalid units")
		
	# TODO [Refactor]: Make it independent of the unit
	def convertMicrometerToMHz(self, value):
		"""Convert micrometers to MHz"""
		_lambda = value * self.TO_M['micrometer']
		c		= self.C * self.TO_M["km"]
		nu		= (c / _lambda ) / self.TO_Hz["MHz"]
		return nu

	def convertMHzToMicrometer(self, value):
		"""docstring for convert"""
		nu		= value * self.TO_Hz['MHz']
		c		= self.C * self.TO_M["km"]
		_lambda	= (c / nu) / self.TO_M["micrometer"]
		return _lambda

	def convertCm1ToMHz(self, value):
		sigma = value / self.TO_M["cm"]
		c     = self.C * self.TO_M["km"]
		nu	  = (sigma * c) / self.TO_Hz["MHz"]
		return nu

	def convertMHzToCm1(self, value):
		c     = self.C * self.TO_M["km"]
		nu	  = value * self.TO_Hz["MHz"]
		sigma = nu / c / self.TO_M_MINUS_1["cm-1"]
		return sigma 

	def toMHz(self, value, fromUnit):
		"""Convert value from a given unit to MHz
		
		Examples:
		---------
		>>> fScale = FrequencyScale(0.0, 686927.6887)
		# Convert micrometer to MHz
		>>> print(fScale.toMHz(1000, "micrometer"))
		299792.458

		# Convert km/s to MHz
		>>> print("%.3f" % fScale.toMHz(0.0, "km/s"))
		686927.689

		# Convert cm-1 to MHz
		>>> print("%.3f" % fScale.toMHz(1.0, "cm-1"))
		29979.246

		"""
		if fromUnit == "micrometer":
			return self.convertMicrometerToMHz(value)
		elif fromUnit == "km/s":
			return self.convertVelocityToFrequency(value, self.vlsr, self.freqRef)
		elif fromUnit == "cm-1":
			return self.convertCm1ToMHz(value)
		else:
			raise ValueError("Invalid unit. Available units: 'micrometer', 'km/s', 'cm-1'")

	def fromMHz(self, value, toUnit):
		"""Convert from MHz to a given unit"""
		if toUnit == "micrometer":
			return self.convertMHzToMicrometer(value)
		elif toUnit == "km/s":
			return self.convertFrequencyToVelocity(value, self.vlsr, self.freqRef)
		elif toUnit == "cm-1":
			return self.convertMHzToCm1(value)
		else:
			raise ValueError("Invalid unit. Available units: 'micrometer', 'km/s', 'cm-1'")

	def toKms(self, value, fromUnit):
		"""Convert value from a given unit to km/s
		
		Examples:
		---------
		>>> fScale = FrequencyScale(0.0, 686927.6887)
		# Convert micrometer to km/s
		>>> print(fScale.toKms(1000, "micrometer"))
		299792.458

		# Convert km/s to MHz
		>>> print("%.3f" % fScale.toKms(0.0, "MHz"))
		686927.689

		# Convert cm-1 to MHz
		>>> print("%.3f" % fScale.toKms(1.0, "cm-1"))
		29979.246

		"""
		if fromUnit == "micrometer":
			# micrometer -> MHz
			_lambda = value * self.TO_M['micrometer']
			c		= self.C * self.TO_M["km"]
			nu		= (c / _lambda ) / self.TO_Hz["MHz"]
			# MHz -> km/s
			return self.convertFrequencyToVelocity(nu, self.vlsr, self.freqRef)
		elif fromUnit == "MHz":
			# MHz -> km/s
			return self.convertFrequencyToVelocity(value, self.vlsr, self.freqRef)
		elif fromUnit == "cm-1":
			nu = self.convertCm1ToMHz(value)
			return self.convertFrequencyToVelocity(nu, self.vlsr, self.freqRef)
		else:
			raise ValueError("Invalid unit. Available units: 'micrometer', 'MHz', 'cm-1'")


	def toVelocity(self, *frequencies):
		"""Convert the input frequency to velocity 

		Usage:
			velocity   = fScale.toVelocity(unit, frequency)
			velocities = fScale.toVelocity(unit, frequency1, frequency2, ...)

		Inputs
		------
		unit: the unit of the frequency
		frequencies: the unit of the frequency

		Returns
		-------
		velocity

		Examples
		--------
		# Convert a frequency to a velocity
		>>> fScale = FrequencyScale(0.0, 686927.6887)
		>>> fScale.toVelocity(686927.6887)
		0.0
		>>> print("%.3f" % fScale.toVelocity(686929.0))
		-0.572
		>>> print("%.3f" % fScale.fromFrequency(686929.0))
		-0.572

		# A list is returned when many frequencies are passed
		>>> isinstance(fScale.toVelocity(686927.6887, 686929.0), list)
		True

		# Convert frequencies to velocities
		>>> fScale = FrequencyScale(0.0, 686927.6887)
		>>> velocities = fScale.toVelocity(686927.6887, 686929.0)
		>>> print("%.3f %.3f" % (velocities[0], velocities[1]))
		0.000 -0.572
		>>> velocities = fScale.fromFrequency(686927.6887, 686929.0)
		>>> print("%.3f %.3f" % (velocities[0], velocities[1]))
		0.000 -0.572

		"""
		velocities = map(lambda f: self.convertFrequencyToVelocity(f, self.vlsr, self.freqRef), frequencies)
		if len(velocities) == 1:
			velocities = velocities[0]
		return velocities

	def toFrequency(self, *velocities):
		"""Convert the input velocity to frequency  

		Usage:
			frequency   = fScale.toFrequency(unit, velocity)
			frequencies = fScale.toFrequency(unit, velocity1, velocity2, ...)

		Inputs
		------
		unit: the unit of the frequency
		frequencies: the unit of the frequency

		Returns
		-------
		velocity

		Examples
		--------
		# Convert a velocity to a frequency
		>>> fScale = FrequencyScale(0.0, 686927.6887)
		>>> print("%.3f" % fScale.toFrequency(0.0))
		686927.689
		>>> print("%.3f" % fScale.toFrequency(2))
		686923.106
		>>> print("%.3f" % fScale.fromVelocity(2))
		686923.106

		# A list is returned when many velocities are passed
		>>> isinstance(fScale.toFrequency(0.0, 2.0), list)
		True

		# Convert velocities to frequencies
		>>> fScale = FrequencyScale(0.0, 686927.6887)
		>>> frequencies = fScale.toFrequency(0.0, 2.0)
		>>> print("%.3f %.3f" % (frequencies[0], frequencies[1]))
		686927.689 686923.106
		>>> frequencies = fScale.toFrequency(0.0, 2.0)
		>>> print("%.3f %.3f" % (frequencies[0], frequencies[1]))
		686927.689 686923.106

		"""
		frequencies = map(lambda v: self.convertVelocityToFrequency(v, self.vlsr, self.freqRef), velocities)
		if len(frequencies) == 1:
			frequencies = frequencies[0]
		return frequencies

	fromFrequency = toVelocity
	fromVelocity  = toFrequency

	def __repr__(self):
		return "%s(vlsr=%.1f km/s, freqRef=%.3f MHz)" % (self.__class__.__name__, self.vlsr, self.freqRef)

	__str__ = __repr__

	def convertVelocityToFrequency(self, velocity, vlsr, freqRef):
		"""Convert velocity to frequency using the vlsr and the reference frequency

		Usage:
			frequency = convertVelocityToFrequency(velocity, vlsr, freqRef)

		Inputs
		------
		`velocity`: The velocity in km/s
		`vlsr`    : The vlsr in "km/s"
		`freqRef` : The reference frequency in MHz

		Returns
		-------
		`frequency`: The corresponding frequency in MHz

		Note: Formula.CKm is the celerity in km/s (c = 299792.458 km/s)

		"""
		return (1 - (velocity - vlsr)/self.C) * freqRef

	def convertFrequencyToVelocity(self, frequency, vlsr, freqRef):
		"""Convert frequency to velocity using the vlsr and the reference frequency

		Usage:
			velocity = convertFrequencyToVelocity(frequency, vlsr, freqRef)

		Inputs
		------
		`frequency`: The frequency in MHz
		`vlsr`     : The vlsr in "km/s"
		`freqRef`  : The reference frequency in MHz

		Returns
		-------
		`velocity`: The corresponding velocity in km/s

		Note: Formula.CKm is the celerity in km/s (c = 299792.458 km/s)
		"""
		return (1 - frequency/freqRef)*self.C + vlsr


# vim: set noet fdm=indent:
