# -*- coding: utf-8 -*-
"""
API de alto nivel - Analizador de audio.

FFT, niveles, BPM, LUFS con BASSloud.
"""

import ctypes
from ..dlls.bass import bass
from ..constantes.bass import (
	BASS_DATA_FFT256, BASS_DATA_FFT512, BASS_DATA_FFT1024,
	BASS_DATA_FFT2048, BASS_DATA_FFT4096, BASS_DATA_FFT8192,
	BASS_DATA_FLOAT, BASS_LEVEL_STEREO, BASS_LEVEL_RMS,
)
from ..constantes.addons import (
	BASS_LOUDNESS_CURRENT, BASS_LOUDNESS_INTEGRATED,
	BASS_LOUDNESS_RANGE, BASS_LOUDNESS_PEAK, BASS_LOUDNESS_TRUEPEAK,
)

FFT_SIZES = {128: BASS_DATA_FFT256, 256: BASS_DATA_FFT512, 512: BASS_DATA_FFT1024,
	1024: BASS_DATA_FFT2048, 2048: BASS_DATA_FFT4096, 4096: BASS_DATA_FFT8192}


class Analizador:
	"""
	Analizador de audio en tiempo real.

	Ejemplo:
		analizador = Analizador(stream)
		nivel_izq, nivel_der = analizador.nivel()
		espectro = analizador.fft(512)
		bpm = analizador.detectar_bpm()
		lufs = analizador.loudness_integrada()
	"""

	def __init__(self, canal: int = 0):
		self._canal = canal
		self._loudness_handle = 0

	@property
	def canal(self) -> int:
		return self._canal

	@canal.setter
	def canal(self, value: int):
		self.detener_loudness()
		self._canal = value

	def nivel(self) -> tuple[float, float]:
		"""Retorna nivel (izq, der) de 0.0 a 1.0."""
		level = bass.BASS_ChannelGetLevel(self._canal)
		if level == 0xFFFFFFFF:
			return (0.0, 0.0)
		izq = (level & 0xFFFF) / 32768.0
		der = ((level >> 16) & 0xFFFF) / 32768.0
		return (izq, der)

	def nivel_rms(self) -> tuple[float, float]:
		"""Retorna nivel RMS (izq, der)."""
		levels = (ctypes.c_float * 2)()
		ok = bass.BASS_ChannelGetLevelEx(self._canal, levels, 0.02, BASS_LEVEL_STEREO | BASS_LEVEL_RMS)
		if ok == 0:
			return (0.0, 0.0)
		return (levels[0], levels[1])

	def fft(self, bandas: int = 512) -> list[float]:
		"""Retorna espectro FFT como lista de floats."""
		flag = FFT_SIZES.get(bandas, BASS_DATA_FFT1024)
		buf = (ctypes.c_float * bandas)()
		leidos = bass.BASS_ChannelGetData(self._canal, ctypes.byref(buf), flag)
		if leidos == 0xFFFFFFFF:
			return [0.0] * bandas
		return list(buf)

	def detectar_bpm(self, inicio: float = 0, fin: float = 30) -> float:
		"""Detecta BPM usando BASS_FX (requiere stream DECODE)."""
		try:
			from ..dlls.bass_fx import bass_fx
			from ..callbacks.addons import BPMPROCESSPROC
			@BPMPROCESSPROC
			def _noop(chan, percent):
				pass
			bpm = bass_fx.BASS_FX_BPM_DecodeGet(self._canal, inicio, fin, 0, 0, _noop)
			return bpm
		except Exception:
			return 0.0

	def iniciar_loudness(self, flags: int = 0):
		"""Inicia medicion de loudness EBU R128."""
		try:
			from ..dlls.addons import bassloud
			flags_total = BASS_LOUDNESS_CURRENT | BASS_LOUDNESS_INTEGRATED | BASS_LOUDNESS_RANGE | BASS_LOUDNESS_TRUEPEAK
			self._loudness_handle = bassloud.BASS_Loudness_Start(self._canal, flags_total, 0)
		except Exception:
			self._loudness_handle = 0

	def loudness_actual(self) -> float:
		"""Retorna LUFS momentaneo."""
		return self._leer_loudness(BASS_LOUDNESS_CURRENT)

	def loudness_integrada(self) -> float:
		"""Retorna LUFS integrada."""
		return self._leer_loudness(BASS_LOUDNESS_INTEGRATED)

	def loudness_range(self) -> float:
		"""Retorna rango de loudness (LRA)."""
		return self._leer_loudness(BASS_LOUDNESS_RANGE)

	def true_peak(self) -> float:
		"""Retorna true peak en dBFS."""
		return self._leer_loudness(BASS_LOUDNESS_TRUEPEAK)

	def _leer_loudness(self, tipo: int) -> float:
		if not self._loudness_handle:
			return -70.0
		try:
			from ..dlls.addons import bassloud
			val = ctypes.c_float(-70.0)
			bassloud.BASS_Loudness_GetLevel(self._loudness_handle, tipo, ctypes.byref(val))
			return val.value
		except Exception:
			return -70.0

	def detener_loudness(self):
		"""Detiene medicion de loudness."""
		if self._loudness_handle:
			try:
				from ..dlls.addons import bassloud
				bassloud.BASS_Loudness_Stop(self._loudness_handle)
			except Exception:
				pass
			self._loudness_handle = 0

	def __del__(self):
		self.detener_loudness()

	def __repr__(self):
		return f"<Analizador canal={self._canal}>"
