//go:build windows

package cat

import "testing"

// The reported symptom: with spot mirroring on, the same station appeared two
// or three times on the panorama.
//
// Its cause is not in OpsLog's spot pipeline — one cluster line produces one
// SendSpot. It is that a popular DX station IS spotted two or three times, by
// different operators within the same minute, and no two of them agree on the
// frequency to better than a few tens of hertz. The backend assumed ExpertSDR
// replaced a spot bearing a callsign it already had; it keys on the frequency
// too, so each of those became its own marker.
func TestSameStationSpottedBySeveralOperatorsIsDrawnOnce(t *testing.T) {
	tci := &TCI{spotsEnabled: true}

	draw, del := tci.noteSpot("UN7GK", 14025000)
	if !draw || del {
		t.Fatalf("first spot: draw=%v delete=%v, want draw and nothing to delete", draw, del)
	}
	// The same station, two more spotters, a few tens of hertz apart.
	for _, hz := range []int64{14025120, 14024900} {
		if draw, del := tci.noteSpot("UN7GK", hz); draw || del {
			t.Errorf("re-spot at %d Hz: draw=%v delete=%v, want nothing sent — this is the duplicate marker", hz, draw, del)
		}
	}
}

// A station that really moves must still move on the panorama, and the marker
// left where it was must go. Deleting first is the whole difference between
// "the spot follows the station" and "the station collects markers".
func TestAStationThatMovesReplacesItsMarker(t *testing.T) {
	tci := &TCI{spotsEnabled: true}
	tci.noteSpot("UN7GK", 14025000)

	draw, del := tci.noteSpot("UN7GK", 14032000) // 7 kHz up: a real QSY
	if !draw || !del {
		t.Fatalf("after a QSY: draw=%v delete=%v, want the old marker deleted and a new one drawn", draw, del)
	}
	// And the new position becomes the reference, so spotters agreeing with it
	// are quiet again.
	if draw, _ := tci.noteSpot("UN7GK", 14032100); draw {
		t.Error("a spot at the station's new frequency was drawn again")
	}
}

// Case matters nowhere in ham radio, and the cluster is not consistent about it.
func TestSpotMemoryIgnoresCase(t *testing.T) {
	tci := &TCI{spotsEnabled: true}
	tci.noteSpot("un7gk", 14025000)
	if draw, _ := tci.noteSpot("UN7GK", 14025000); draw {
		t.Error("the same call in another case was treated as a different station")
	}
}

// Two different stations are two markers — the whole point of the panorama.
func TestDifferentStationsEachGetAMarker(t *testing.T) {
	tci := &TCI{spotsEnabled: true}
	tci.noteSpot("UN7GK", 14025000)
	draw, del := tci.noteSpot("ZD7BG", 14025050) // 50 Hz away, a different operator
	if !draw {
		t.Error("a second station near the first was swallowed as a duplicate")
	}
	if del {
		t.Error("deleting by callsign would have removed a spot this station never had")
	}
}

// The connection drops and comes back: Connect sends spot_clear, so the
// panorama is empty. If the memory survived that, the next spot for each of
// those calls would be suppressed as "already drawn" onto an empty panorama —
// the operator's spots would simply stop appearing until they changed
// frequency.
func TestReconnectingForgetsWhatWasDrawn(t *testing.T) {
	tci := &TCI{spotsEnabled: true}
	tci.noteSpot("UN7GK", 14025000)

	tci.mu.Lock()
	tci.spotFreq = map[string]int64{} // what Connect does alongside spot_clear
	tci.mu.Unlock()

	if draw, del := tci.noteSpot("UN7GK", 14025000); !draw || del {
		t.Errorf("after a reconnect: draw=%v delete=%v, want it drawn again and nothing deleted", draw, del)
	}
}
