package stereo

import (
	"math"

	"github.com/jan/fm-rds-tx/internal/audio"
	"github.com/jan/fm-rds-tx/internal/dsp"
)

// Components holds the individual MPX components produced by the stereo encoder.
// All outputs are unity-normalized. The combiner controls actual injection levels.
type Components struct {
	Mono   float64 // (L+R)/2 baseband
	Stereo float64 // (L-R)/2 * sin(2 * pilotPhase), unity subcarrier
	Pilot  float64 // sin(pilotPhase), unity amplitude
}

// StereoEncoder generates stereo MPX primitives from stereo audio frames.
// The 38 kHz subcarrier is derived from the pilot phase (2× multiplication),
// guaranteeing perfect phase coherence as required by the FM stereo standard.
type StereoEncoder struct {
	pilot     dsp.PilotGenerator
	lastPhase float64 // phase captured in last Encode(), for coherent RDS carrier
}

// NewStereoEncoder creates a StereoEncoder configured for the provided sample rate.
func NewStereoEncoder(sampleRate float64) StereoEncoder {
	return StereoEncoder{
		pilot: dsp.NewPilotGenerator(sampleRate),
	}
}

// Encode converts a stereo frame into MPX components.
// The 38 kHz subcarrier is sin(2*pilotPhase), derived directly from the pilot
// oscillator's phase — not from a separate oscillator.
func (s *StereoEncoder) Encode(frame audio.Frame) Components {
	// Capture phase BEFORE advancing — the 38 kHz subcarrier must use the
	// same phase instant as the pilot sample to maintain coherence.
	pilotPhase := s.pilot.Phase()
	s.lastPhase = pilotPhase
	pilot := s.pilot.Sample() // sin(2π * 19000 * t), then advances phase

	// 38 kHz subcarrier = sin(2 * pilotPhase * 2π) = sin(4π * 19000 * t)
	// This is mathematically identical to sin(2π * 38000 * t) but guaranteed
	// phase-locked to the pilot. FM receivers PLL onto the pilot and derive
	// the 38 kHz reference this exact same way.
	sub38 := math.Sin(2 * math.Pi * 2 * pilotPhase)

	return Components{
		Mono:   float64(frame.Mono()),
		Stereo: float64(frame.Difference()) * sub38,
		Pilot:  pilot,
	}
}

// Reset restarts the pilot generator.
func (s *StereoEncoder) Reset() {
	s.pilot.Reset()
}

// PilotPhase returns the pilot phase used in the most recent Encode() call.
// This is the coherent phase instant for deriving subcarriers (38 kHz = 2×, 57 kHz = 3×).
func (s *StereoEncoder) PilotPhase() float64 {
	return s.lastPhase
}

// RDSCarrier returns sin(3 * pilotPhase * 2π) — the 57 kHz carrier
// phase-locked to the pilot, as required by the RDS standard.
// Uses the phase captured in the most recent Encode() call so that
// pilot, 38 kHz subcarrier, and 57 kHz RDS carrier are all coherent.
func (s *StereoEncoder) RDSCarrier() float64 {
	return math.Sin(2 * math.Pi * 3 * s.lastPhase)
}
