package spe

import (
	"testing"
	"time"
)

// decodeCSV marks the client connected on EVERY frame it parses, and the poll
// goroutine can be mid-read while PowerOff runs. So the frame from a second ago
// landed after PowerOff had marked the amp offline and put it straight back —
// the console kept showing the amplifier on until the operator pressed OFF a
// second time.
func TestFrameInFlightDoesNotResurrectASwitchedOffAmp(t *testing.T) {
	c := &Client{}
	const frame = ",13K,O,R,A,1,05,1b,0r,M,0000, 0.00, 0.00, 1.3, 0.0, 26,000,000,N,N,"

	// Normal running: a frame marks it connected and reads OPERATE.
	c.decodeCSV(frame)
	if !c.GetStatus().Connected || !c.GetStatus().Operate {
		t.Fatal("a status frame should mark the amp connected")
	}

	// The operator presses OFF. This is what PowerOff does, in order.
	c.statusMu.Lock()
	c.offUntil = time.Now().Add(3 * time.Second)
	c.statusMu.Unlock()
	c.setErr(errSwitchedOff{})

	// A frame that was already on the wire arrives now.
	c.decodeCSV(frame)

	if c.GetStatus().Connected {
		t.Error("a frame from before the switch-off put the amp back on — this is the double-click on OFF")
	}
}

// An amp switched back on at its own front panel must reappear without OpsLog
// being told, so the suppression has to expire rather than latch.
func TestSuppressionExpires(t *testing.T) {
	c := &Client{}
	c.statusMu.Lock()
	c.offUntil = time.Now().Add(-time.Second) // window already past
	c.statusMu.Unlock()
	c.setErr(errSwitchedOff{})

	c.decodeCSV(",13K,O,R,A,1,05,1b,0r,M,0000, 0.00, 0.00, 1.3, 0.0, 26,000,000,N,N,")
	if !c.GetStatus().Connected {
		t.Error("an amp answering after the window must come back on its own")
	}
}

type errSwitchedOff struct{}

func (errSwitchedOff) Error() string { return "switched off" }
