package udp

import (
	"bytes"
	"encoding/binary"
	"fmt"
	"strings"

	"hamlog/internal/applog"
)

// WSJT-X Free Text (message type 9) — "put this in the free-text box, and send".
//
//	Free Text  type 9
//	  id    utf8  the target application's own id
//	  text  utf8  the message
//	  send  bool  true = transmit it now, false = only fill the box
//
// What this is FOR, and what it is not:
//
// It is the one "transmit this now" primitive that behaves the same on WSJT-X,
// WSJT-Z, JTDX, JTDX-Improved and MSHV, because it needs no decode to match
// against — unlike Reply, which the receiving application will only action if it
// can find the exact decode being answered.
//
// It is NOT a way to call a station. FT8 free text is 13 characters, so a
// standard "<DX> <ME> <GRID>" does not fit, and more importantly the text goes
// out AS free text without setting the DX Call — so the sequencer never takes
// over, and no report, RR73 or logging prompt follows. Use it for a 73, a short
// note or a CQ; use Reply to start a QSO.
const wsjtMsgFreeText = 9

// EncodeFreeText builds the datagram. send=false fills the box without keying.
func EncodeFreeText(programID, text string, send bool) []byte {
	var b bytes.Buffer
	_ = binary.Write(&b, binary.BigEndian, uint32(wsjtMagic))
	_ = binary.Write(&b, binary.BigEndian, uint32(2)) // schema 2 — what every current sender speaks
	_ = binary.Write(&b, binary.BigEndian, uint32(wsjtMsgFreeText))
	writeQString(&b, programID)
	writeQString(&b, text)
	var s uint8
	if send {
		s = 1
	}
	_ = binary.Write(&b, binary.BigEndian, s)
	return b.Bytes()
}

// SendFreeText hands a free-text message to one decoding application.
//
// Routed by program id to the address that instance's packets arrive from, for
// the same reason as SendReply and SendHaltTx: two receivers can share one
// multicast group, and transmitting from the wrong one is worse than not
// transmitting at all.
func (m *Manager) SendFreeText(programID, text string, send bool) error {
	if strings.TrimSpace(programID) == "" {
		return fmt.Errorf("no application id — cannot tell which receiver to send with")
	}
	m.mu.Lock()
	servers := make([]*Server, 0, len(m.inbound))
	for _, s := range m.inbound {
		servers = append(servers, s)
	}
	m.mu.Unlock()

	for _, s := range servers {
		conn, addr := s.replyTarget(programID)
		if conn == nil || addr == nil {
			continue
		}
		if _, err := conn.WriteToUDP(EncodeFreeText(programID, text, send), addr); err != nil {
			return fmt.Errorf("send free text to %s at %s: %w", programID, addr, err)
		}
		applog.Printf("udp: free text sent to %s at %s — %q (send=%v)", programID, addr, text, send)
		return nil
	}
	return fmt.Errorf("no packet has arrived from %q yet — nothing to send to", programID)
}
