package dsp

import "math"

// PreEmphasis implements a first-order high-shelf filter for FM broadcast
// pre-emphasis. Standard time constants are 50 µs (Europe/world) and
// 75 µs (North America/South Korea).
//
// Transfer function: H(s) = 1 + s*τ
// Bilinear transform to discrete: H(z) = (b0 + b1*z^-1) / (1 + a1*z^-1)
type PreEmphasis struct {
	b0, b1  float64
	x1      float64 // state
	enabled bool
}

// NewPreEmphasis creates a pre-emphasis filter for the given time constant
// and sample rate. tau is in microseconds (50 or 75).
func NewPreEmphasis(tauMicroseconds, sampleRate float64) *PreEmphasis {
	if tauMicroseconds <= 0 || sampleRate <= 0 {
		return &PreEmphasis{enabled: false}
	}
	tau := tauMicroseconds * 1e-6

	// Bilinear transform of H(s) = 1 + s*tau
	// With pre-warping: let c = 2*fs
	// Numerator: (1 + tau*c) + (-1 + tau*c)*z^-1  =>  b0 = 1+tau*c, b1 = -1+tau*c
	// Denominator: (1 + tau*c) + (1 - tau*c)*z^-1  (but we normalize so a0=1)
	// Wait - cleaner approach: standard first-order shelf.

	// H(s) = (1 + s*tau) mapped with bilinear: s = c*(1 - z^-1)/(1 + z^-1), c = 2*fs
	// De-emphasis: H_de(z) = (1-alpha)/(1 - alpha*z^-1), alpha = exp(-1/(tau*fs))
	// Pre-emphasis: H_pre(z) = 1/H_de(z) = (1 - alpha*z^-1)/(1-alpha)
	// y[n] = (x[n] - alpha*x[n-1]) / (1 - alpha)
	alpha := math.Exp(-1.0 / (tau * sampleRate))
	gain := 1.0 / (1.0 - alpha)

	return &PreEmphasis{
		b0:      gain,
		b1:      -alpha * gain,
		enabled: true,
	}
}

// Process applies the pre-emphasis filter to a single sample.
func (p *PreEmphasis) Process(in float64) float64 {
	if !p.enabled {
		return in
	}
	out := p.b0*in + p.b1*p.x1
	p.x1 = in
	return out
}

// Reset clears the filter state.
func (p *PreEmphasis) Reset() {
	p.x1 = 0
}

// DeEmphasis implements the complementary de-emphasis filter.
// H(z) = (1-alpha)/(1 - alpha*z^-1)
type DeEmphasis struct {
	alpha   float64
	gain    float64
	prevOut float64
	enabled bool
}

// NewDeEmphasis creates a de-emphasis filter.
func NewDeEmphasis(tauMicroseconds, sampleRate float64) *DeEmphasis {
	if tauMicroseconds <= 0 || sampleRate <= 0 {
		return &DeEmphasis{enabled: false}
	}
	tau := tauMicroseconds * 1e-6
	alpha := math.Exp(-1.0 / (tau * sampleRate))
	return &DeEmphasis{alpha: alpha, gain: 1.0 - alpha, enabled: true}
}

// Process applies the de-emphasis filter.
func (d *DeEmphasis) Process(in float64) float64 {
	if !d.enabled {
		return in
	}
	out := d.gain*in + d.alpha*d.prevOut
	d.prevOut = out
	return out
}

// Reset clears the filter state.
func (d *DeEmphasis) Reset() {
	d.prevOut = 0
}
