package main

import "testing"

// A per-band tune frequency goes straight to the antenna as a command, so a
// value that is not actually in that band has to be refused rather than obeyed.
// One wrong digit sends the elements travelling to a length that is wrong for
// the band the operator is on — and on a SteppIR that journey inhibits transmit
// the whole way.
func TestNormMotorBandFreqsRefusesOutOfBand(t *testing.T) {
	in := map[string]int{
		"20m":   14050,    // fine, CW end
		"40m":   7005,     // fine
		"6m":    50313,    // fine, FT8
		"15m":   1450,     // a digit lost — lands in the broadcast band
		"10m":   28400000, // Hz typed where kHz was asked
		"17m":   14100,    // right number, wrong band
		"30m":   0,        // not set
		"80m":   3750,     // not a band this antenna covers at all
		"bogus": 14100,    // not a band
	}
	got := normMotorBandFreqs(in)
	want := map[string]int{"40m": 7005, "20m": 14050, "6m": 50313}
	if len(got) != len(want) {
		t.Fatalf("kept %v, want %v", got, want)
	}
	for k, v := range want {
		if got[k] != v {
			t.Errorf("%s = %d, want %d", k, got[k], v)
		}
	}
}

// The stored form round-trips, in canonical band order rather than map order so
// the settings row does not churn between saves.
func TestMotorBandFreqsRoundTrip(t *testing.T) {
	m := map[string]int{"20m": 14050, "40m": 7005, "6m": 50313}
	enc := encodeMotorBandFreqs(m)
	if enc != "40m=7005,20m=14050,6m=50313" {
		t.Errorf("encoded %q — want canonical low→high order", enc)
	}
	back := decodeMotorBandFreqs(enc)
	for k, v := range m {
		if back[k] != v {
			t.Errorf("round trip lost %s: %d → %d", k, v, back[k])
		}
	}
	// Garbage in one entry must cost only that entry.
	part := decodeMotorBandFreqs("40m=7005,20m=oops,6m=50313")
	if part["40m"] != 7005 || part["6m"] != 50313 {
		t.Errorf("one bad entry took the others down: %v", part)
	}
	if _, ok := part["20m"]; ok {
		t.Errorf("kept an unparseable entry: %v", part)
	}
}

// An unset band falls back to its default, which is what makes the Settings box
// safe to leave empty.
func TestMotorTuneKHzForBandFallsBack(t *testing.T) {
	m := map[string]int{"20m": 14050}
	if got := motorTuneKHzForBand(m, "20m"); got != 14050 {
		t.Errorf("chosen frequency ignored: %d", got)
	}
	if got := motorTuneKHzForBand(m, "15m"); got != 21150 {
		t.Errorf("15m = %d, want the 21150 default", got)
	}
	if got := motorTuneKHzForBand(m, "80m"); got != 0 {
		t.Errorf("80m = %d, want 0 — not a motor band", got)
	}
	// Every default must itself be in its band, or the fallback ships the very
	// fault normMotorBandFreqs exists to catch.
	for _, b := range motorBands {
		if got := bandForHz(int64(b.defKHz) * 1000); got != b.name {
			t.Errorf("default %d kHz for %s reads as %q", b.defKHz, b.name, got)
		}
	}
}

// The Ultrabeam's USB link answers on 19200 and on nothing else — confirmed on
// hardware, where 9600 opened the port and returned not one byte. A speed with a
// single right answer must not be storable as anything else, whatever an older
// config or a hand-edited setting says.
func TestNormMotorBaud(t *testing.T) {
	for _, tc := range []struct {
		typ, transport string
		in, want       int
	}{
		{"ultrabeam", "serial", 9600, 19200},
		{"ultrabeam", "serial", 0, 19200},
		{"ultrabeam", "serial", 115200, 19200},
		// Over TCP the speed is the adapter's business, not ours.
		{"ultrabeam", "tcp", 9600, 9600},
		// The SteppIR controller really does run at several speeds.
		{"steppir", "serial", 4800, 4800},
		{"steppir", "serial", 19200, 19200},
		// Nonsense falls back rather than reaching the driver.
		{"steppir", "serial", 0, 9600},
		{"steppir", "serial", 999999, 9600},
	} {
		if got := normMotorBaud(tc.typ, tc.transport, tc.in); got != tc.want {
			t.Errorf("normMotorBaud(%q, %q, %d) = %d, want %d", tc.typ, tc.transport, tc.in, got, tc.want)
		}
	}
}
