package main

// The voice keyer, through the radio's own link.
//
// Selecting the radio as the "To radio" output makes the voice keyer hand its
// messages to the CAT backend instead of a sound card. Everything around it is
// unchanged — the same PTT before and after, the same gain, the same files —
// which is the point: the audio takes a different road, not a different route.

import (
	"fmt"

	"hamlog/internal/applog"
	"hamlog/internal/audio"
	"hamlog/internal/cat"
)

// tciTXPlayer hands one message to the radio.
//
// The controller is fetched on the CAT goroutine and the message is then played
// OFF it. Playing on it would hold that goroutine for the length of the
// message, and everything else about the rig — frequency, mode, PTT state —
// goes through the same place: a ten-second call would freeze the display and
// the antenna following for ten seconds. The TCI backend serialises its own
// writes, so this is safe to call from here.
func (a *App) tciTXPlayer(pcm []byte, rate, ch, bits int, stop <-chan struct{}) error {
	if a.cat == nil {
		return fmt.Errorf("CAT not initialized")
	}
	type txPlayer interface {
		PlayTXAudio(pcm []byte, rate, ch, bits int, stop <-chan struct{}) error
	}
	var player txPlayer
	err := a.cat.TCIAudioDo(func(t cat.TCIAudioController) error {
		p, ok := t.(txPlayer)
		if !ok {
			return fmt.Errorf("this radio cannot take transmit audio over its CAT link")
		}
		player = p
		return nil
	})
	if err != nil {
		return err
	}
	return player.PlayTXAudio(pcm, rate, ch, bits, stop)
}

// applyTCITXSource tells the radio where to take its transmit audio from,
// following the "To radio" device.
//
// This is the setting an operator would otherwise have to find in ExpertSDR3
// and set again for every mode, because it is remembered per mode there. It is
// the third argument of TRX, so OpsLog can simply say it each time it keys —
// and a radio told nothing keeps the operator's microphone, which is what
// every PTT that is not a voice message should do.
func (a *App) applyTCITXSource() {
	if a.cat == nil {
		return
	}
	cfg, _ := a.GetAudioSettings()
	src := ""
	if cfg.ToRadio == audio.NetworkDeviceID {
		src = "tci"
	}
	_ = a.cat.TCIAudioDo(func(t cat.TCIAudioController) error {
		if s, ok := t.(interface{ SetTXAudioSource(string) }); ok {
			s.SetTXAudioSource(src)
		}
		return nil
	})
}

// installTCITXPlayer offers the radio as an audio output, or withdraws it.
//
// Withdrawing matters as much as offering: a radio that has gone away must stop
// being a device the voice keyer will happily "play" to, or a message goes
// nowhere and the operator hears their own PTT click and assumes it worked.
func (a *App) installTCITXPlayer(on bool) {
	if !on {
		audio.SetNetworkPlayer(nil)
		return
	}
	audio.SetNetworkPlayer(a.tciTXPlayer)
	a.applyTCITXSource()
	applog.Printf("tci: the radio is available as an audio output — no virtual cable needed for the voice keyer")
}
