use super::harness::*;

#[test]
fn step() {
    let ctx = ctx_with(|c| c.step = 7);
    let f = run_ctx("step", &ctx);
    assert_eq!(stack_int(&f), 7);
}

#[test]
fn beat() {
    let ctx = ctx_with(|c| c.beat = 4.5);
    let f = run_ctx("beat", &ctx);
    assert!((stack_float(&f) - 4.5).abs() < 1e-9);
}

#[test]
fn pattern() {
    let ctx = ctx_with(|c| c.pattern = 3);
    let f = run_ctx("pattern", &ctx);
    assert_eq!(stack_int(&f), 3);
}

#[test]
fn tempo() {
    let ctx = ctx_with(|c| c.tempo = 140.0);
    let f = run_ctx("tempo", &ctx);
    assert!((stack_float(&f) - 140.0).abs() < 1e-9);
}

#[test]
fn phase() {
    let ctx = ctx_with(|c| c.phase = 0.25);
    let f = run_ctx("phase", &ctx);
    assert!((stack_float(&f) - 0.25).abs() < 1e-9);
}

#[test]
fn slot() {
    let ctx = ctx_with(|c| c.slot = 5);
    let f = run_ctx("slot", &ctx);
    assert_eq!(stack_int(&f), 5);
}

#[test]
fn runs() {
    let ctx = ctx_with(|c| c.runs = 10);
    let f = run_ctx("runs", &ctx);
    assert_eq!(stack_int(&f), 10);
}

#[test]
fn iter() {
    let ctx = ctx_with(|c| c.iter = 5);
    let f = run_ctx("iter", &ctx);
    assert_eq!(stack_int(&f), 5);
}

#[test]
fn every_true_on_zero() {
    let ctx = ctx_with(|c| c.iter = 0);
    let f = run_ctx("( 100 ) 4 every", &ctx);
    assert_eq!(stack_int(&f), 100);
}

#[test]
fn every_true_on_multiple() {
    let ctx = ctx_with(|c| c.iter = 8);
    let f = run_ctx("( 100 ) 4 every", &ctx);
    assert_eq!(stack_int(&f), 100);
}

#[test]
fn every_false_between() {
    for i in 1..4 {
        let ctx = ctx_with(|c| c.iter = i);
        let f = run_ctx("( 100 ) 4 every", &ctx);
        assert!(f.stack().is_empty(), "iter={} should not execute quotation", i);
    }
}

#[test]
fn every_zero_count() {
    expect_error("( 1 ) 0 every", "every count must be > 0");
}

#[test]
fn stepdur() {
    // stepdur = 60.0 / tempo / 4.0 / speed = 60 / 120 / 4 / 1 = 0.125
    let f = run("stepdur");
    assert!((stack_float(&f) - 0.125).abs() < 1e-9);
}

#[test]
fn context_in_computation() {
    let ctx = ctx_with(|c| c.step = 3);
    let f = run_ctx("60 step +", &ctx);
    assert_eq!(stack_int(&f), 63);
}

// bjork (runs-based)

#[test]
fn bjork_tresillo() {
    // Bresenham(3,8) hits at positions 0, 2, 5
    for runs in 0..8 {
        let ctx = ctx_with(|c| c.runs = runs);
        let f = run_ctx("( 100 ) 3 8 bjork", &ctx);
        let hit = ((runs + 1) * 3) / 8 != (runs * 3) / 8;
        if hit {
            assert_eq!(stack_int(&f), 100, "runs={} should hit", runs);
        } else {
            assert!(f.stack().is_empty(), "runs={} should miss", runs);
        }
    }
}

#[test]
fn bjork_hit_count() {
    // Bjorklund(3,8) should produce exactly 3 hits
    let mut hit_count = 0;
    for runs in 0..8 {
        let ctx = ctx_with(|c| c.runs = runs);
        let f = run_ctx("( 100 ) 3 8 bjork", &ctx);
        if !f.stack().is_empty() {
            hit_count += 1;
        }
    }
    assert_eq!(hit_count, 3);
}

#[test]
fn bjork_all_hits() {
    let ctx = ctx_with(|c| c.runs = 0);
    let f = run_ctx("( 100 ) 8 8 bjork", &ctx);
    assert_eq!(stack_int(&f), 100);
}

#[test]
fn bjork_zero_hits() {
    let ctx = ctx_with(|c| c.runs = 0);
    let f = run_ctx("( 100 ) 0 8 bjork", &ctx);
    assert!(f.stack().is_empty());
}

#[test]
fn bjork_invalid() {
    expect_error("( 1 ) 3 0 bjork", "bjork");
}

// pbjork (iter-based)

#[test]
fn pbjork_cinquillo() {
    let mut hit_count = 0;
    for iter in 0..8 {
        let ctx = ctx_with(|c| c.iter = iter);
        let f = run_ctx("( 100 ) 5 8 pbjork", &ctx);
        if !f.stack().is_empty() {
            hit_count += 1;
        }
    }
    assert_eq!(hit_count, 5);
}

#[test]
fn pbjork_wraps() {
    let ctx0 = ctx_with(|c| c.iter = 0);
    let ctx8 = ctx_with(|c| c.iter = 8);
    let f0 = run_ctx("( 100 ) 3 8 pbjork", &ctx0);
    let f8 = run_ctx("( 100 ) 3 8 pbjork", &ctx8);
    assert_eq!(f0.stack().is_empty(), f8.stack().is_empty());
}
