"""The Parameter defines the physical parameter and the domain on which it is set.
"""

import decimal


def dec(value):
	"""Return the decimal value"""
	if value is None:
		return value
	else:
		return decimal.Decimal(str(value))


# TODO Refactor Parameter: The values displayed should be the values set 
# by the user. log_mode shouldn't modify the initial values but instead
# they should inform the functions on how to deal with the values
# (log_mode == True means apply log10, otherwise do nothing)
class Parameter(object):
	def __init__(self, name, min, max, step=None, nstep=None, unit="", log_mode = False):
		self.name        = name
		self._unit       = unit
		self._min        = dec(min)
		self._max        = dec(max)
		self.log_mode    = log_mode
		self.model       = ""
		self.interacting = None
		self.geometry    = ""
		self._values     = []
		
		if self.unit:
			self.x_axis_label = "%s" % self.unit
			self.y_axis_label = "%s" % self.unit
		else:
			self.unit = ""
			self.x_axis_label = "%s" % self.name
			self.y_axis_label = "%s" % self.name
		
		# Setting step and nstep
		# Only step or nstep at a time
		if step and nstep:
			raise ValueError("You cannot define step and nstep in the same time.")
		
		# At least step or nstep
		if step is None and nstep is None :
			raise ValueError("You must define step or nstep.")

		self._step  = dec(step)
		self._nstep = dec(nstep)

	# Unit
	def get_unit(self):
		if not self._unit:
			if self.name.lower().startswith("n"):
				unit = " (cm-2)"
			elif self.name.lower().startswith("tex"):
				unit = " (K)"
			elif self.name.lower().startswith("tkin"):
				unit = " (K)"
			else:
				unit = ""
		else:
			unit = self._unit

		if self.log_mode:
			return "Log[%s%s]" % (self.name, unit)
		else:
			return "%s%s" % (self.name, unit)

	# TODO Fix set_unit
	def set_unit(self, value):
		# if self.log_mode:
		# 	self._unit = "Log[%s (%s)]" % (self.name, value)
		# else:
		# 	self._unit = "%s (%s)" % (self.name, value)
		self._unit = value

	# Min
	def get_min(self):
		if self.log_mode:
			return self._min.log10()
		else:
			return self._min

	def set_min(self, value):
		self._min = dec(value)
		
	# Max
	def get_max(self):
		if self.log_mode:
			return self._max.log10()
		else:
			return self._max

	def set_max(self, value):
		self._max = dec(value)
		
	# Step
	def get_step(self):
		if self.min == self.max:
			return 0

		if self._step and self._nstep is None:
			if self.log_mode:
				return (self.max - self.min)/(self.nstep - 1)
			else:
				return self._step

		elif self._nstep and self._step is None:
			# Prevent nstep from being lower than 1 (otherwise DivisionByZero)
			if self.nstep > 1:
				return (self.max - self.min)/(self.nstep - 1)
			else:
				return (self.max - self.min)

	def set_step(self, value):
		self._step = value
		
	# Nstep
	def get_nstep(self):
		if self.min == self.max:
			return 1
		
		if self._nstep and self._step is None:
			return int(self._nstep)

		elif self._step and self._nstep is None:
			if self.log_mode:
				c = decimal.ExtendedContext.copy()
				max = c.power(decimal.Decimal('10'), self.max)
				min = c.power(decimal.Decimal('10'), self.min)
				return int((max - min)/self._step) + 1
			else:
				return int((self.max - self.min)/self._step) + 1

	def set_nstep(self, value):
		self._nstep = int(value)

	def __str__(self):
		if self.unit:
			return "%s is defined between [%g, %g] %s in %d steps." % (self.name, self.min, self.max, self.unit, self.nstep)
		else:
			return "%s is defined between [%g, %g] in %d steps." % (self.name, self.min, self.max, self.nstep)

	__repr__ = __str__

	def get_values(self):
		self._values = map(lambda i: self.min + self.step*i, range(self.nstep))
		if self.log_mode:
			c = decimal.ExtendedContext.copy()
			self._values = map(lambda value: c.power(decimal.Decimal("10"), value), self._values)
		return self._values

	def set_values(self, values):
		self._values = values
		self._min = dec(min(self._values))
		self._max = dec(max(self._values))

	step   = property(get_step , set_step)
	nstep  = property(get_nstep, set_nstep)
	min    = property(get_min  , set_min)
	max    = property(get_max  , set_max)
	unit   = property(get_unit , set_unit)
	values = property(get_values, set_values)
		

# vim: set noet fdm=indent:
