use super::harness::*;

#[test]
fn choose_from_stack() {
    let f = forth();
    let ctx = default_ctx();
    f.evaluate("1 2 3 3 choose", &ctx).unwrap();
    let val = stack_int(&f);
    assert!(val >= 1 && val <= 3, "expected 1, 2, or 3, got {}", val);
}

#[test]
fn cycle_by_runs() {
    let ctx = ctx_with(|c| c.runs = 0);
    let f = run_ctx("10 20 30 3 cycle", &ctx);
    assert_eq!(stack_int(&f), 10);

    let ctx = ctx_with(|c| c.runs = 1);
    let f = run_ctx("10 20 30 3 cycle", &ctx);
    assert_eq!(stack_int(&f), 20);

    let ctx = ctx_with(|c| c.runs = 2);
    let f = run_ctx("10 20 30 3 cycle", &ctx);
    assert_eq!(stack_int(&f), 30);

    let ctx = ctx_with(|c| c.runs = 3);
    let f = run_ctx("10 20 30 3 cycle", &ctx);
    assert_eq!(stack_int(&f), 10);
}

#[test]
fn pcycle_by_iter() {
    let ctx = ctx_with(|c| c.iter = 0);
    let f = run_ctx("10 20 30 3 pcycle", &ctx);
    assert_eq!(stack_int(&f), 10);

    let ctx = ctx_with(|c| c.iter = 1);
    let f = run_ctx("10 20 30 3 pcycle", &ctx);
    assert_eq!(stack_int(&f), 20);

    let ctx = ctx_with(|c| c.iter = 2);
    let f = run_ctx("10 20 30 3 pcycle", &ctx);
    assert_eq!(stack_int(&f), 30);
}

#[test]
fn cycle_with_quotations() {
    let ctx = ctx_with(|c| c.runs = 0);
    let f = run_ctx("5 ( dup ) ( 2 * ) 2 cycle", &ctx);
    let stack = f.stack();
    assert_eq!(stack.len(), 2);
    assert_eq!(stack_int(&f), 5);

    let ctx = ctx_with(|c| c.runs = 1);
    let f = run_ctx("5 ( dup ) ( 2 * ) 2 cycle", &ctx);
    assert_eq!(stack_int(&f), 10);
}

#[test]
fn cycle_executes_quotation() {
    let ctx = ctx_with(|c| c.runs = 0);
    let f = run_ctx("10 ( 3 + ) ( 5 + ) 2 cycle", &ctx);
    assert_eq!(stack_int(&f), 13);
}

#[test]
fn dupn_basic() {
    // 5 3 dupn -> 5 5 5, then + + -> 15
    expect_int("5 3 dupn + +", 15);
}

#[test]
fn dupn_alias() {
    expect_int("5 3 ! + +", 15);
}

#[test]
fn cycle_zero_count_error() {
    expect_error("1 2 3 0 cycle", "cycle count must be > 0");
}

#[test]
fn choose_zero_count_error() {
    expect_error("1 2 3 0 choose", "choose count must be > 0");
}

// Bracket syntax tests

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

    let ctx = ctx_with(|c| c.runs = 1);
    let f = run_ctx("[ 10 20 30 ] cycle", &ctx);
    assert_eq!(stack_int(&f), 20);

    let ctx = ctx_with(|c| c.runs = 2);
    let f = run_ctx("[ 10 20 30 ] cycle", &ctx);
    assert_eq!(stack_int(&f), 30);

    let ctx = ctx_with(|c| c.runs = 3);
    let f = run_ctx("[ 10 20 30 ] cycle", &ctx);
    assert_eq!(stack_int(&f), 10);
}

#[test]
fn bracket_with_quotations() {
    let ctx = ctx_with(|c| c.runs = 0);
    let f = run_ctx("5 [ ( 3 + ) ( 5 + ) ] cycle", &ctx);
    assert_eq!(stack_int(&f), 8);

    let ctx = ctx_with(|c| c.runs = 1);
    let f = run_ctx("5 [ ( 3 + ) ( 5 + ) ] cycle", &ctx);
    assert_eq!(stack_int(&f), 10);
}

#[test]
fn bracket_nested() {
    let ctx = ctx_with(|c| { c.runs = 0; c.iter = 0; });
    let f = run_ctx("[ [ 10 20 ] cycle [ 30 40 ] cycle ] pcycle", &ctx);
    assert_eq!(stack_int(&f), 10);

    let ctx = ctx_with(|c| { c.runs = 0; c.iter = 1; });
    let f = run_ctx("[ [ 10 20 ] cycle [ 30 40 ] cycle ] pcycle", &ctx);
    assert_eq!(stack_int(&f), 30);
}

#[test]
fn bracket_with_generator() {
    let ctx = ctx_with(|c| c.runs = 0);
    let f = run_ctx("[ 1 4 .. ] cycle", &ctx);
    assert_eq!(stack_int(&f), 1);

    let ctx = ctx_with(|c| c.runs = 3);
    let f = run_ctx("[ 1 4 .. ] cycle", &ctx);
    assert_eq!(stack_int(&f), 4);
}

#[test]
fn stray_bracket_error() {
    expect_error("10 ] cycle", "unexpected ]");
}

#[test]
fn unclosed_bracket_error() {
    expect_error("[ 10 20", "missing ]");
}

// Index tests

#[test]
fn index_basic() {
    expect_int("10 20 30 3 1 index", 20);
}

#[test]
fn index_with_brackets() {
    expect_int("[ 10 20 30 ] 1 index", 20);
}

#[test]
fn index_modulo_wraps() {
    expect_int("[ 10 20 30 ] 5 index", 30);
}

#[test]
fn index_negative_wraps() {
    expect_int("[ 10 20 30 ] -1 index", 30);
}

#[test]
fn index_with_quotation() {
    expect_int("5 [ ( 3 + ) ( 5 + ) ] 0 index", 8);
}

#[test]
fn index_zero_count_error() {
    expect_error("0 0 index", "index count must be > 0");
}
