# Standard modules
from dataclasses import dataclass
#from dataclasses_json import dataclass_json
from dataclasses import asdict as dc_asdict
from typing import Optional, List

# Kaipy modules
import kaipy.rcm.lambdautils.DistTypes as dT

# dataclasses_json isn't a default package. Since its only used for reading, don't want to make it a requirement for everyone
try:
    from dataclasses_json import dataclass_json
    dataclasses_json_module_imported = True
except ModuleNotFoundError:
    dataclass_json = None
    dataclasses_json_module_imported = False

def conditional_decorator(dec, dataclasses_json_module_imported):
	"""
	A decorator that conditionally applies another decorator based on the availability of the dataclasses_json module.

	Args:
		dec: The decorator to be applied.
		dataclasses_json_module_imported: A boolean value indicating whether the dataclasses_json module is imported.

	Returns:
		The decorated function if the dataclasses_json module is imported, otherwise returns the function unchanged.
	"""
	def decorator(func):
		if not dataclasses_json_module_imported:
			# Return the function unchanged, not decorated.
			return func
		return dec(func)
	return decorator


# Defines a single species
@dataclass
class SpecParams:
	"""Defines a single species.

	A full species parameter set is defined by both the params listed here AND the ones in whatever DistType is chosen.

	Attributes:
		n (int): Number of channels.
		amin (float): Lower lambda bound for species.
		amax (float): Upper lambda bound for species.
		distType (dT.DistType): DistType params used to generate final lambda distribution.
		flav (int): "Flavor", used to distinguish species types in RCM.
					1 = electrons, 2 = protons.
		fudge (Optional[float], optional): "Fudge factor" loss ratio. Defaults to 0.
		name (Optional[str], optional): Name of the species. Defaults to None.
	"""

	n: int  # Number of channels
	amin: float  # Lower lambda bound for species
	amax: float  # Upper lambda bound for species
	distType: dT.DistType  # DistType params used to generate final lambda distribution
	flav: int  # "Flavor", used to distinguish species types in RCM
			   # 1 = electrons, 2 = protons
	fudge: Optional[float] = 0  # "Fudge factor" loss ratio
	name: Optional[str] = None

	def genAlams(self):
		"""Generate alams based on the given DistType's rules.

		Returns:
			specData: Data generated by the DistType's 'genAlamsFromSpecies' function.
		"""
		specData = self.distType.genAlamsFromSpecies(self)
		return specData

#@dataclass_json
@conditional_decorator(dataclass_json, dataclasses_json_module_imported)
@dataclass
class AlamParams:
	"""
	Class representing the parameters for Alam dataset generation.

	Attributes:
		doUsePsphere (bool): Whether or not the resulting dataset will include a zero-energy plasmasphere channel.
		specParams (List[SpecParams]): List of all specParams to be included in the dataset.
		emine (Optional[float]): Electron min energy in eV.
		eminp (Optional[float]): Proton min energy in eV.
		emaxe (Optional[float]): Electron max energy in eV.
		emaxp (Optional[float]): Proton max energy in eV.
		L_kt (Optional[float]): L value that e/p min/maxes correspond to.
	"""
	doUsePsphere: bool
	specParams: List[SpecParams]
	# These can help to determine some things for higher-level lambda generators
	#tiote: Optional[float] = None  # Ratio of ion temperature to electron temperature
	#ktMax: Optional[float] = None  # Energy in eV
	emine: Optional[float] = None
	eminp: Optional[float] = None
	emaxe: Optional[float] = None
	emaxp: Optional[float] = None
	L_kt : Optional[float] = None
