# encoding=utf-8
"""
Class used to encapsulate some calculations in parselmouth
"""

__author__ = "Aaron Randreth"
__copyright__ = "Copyright 2015+, Consortium MonPaGe"
__license__ = "Creative Commons 4.0 By-Nc-Sa"
__maintainer__ = "Roland Trouville"
__email__ = "contact.monpage@gmail.com"
__status__ = "Production"

from typing import Optional
import numpy
import parselmouth  # type: ignore

PRAAT_FULL_FILE: float = 0.0
PITCH_UNIT: str = "Hertz"
MINIMUM_INTERPOLATION_TYPE: str = "Parabolic"


class ParselmouthIntensity:
	"""
	A wrapper around a Parselmouth Intensity object to analyze intensity metrics within a specified time interval.

	Attributes:
	    intensity (parselmouth.Intensity): The Parselmouth Intensity object derived from a sound.
	    start_time (float): The start time (in seconds) of the analysis interval.
	    end_time (float): The end time (in seconds) of the analysis interval.
	    WEAK_SNR_CEILING (int): Threshold below which the signal-to-noise ratio (SNR) is considered weak.
	    UNUSABLE_SNR_CEILING (int): Threshold below which the SNR is considered unusable.
	"""

	intensity: parselmouth.Intensity
	# weak_snr = bad sound quality

	WEAK_SNR_CEILING = 40
	UNUSABLE_SNR_CEILING = 30

	def __init__(
		self,
		sound: parselmouth.Sound,
		start_time: float,
		end_time: float = PRAAT_FULL_FILE,
		minimum_pitch: float = 100.0,
		time_step: Optional[float] = None,  # time_step > 0
		subtract_mean: bool = True,
	):
		self.intensity = sound.to_intensity(minimum_pitch, time_step, subtract_mean)

		self.start_time = start_time
		self.end_time = end_time

	def get_minimum(self) -> float:
		"""
		Get the minimum intensity value within the specified time range.

		Returns:
			float: The minimum intensity value obtained using a parabolic method.
		"""
		return parselmouth.praat.call(
			self.intensity, "Get minimum", self.start_time, self.end_time, "Parabolic"
		)

	def get_maximum(self) -> float:
		"""
		Get the maximum intensity value within the specified time range.

		Returns:
			float: The maximum intensity value obtained using a parabolic method.
		"""
		return parselmouth.praat.call(
			self.intensity, "Get maximum", self.start_time, self.end_time, "Parabolic"
		)

	def get_signal_to_noise_ratio(self) -> float:
		"""
		Calculate the signal-to-noise ratio (SNR) as the difference between maximum and minimum intensity.

		Returns:
			float: The signal-to-noise ratio (SNR) value.
		"""
		noise: float = self.get_minimum()
		signal: float = self.get_maximum()

		return signal - noise

	def get_is_weak_snr(self) -> bool:
		"""
		Determine if the signal-to-noise ratio is considered weak.

		Returns:
			bool: True if SNR is below the weak SNR threshold, False otherwise.
		"""
		return self.get_signal_to_noise_ratio() < self.WEAK_SNR_CEILING

	def get_is_unusable_snr(self) -> bool:
		"""
		Determine if the signal-to-noise ratio is considered unusable.

		Returns:
			bool: True if SNR is below the unusable SNR threshold, False otherwise.
		"""
		return self.get_signal_to_noise_ratio() < self.UNUSABLE_SNR_CEILING

	def get_plot_values(self) -> tuple[numpy.ndarray, numpy.ndarray]:
		"""
		Get filtered time and frequency values for plotting intensity data.

		Filters values to include only those within the time interval [start_time, end_time]
		and frequency range (0, 1000). Points outside the frequency range are replaced with NaN.

		Returns:
			tuple: Two lists containing filtered times and frequencies respectively.
		"""
		times = self.intensity.xs()
		frequencies = self.intensity.values[0]

		filtered_times = []
		filtered_frequencies = []

		floor, ceil = 0, 1000

		for time, frequency in zip(times, frequencies):
			in_interval = self.start_time <= time and time <= self.end_time

			if not in_interval:
				continue

			in_range = floor < frequency and frequency < ceil

			if not in_range:
				filtered_times.append(float("NaN"))
				filtered_frequencies.append(float("NaN"))
				continue

			filtered_times.append(time)
			filtered_frequencies.append(frequency)

		return filtered_times, filtered_frequencies
