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

Gestiona efectos DX8 y BASS_FX de forma sencilla con presets.
"""

import ctypes
from ..dlls.bass import bass
from ..constantes.bass import (
	BASS_FX_DX8_CHORUS, BASS_FX_DX8_COMPRESSOR, BASS_FX_DX8_DISTORTION,
	BASS_FX_DX8_ECHO, BASS_FX_DX8_FLANGER, BASS_FX_DX8_GARGLE,
	BASS_FX_DX8_I3DL2REVERB, BASS_FX_DX8_PARAMEQ, BASS_FX_DX8_REVERB,
	BASS_FX_VOLUME,
)
from ..constantes.bass_fx import (
	BASS_FX_BFX_ROTATE, BASS_FX_BFX_ECHO, BASS_FX_BFX_FLANGER,
	BASS_FX_BFX_VOLUME, BASS_FX_BFX_PEAKEQ, BASS_FX_BFX_REVERB,
	BASS_FX_BFX_LPF, BASS_FX_BFX_MIX, BASS_FX_BFX_DAMP,
	BASS_FX_BFX_AUTOWAH, BASS_FX_BFX_ECHO2, BASS_FX_BFX_PHASER,
	BASS_FX_BFX_ECHO3, BASS_FX_BFX_CHORUS, BASS_FX_BFX_APF,
	BASS_FX_BFX_COMPRESSOR, BASS_FX_BFX_DISTORTION,
	BASS_FX_BFX_COMPRESSOR2, BASS_FX_BFX_VOLUME_ENV, BASS_FX_BFX_BQF,
)
from ..estructuras.bass import (
	BASS_DX8_CHORUS, BASS_DX8_COMPRESSOR, BASS_DX8_DISTORTION,
	BASS_DX8_ECHO, BASS_DX8_FLANGER, BASS_DX8_GARGLE,
	BASS_DX8_I3DL2REVERB, BASS_DX8_PARAMEQ, BASS_DX8_REVERB,
	BASS_FX_VOLUME_PARAM,
)
from ..estructuras.addons import (
	BASS_BFX_ECHO, BASS_BFX_FLANGER as BFX_FLANGER_S, BASS_BFX_VOLUME as BFX_VOL_S,
	BASS_BFX_PEAKEQ, BASS_BFX_REVERB as BFX_REVERB_S,
	BASS_BFX_LPF, BASS_BFX_DAMP, BASS_BFX_AUTOWAH,
	BASS_BFX_ECHO2, BASS_BFX_PHASER, BASS_BFX_ECHO3,
	BASS_BFX_CHORUS as BFX_CHORUS_S, BASS_BFX_APF,
	BASS_BFX_COMPRESSOR as BFX_COMP_S, BASS_BFX_DISTORTION as BFX_DIST_S,
	BASS_BFX_COMPRESSOR2, BASS_BFX_BQF,
)

# Mapa nombre -> (constante_fx, struct_class, defaults)
EFECTOS_DX8 = {
	"chorus": (BASS_FX_DX8_CHORUS, BASS_DX8_CHORUS, {"fWetDryMix": 50, "fDepth": 10, "fFeedback": 25, "fFrequency": 1.1, "lWaveform": 1, "fDelay": 16, "lPhase": 3}),
	"compressor_dx8": (BASS_FX_DX8_COMPRESSOR, BASS_DX8_COMPRESSOR, {"fGain": 0, "fAttack": 10, "fRelease": 200, "fThreshold": -20, "fRatio": 3, "fPredelay": 4}),
	"distortion_dx8": (BASS_FX_DX8_DISTORTION, BASS_DX8_DISTORTION, {"fGain": -18, "fEdge": 15, "fPostEQCenterFrequency": 2400, "fPostEQBandwidth": 2400, "fPreLowpassCutoff": 8000}),
	"echo": (BASS_FX_DX8_ECHO, BASS_DX8_ECHO, {"fWetDryMix": 50, "fFeedback": 50, "fLeftDelay": 250, "fRightDelay": 250, "lPanDelay": 0}),
	"flanger_dx8": (BASS_FX_DX8_FLANGER, BASS_DX8_FLANGER, {"fWetDryMix": 50, "fDepth": 100, "fFeedback": -50, "fFrequency": 0.25, "lWaveform": 1, "fDelay": 2, "lPhase": 2}),
	"gargle": (BASS_FX_DX8_GARGLE, BASS_DX8_GARGLE, {"dwRateHz": 20, "dwWaveShape": 0}),
	"i3dl2reverb": (BASS_FX_DX8_I3DL2REVERB, BASS_DX8_I3DL2REVERB, {"lRoom": -1000, "lRoomHF": -100, "flRoomRolloffFactor": 0, "flDecayTime": 1.49, "flDecayHFRatio": 0.83, "lReflections": -2602, "flReflectionsDelay": 0.007, "lReverb": 200, "flReverbDelay": 0.011, "flDiffusion": 100, "flDensity": 100, "flHFReference": 5000}),
	"parameq": (BASS_FX_DX8_PARAMEQ, BASS_DX8_PARAMEQ, {"fCenter": 1000, "fBandwidth": 12, "fGain": 0}),
	"reverb": (BASS_FX_DX8_REVERB, BASS_DX8_REVERB, {"fInGain": 0, "fReverbMix": -10, "fReverbTime": 1000, "fHighFreqRTRatio": 0.5}),
	"volume_dsp": (BASS_FX_VOLUME, BASS_FX_VOLUME_PARAM, {"fTarget": 1, "fCurrent": 1, "fTime": 0, "lCurve": 0}),
}

EFECTOS_BFX = {
	"bfx_echo": (BASS_FX_BFX_ECHO, BASS_BFX_ECHO, {"fLevel": 50, "lDelay": 200}),
	"bfx_flanger": (BASS_FX_BFX_FLANGER, BFX_FLANGER_S, {"fWetDry": 50, "fSpeed": 0.5, "lChannel": -1}),
	"bfx_reverb": (BASS_FX_BFX_REVERB, BFX_REVERB_S, {"fLevel": 50, "lDelay": 300}),
	"bfx_lpf": (BASS_FX_BFX_LPF, BASS_BFX_LPF, {"fResonance": 2, "fCutOffFreq": 4000, "lChannel": -1}),
	"bfx_damp": (BASS_FX_BFX_DAMP, BASS_BFX_DAMP, {"fTarget": 1, "fQuiet": 0.01, "fRate": 0.01, "fGain": 1, "fDelay": 0, "lChannel": -1}),
	"bfx_autowah": (BASS_FX_BFX_AUTOWAH, BASS_BFX_AUTOWAH, {"fDryMix": 0.5, "fWetMix": 1.5, "fFeedback": 0.5, "fRate": 2, "fRange": 4.3, "fFreq": 50, "lChannel": -1}),
	"bfx_echo2": (BASS_FX_BFX_ECHO2, BASS_BFX_ECHO2, {"fDryMix": 0.9, "fWetMix": 0.7, "fFeedback": 0.5, "fDelay": 0.3, "lChannel": -1}),
	"bfx_phaser": (BASS_FX_BFX_PHASER, BASS_BFX_PHASER, {"fDryMix": 0.9, "fWetMix": 0.7, "fFeedback": 0.5, "fRate": 1, "fRange": 4, "fFreq": 50, "lChannel": -1}),
	"bfx_echo3": (BASS_FX_BFX_ECHO3, BASS_BFX_ECHO3, {"fDryMix": 0.9, "fWetMix": 0.7, "fDelay": 0.3, "lChannel": -1}),
	"bfx_chorus": (BASS_FX_BFX_CHORUS, BFX_CHORUS_S, {"fDryMix": 0.9, "fWetMix": 0.35, "fFeedback": 0.5, "fMinSweep": 1, "fMaxSweep": 5, "fRate": 1, "lChannel": -1}),
	"bfx_apf": (BASS_FX_BFX_APF, BASS_BFX_APF, {"fGain": 0.5, "fDelay": 0.5, "lChannel": -1}),
	"bfx_compressor": (BASS_FX_BFX_COMPRESSOR, BFX_COMP_S, {"fThreshold": 0.5, "fAttacktime": 10, "fReleasetime": 200, "lChannel": -1}),
	"bfx_distortion": (BASS_FX_BFX_DISTORTION, BFX_DIST_S, {"fDrive": 0.2, "fDryMix": 0.9, "fWetMix": 0.1, "fFeedback": 0, "fVolume": 1, "lChannel": -1}),
	"bfx_compressor2": (BASS_FX_BFX_COMPRESSOR2, BASS_BFX_COMPRESSOR2, {"fGain": 5, "fThreshold": -20, "fRatio": 4, "fAttack": 10, "fRelease": 200, "lChannel": -1}),
	"bfx_bqf": (BASS_FX_BFX_BQF, BASS_BFX_BQF, {"lFilter": 0, "fCenter": 1000, "fGain": 0, "fBandwidth": 2.5, "fQ": 0, "fS": 0, "lChannel": -1}),
}

TODOS_LOS_EFECTOS = {**EFECTOS_DX8, **EFECTOS_BFX}


class CadenaEfectos:
	"""
	Cadena de efectos de audio de alto nivel.

	Ejemplo:
		cadena = CadenaEfectos(stream_handle)
		cadena.agregar("reverb")
		cadena.agregar("echo", fFeedback=60)
		cadena.agregar("bfx_phaser")
		cadena.bypass("reverb", True)
		cadena.quitar("echo")
		cadena.limpiar()
	"""

	def __init__(self, canal: int = 0):
		self._canal = canal
		self._activos: dict[str, dict] = {}

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

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

	def agregar(self, nombre: str, **params) -> bool:
		"""Agrega un efecto por nombre con parametros opcionales."""
		if nombre in self._activos:
			return False
		if nombre not in TODOS_LOS_EFECTOS:
			raise ValueError(f"Efecto desconocido: {nombre}. Disponibles: {list(TODOS_LOS_EFECTOS.keys())}")
		tipo_fx, struct_cls, defaults = TODOS_LOS_EFECTOS[nombre]
		handle = bass.BASS_ChannelSetFX(self._canal, tipo_fx, 0)
		if handle == 0:
			return False
		p = struct_cls()
		valores = {**defaults, **params}
		for k, v in valores.items():
			if hasattr(p, k):
				setattr(p, k, v)
		bass.BASS_FXSetParameters(handle, ctypes.byref(p))
		self._activos[nombre] = {"handle": handle, "tipo": tipo_fx, "struct": struct_cls, "params": valores}
		return True

	def quitar(self, nombre: str) -> bool:
		"""Quita un efecto activo."""
		if nombre not in self._activos:
			return False
		bass.BASS_ChannelRemoveFX(self._canal, self._activos[nombre]["handle"])
		del self._activos[nombre]
		return True

	def bypass(self, nombre: str, activar: bool) -> bool:
		"""Activa/desactiva bypass de un efecto."""
		if nombre not in self._activos:
			return False
		try:
			return bass.BASS_FXSetBypass(self._activos[nombre]["handle"], 1 if activar else 0) != 0
		except Exception:
			return False

	def actualizar(self, nombre: str, **params) -> bool:
		"""Actualiza parametros de un efecto activo."""
		if nombre not in self._activos:
			return False
		info = self._activos[nombre]
		info["params"].update(params)
		p = info["struct"]()
		for k, v in info["params"].items():
			if hasattr(p, k):
				setattr(p, k, v)
		return bass.BASS_FXSetParameters(info["handle"], ctypes.byref(p)) != 0

	def reset(self, nombre: str) -> bool:
		"""Resetea el estado interno del efecto."""
		if nombre not in self._activos:
			return False
		try:
			return bass.BASS_FXReset(self._activos[nombre]["handle"]) != 0
		except Exception:
			return False

	def limpiar(self):
		"""Quita todos los efectos."""
		for nombre in list(self._activos):
			self.quitar(nombre)

	@property
	def activos(self) -> list[str]:
		return list(self._activos.keys())

	@staticmethod
	def efectos_disponibles() -> list[str]:
		return list(TODOS_LOS_EFECTOS.keys())

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