package solutions.tretter.mindmachine

import solutions.tretter.mindmachine.session.FlashColor
import solutions.tretter.mindmachine.session.SplitFlashSequencer
import solutions.tretter.mindmachine.session.computeSplitFlashFrame
import org.junit.Assert.assertEquals
import org.junit.Test

class VisualSignalTest {

    @Test
    fun splitFlashPattern_cycles_redGreen_black_greenRed_black() {
        val intervalMs = 100

        val phase0 = computeSplitFlashFrame(frameTimeNanos = 0L, flashOnMs = intervalMs, flashOffMs = intervalMs)
        val phase1 = computeSplitFlashFrame(frameTimeNanos = 100_000_000L, flashOnMs = intervalMs, flashOffMs = intervalMs)
        val phase2 = computeSplitFlashFrame(frameTimeNanos = 200_000_000L, flashOnMs = intervalMs, flashOffMs = intervalMs)
        val phase3 = computeSplitFlashFrame(frameTimeNanos = 300_000_000L, flashOnMs = intervalMs, flashOffMs = intervalMs)

        assertEquals(FlashColor.RED, phase0.left)
        assertEquals(FlashColor.GREEN, phase0.right)

        assertEquals(FlashColor.BLACK, phase1.left)
        assertEquals(FlashColor.BLACK, phase1.right)

        assertEquals(FlashColor.GREEN, phase2.left)
        assertEquals(FlashColor.RED, phase2.right)

        assertEquals(FlashColor.BLACK, phase3.left)
        assertEquals(FlashColor.BLACK, phase3.right)
    }

    @Test
    fun interval_is_clamped_to_valid_range() {
        val tooFast = computeSplitFlashFrame(frameTimeNanos = 49_000_000L, flashOnMs = 1, flashOffMs = 1)
        val validAt50ms = computeSplitFlashFrame(frameTimeNanos = 49_000_000L, flashOnMs = 50, flashOffMs = 50)
        assertEquals(validAt50ms, tooFast)

        val tooSlow = computeSplitFlashFrame(frameTimeNanos = 2_100_000_000L, flashOnMs = 5000, flashOffMs = 5000)
        val validAt2000ms = computeSplitFlashFrame(frameTimeNanos = 2_100_000_000L, flashOnMs = 2000, flashOffMs = 2000)
        assertEquals(validAt2000ms, tooSlow)
    }

    @Test
    fun sequencer_keeps_blank_duration_constant_when_flash_duration_changes_mid_blank() {
        val sequencer = SplitFlashSequencer(startTimeNanos = 0L, flashOnMs = 100, flashOffMs = 1000)

        assertEquals(FlashColor.BLACK, sequencer.frameAt(100_000_000L).left)

        sequencer.updateIntervals(flashOnMs = 200, flashOffMs = 1000)

        // Still blank before the configured 1000ms blank phase is complete.
        assertEquals(FlashColor.BLACK, sequencer.frameAt(600_000_000L).left)
        assertEquals(FlashColor.BLACK, sequencer.frameAt(1_099_000_000L).left)

        // At 1100ms total, blank ends and next on-phase starts.
        assertEquals(FlashColor.GREEN, sequencer.frameAt(1_100_000_000L).left)
    }

    @Test
    fun sequencer_applies_new_blank_interval_on_next_blank_boundary() {
        val sequencer = SplitFlashSequencer(startTimeNanos = 0L, flashOnMs = 100, flashOffMs = 100)

        // Enter second on phase.
        assertEquals(FlashColor.GREEN, sequencer.frameAt(200_000_000L).left)

        // Change blank interval during on phase; current on should finish uninterrupted.
        sequencer.updateIntervals(flashOnMs = 100, flashOffMs = 1000)
        assertEquals(FlashColor.GREEN, sequencer.frameAt(250_000_000L).left)

        // Next phase is blank and should use new 1000ms off interval.
        assertEquals(FlashColor.BLACK, sequencer.frameAt(300_000_000L).left)
        assertEquals(FlashColor.BLACK, sequencer.frameAt(1_299_000_000L).left)
        assertEquals(FlashColor.RED, sequencer.frameAt(1_300_000_000L).left)
    }
}
