use super::*;

#[track_caller]
fn run_ppu_cycles(apu: &mut APU, cycles: usize) {
    for i in 0..cycles {
	    apu.run_one_clock_cycle(i);
    }
}

trait Pattern {
    fn matches(&mut self, val: u8) -> bool;
}

impl Pattern for u8 {
    fn matches(&mut self, val: u8) -> bool {
        val == *self
    }
}

impl<I: Iterator<Item = u8>> Pattern for I {
    fn matches(&mut self, val: u8) -> bool {
        self.next().is_some_and(|v| v == val)
    }
}

fn is(samples: &[u8], mut pattern: impl Pattern) -> bool {
    for s in samples {
        if !pattern.matches(*s) {
            return false;
        }
    }
    true
}

#[test]
fn check_is() {
    assert!(&[0; 20], 0..5);
}

macro_rules! cycle_check {
    (|$apu:ident| $init:expr; $([$pat:expr; $count:expr]),* ; [$final:expr; _]) => {
        let mut $apu = APU::init();
        $init;
	    run_ppu_cycles(&mut $apu, 10000);
        let mut s = $apu.get_frame_samples();
        let mut cur = 0;
        $(
            let count = $count;
            if !is(&s[..count], $pat) {
                panic!("Incorrect samples from {} to {}:\nExpected: {:?}\n Found: {:?}", cur, cur + count, $pat, &s[..count]);
            }
            s = &s[count..];
            cur += count;
        )*
        if !is(s, $final) {
            panic!("Incorrect samples from {} to {}:\nExpected: {:?}\n Found: {:?}", cur, cur + count, $final, s);
        }
    };
}

const SNDCHN: u16 = 0x15;

const PULSE_CH1_DLCV: u16 = 0x00;
const PULSE_CH1_SWEEP: u16 = 0x01;
const PULSE_CH1_TLOW: u16 = 0x02;
const PULSE_CH1_LCTH: u16 = 0x03;

#[test]
fn run_apu() {
	let mut apu = APU::init();
	run_ppu_cycles(&mut apu, 10000);
	println!("Count: {}", apu.get_frame_samples().len());
	assert_eq!(apu.get_frame_samples(), &[0; 417]);
}

#[test]
fn run_apu_pulse_1() {
    // Base? case
	cycle_check!(|apu| {
    	apu.write_reg(SNDCHN, 0x01);
    	apu.write_reg(PULSE_CH1_DLCV, 0xBF);
    	apu.write_reg(PULSE_CH1_SWEEP, 0x7F);
    	apu.write_reg(PULSE_CH1_TLOW, 0x6F);
    	apu.write_reg(PULSE_CH1_LCTH, 0xF0);
    	// 1666 APU cycles (every fourth is a sample)
    	// Advance in duty cycle every 111 + 1 apu cycle (28 samples)
	}; [0; 28], [37; 28*4], [0; 28*4], [37; 28*4] ; [0; _]);

	cycle_check!(|apu| {
    	apu.write_reg(SNDCHN, 0x01);
    	apu.write_reg(PULSE_CH1_DLCV, 0xBF);
    	apu.write_reg(PULSE_CH1_SWEEP, 0x7F);
    	apu.write_reg(PULSE_CH1_TLOW, 0x60);
    	apu.write_reg(PULSE_CH1_LCTH, 0xF0);
    	// 1666 APU cycles (every fourth is a sample)
    	// Advance in duty cycle every 96 + 1 apu cycle (24.25 samples)
	}; [0; 24], [37; 24*4+1], [0; 24*4+1], [37; 24*4+1], [0; 24*4 + 1] ; [37; _]);
}
