<?php
/**
 * ADSR HAMILTONIAN — Wave Modulator
 * 
 * Attack, Decay, Sustain, Release.
 * The envelope of all phase transitions.
 * Zero energy loss during shifting — Hamiltonian conservation.
 */
class ADSRHamiltonian
{
    private float $attack;
    private float $decay;
    private float $sustain;
    private float $release;
    private float $currentAmplitude;
    private string $phase; // 'attack', 'decay', 'sustain', 'release', 'idle'
    private float $phaseStartTime;
    private float $pdtc;

    public function __construct(array $constants)
    {
        $adsr = $constants['adsr'];
        $this->attack = $adsr['attack'];
        $this->decay = $adsr['decay'];
        $this->sustain = $adsr['sustain'];
        $this->release = $adsr['release'];
        $this->pdtc = $constants['pdtc'];
        $this->currentAmplitude = 0.0;
        $this->phase = 'idle';
        $this->phaseStartTime = microtime(true);
    }

    /**
     * Trigger a new wave — begins Attack phase.
     */
    public function trigger(float $peakAmplitude = 1.0): void
    {
        $this->currentAmplitude = $peakAmplitude;
        $this->phase = 'attack';
        $this->phaseStartTime = microtime(true);
    }

    /**
     * Release the current wave.
     */
    public function release(): void
    {
        $this->phase = 'release';
        $this->phaseStartTime = microtime(true);
    }

    /**
     * Compute the current amplitude based on elapsed time.
     */
    public function compute(): float
    {
        $elapsed = microtime(true) - $this->phaseStartTime;

        switch ($this->phase) {
            case 'attack':
                if ($elapsed >= $this->attack) {
                    $this->phase = 'decay';
                    $this->phaseStartTime = microtime(true);
                    return 1.0;
                }
                return min(1.0, $elapsed / $this->attack);

            case 'decay':
                if ($elapsed >= $this->decay) {
                    $this->phase = 'sustain';
                    $this->phaseStartTime = microtime(true);
                    return $this->sustain;
                }
                $decayProgress = $elapsed / $this->decay;
                return 1.0 - ($decayProgress * (1.0 - $this->sustain));

            case 'sustain':
                return $this->sustain;

            case 'release':
                $releaseProgress = min(1.0, $elapsed / $this->release);
                $amplitude = $this->sustain * (1.0 - $releaseProgress);
                if ($releaseProgress >= 1.0) {
                    $this->phase = 'idle';
                    $this->currentAmplitude = 0.0;
                    return 0.0;
                }
                return $amplitude;

            case 'idle':
            default:
                return 0.0;
        }
    }

    /**
     * Modulate a signal through the ADSR envelope.
     * Returns the modulated value.
     */
    public function modulate(float $signal): float
    {
        $amplitude = $this->compute();
        return $signal * $amplitude;
    }

    /**
     * Compute Hamiltonian energy conservation.
     * H = ∫|ψ|² dt = constant (zero loss)
     */
    public function computeHamiltonian(): float
    {
        // The integral of the ADSR envelope squared
        // Attack: ∫₀ᴬ t² dt = A³/3
        // Decay: ∫₀ᴰ (1 - (1-S)t/D)² dt
        // Sustain: ∫S² dt = S²·∞ (bounded by observation window)
        // Release: ∫₀ᴿ S²(1-t/R)² dt = S²R/3

        $a = $this->attack;
        $d = $this->decay;
        $s = $this->sustain;
        $r = $this->release;

        $hAttack = pow($a, 3) / 3.0;
        $hDecay = $d * (1.0 + $s + $s * $s) / 3.0;
        $hSustain = $s * $s; // instantaneous
        $hRelease = $r * $s * $s / 3.0;

        return $hAttack + $hDecay + $hSustain + $hRelease;
    }

    /**
     * Generate a wave sample at a given frequency and time.
     */
    public function generateWave(float $frequency, float $time, string $type = 'sine'): float
    {
        $phase = 2.0 * M_PI * $frequency * $time;
        $amplitude = $this->compute();

        switch ($type) {
            case 'sine':
                return $amplitude * sin($phase);
            case 'saw':
                return $amplitude * (2.0 * ($frequency * $time - floor(0.5 + $frequency * $time)));
            case 'square':
                return $amplitude * (sin($phase) >= 0 ? 1.0 : -1.0);
            case 'triangle':
                return $amplitude * (2.0 * abs(2.0 * ($frequency * $time - floor($frequency * $time + 0.5))) - 1.0);
            default:
                return $amplitude * sin($phase);
        }
    }

    /**
     * Serialize current state.
     */
    public function serialize(): array
    {
        return [
            'attack' => $this->attack,
            'decay' => $this->decay,
            'sustain' => $this->sustain,
            'release' => $this->release,
            'current_amplitude' => round($this->compute(), 6),
            'phase' => $this->phase,
            'hamiltonian' => round($this->computeHamiltonian(), 6),
        ];
    }
}
