//! 100% port of pdfminer.six test_pdfminer_ccitt.py

use bolivar_core::ccitt::{CCITTFaxDecoder, CCITTG4Parser, CcittParams, ccittfaxdecode};

/// Helper to create a parser with a given reference line
fn get_parser(bits: &str) -> CCITTG4Parser {
    let mut parser = CCITTG4Parser::new(bits.len(), false);
    parser.set_curline(
        bits.chars()
            .map(|c| c.to_digit(10).unwrap() as i8)
            .collect(),
    );
    parser.reset_line();
    parser
}

// TestCCITTG4Parser tests

#[test]
fn test_b1() {
    let mut parser = get_parser("00000");
    parser.do_vertical(0);
    assert_eq!(parser.curpos(), 0);
}

#[test]
fn test_b2() {
    let mut parser = get_parser("10000");
    parser.do_vertical(-1);
    assert_eq!(parser.curpos(), 0);
}

#[test]
fn test_b3() {
    let mut parser = get_parser("000111");
    parser.do_pass();
    assert_eq!(parser.curpos(), 3);
    assert_eq!(parser.get_bits(), "111");
}

#[test]
fn test_b4() {
    let mut parser = get_parser("00000");
    parser.do_vertical(2);
    assert_eq!(parser.curpos(), 2);
    assert_eq!(parser.get_bits(), "11");
}

#[test]
fn test_b5() {
    let mut parser = get_parser("11111111100");
    parser.do_horizontal(0, 3);
    assert_eq!(parser.curpos(), 3);
    parser.do_vertical(1);
    assert_eq!(parser.curpos(), 10);
    assert_eq!(parser.get_bits(), "0001111111");
}

#[test]
fn test_e1() {
    let mut parser = get_parser("10000");
    parser.do_vertical(0);
    assert_eq!(parser.curpos(), 1);
    parser.do_vertical(0);
    assert_eq!(parser.curpos(), 5);
    assert_eq!(parser.get_bits(), "10000");
}

#[test]
fn test_e2() {
    let mut parser = get_parser("10011");
    parser.do_vertical(0);
    assert_eq!(parser.curpos(), 1);
    parser.do_vertical(2);
    assert_eq!(parser.curpos(), 5);
    assert_eq!(parser.get_bits(), "10000");
}

#[test]
fn test_e3() {
    let mut parser = get_parser("011111");
    parser.set_color(0);
    parser.do_vertical(0);
    assert_eq!(parser.color(), 1);
    assert_eq!(parser.curpos(), 1);
    parser.do_vertical(-2);
    assert_eq!(parser.color(), 0);
    assert_eq!(parser.curpos(), 4);
    parser.do_vertical(0);
    assert_eq!(parser.curpos(), 6);
    assert_eq!(parser.get_bits(), "011100");
}

#[test]
fn test_e4() {
    let mut parser = get_parser("10000");
    parser.do_vertical(0);
    assert_eq!(parser.curpos(), 1);
    parser.do_vertical(-2);
    assert_eq!(parser.curpos(), 3);
    parser.do_vertical(0);
    assert_eq!(parser.curpos(), 5);
    assert_eq!(parser.get_bits(), "10011");
}

#[test]
fn test_e5() {
    let mut parser = get_parser("011000");
    parser.set_color(0);
    parser.do_vertical(0);
    assert_eq!(parser.curpos(), 1);
    parser.do_vertical(3);
    assert_eq!(parser.curpos(), 6);
    assert_eq!(parser.get_bits(), "011111");
}

#[test]
fn test_e6() {
    let mut parser = get_parser("11001");
    parser.do_pass();
    assert_eq!(parser.curpos(), 4);
    parser.do_vertical(0);
    assert_eq!(parser.curpos(), 5);
    assert_eq!(parser.get_bits(), "11111");
}

#[test]
fn test_e7() {
    let mut parser = get_parser("0000000000");
    parser.set_curpos(2);
    parser.set_color(1);
    parser.do_horizontal(2, 6);
    assert_eq!(parser.curpos(), 10);
    assert_eq!(parser.get_bits(), "1111000000");
}

#[test]
fn test_e8() {
    let mut parser = get_parser("001100000");
    parser.set_curpos(1);
    parser.set_color(0);
    parser.do_vertical(0);
    assert_eq!(parser.curpos(), 2);
    parser.do_horizontal(7, 0);
    assert_eq!(parser.curpos(), 9);
    assert_eq!(parser.get_bits(), "101111111");
}

#[test]
fn test_m1() {
    let mut parser = get_parser("10101");
    parser.do_pass();
    assert_eq!(parser.curpos(), 2);
    parser.do_pass();
    assert_eq!(parser.curpos(), 4);
    assert_eq!(parser.get_bits(), "1111");
}

#[test]
fn test_m2() {
    let mut parser = get_parser("101011");
    parser.do_vertical(-1);
    parser.do_vertical(-1);
    parser.do_vertical(1);
    parser.do_horizontal(1, 1);
    assert_eq!(parser.get_bits(), "011101");
}

#[test]
fn test_m3() {
    let mut parser = get_parser("10111011");
    parser.do_vertical(-1);
    parser.do_pass();
    parser.do_vertical(1);
    parser.do_vertical(1);
    assert_eq!(parser.get_bits(), "00000001");
}

// TestCCITTFaxDecoder tests

// Python test passes b"0" which is [48] (ASCII '0'), a truthy value
// The Python output_line checks `if b:` where any non-zero is truthy
#[test]
fn test_decoder_b1() {
    let mut decoder = CCITTFaxDecoder::new(5, false, false);
    // In Python: decoder.output_line(0, b"0") where b"0" = [48]
    // Since 48 is truthy, it should produce 0x80
    decoder.output_line(0, &[48]); // ASCII '0' = 48, which is truthy
    assert_eq!(decoder.close(), vec![0x80]);
}

// Integration tests - verify the full decode pipeline works

/// Test that feedbytes produces output via the parser-decoder pipeline.
/// This is the critical test that was failing before the architecture fix.
#[test]
fn test_decoder_feedbytes_produces_output() {
    // Create a simple 8-pixel wide image with one scanline
    // Reference line is all white (1s), we encode a line that's also all white
    // V(0) code = "1" means copy reference position, repeated 8 times to fill the line
    //
    // For an 8-pixel all-white line starting from white:
    // - First V(0) at position 0 copies to pos 8 (end of line) with color=1
    // This should produce one complete scanline

    let mut decoder = CCITTFaxDecoder::new(8, false, false);

    // V(0) = "1" bit. For 8 pixels starting white on white refline:
    // One V(0) code advances curpos from -1 to width (8), completing the line
    // The byte 0x80 = 10000000 contains one "1" bit
    decoder.feedbytes(&[0b10000000]);

    let output = decoder.close();
    // Should have produced 1 byte of output (8 pixels / 8 bits per byte)
    assert!(
        !output.is_empty(),
        "Decoder should produce output after feedbytes"
    );
    assert_eq!(output.len(), 1, "8-pixel line should produce 1 byte");
    // All-white line (color=1 means white in CCITT) should be 0xFF
    assert_eq!(output[0], 0xFF, "All-white scanline should be 0xFF");
}

/// Test ccittfaxdecode function returns non-empty data
#[test]
fn test_ccittfaxdecode_returns_data() {
    let params = CcittParams {
        k: -1, // Group 4
        columns: 8,
        encoded_byte_align: false,
        black_is_1: false,
    };

    // Single V(0) code to produce one white scanline
    let data = &[0b10000000];
    let result = ccittfaxdecode(data, &params);

    assert!(result.is_ok(), "ccittfaxdecode should succeed");
    let decoded = result.unwrap();
    assert!(
        !decoded.is_empty(),
        "ccittfaxdecode should return non-empty data"
    );
    assert_eq!(decoded.len(), 1);
    assert_eq!(decoded[0], 0xFF);
}

/// Test decoding a simple black and white pattern
#[test]
fn test_decode_black_white_pattern() {
    // Encode: 4 white pixels, then 4 black pixels on an 8-pixel line
    // Reference line is all 1s (white)
    //
    // V(0) at pos 0 with white ref would go to width, so we need vertical offset
    // V(-4) would position at 4 with color flip
    // V(-4) code = "000010"
    // Then V(0) = "1" to complete the line

    let mut decoder = CCITTFaxDecoder::new(8, false, false);

    // V(-2) = "000010" positions at b1-2 with color 0 (black)
    // V(-2) again positions at next change -2
    // This is getting complex. Let's use horizontal mode instead.
    //
    // H mode = "001"
    // Then white run length 4 = "1011"
    // Then black run length 4 = "011"
    //
    // Full sequence: 001 1011 011 = 00110110 11xxxxxx
    // That's 0x36 0xC0

    decoder.feedbytes(&[0b00110110, 0b11000000]);

    let output = decoder.close();
    assert!(
        !output.is_empty(),
        "Should produce output for H-mode pattern"
    );
    // 4 white (1111) + 4 black (0000) = 11110000 = 0xF0
    if output.len() == 1 {
        assert_eq!(output[0], 0xF0, "4 white + 4 black should be 0xF0");
    }
}

/// Test that multiple scanlines are decoded correctly
#[test]
fn test_decode_multiple_scanlines() {
    let mut decoder = CCITTFaxDecoder::new(8, false, false);

    // Two V(0) codes = two all-white scanlines
    // V(0) V(0) = "1" "1" = 11xxxxxx = 0xC0
    decoder.feedbytes(&[0b11000000]);

    let output = decoder.close();
    // Should have 2 scanlines = 2 bytes
    assert_eq!(output.len(), 2, "Two V(0) codes should produce 2 scanlines");
    assert_eq!(output[0], 0xFF);
    assert_eq!(output[1], 0xFF);
}

/// Test EOFB (End Of Fax Block) handling
#[test]
fn test_eofb_stops_decoding() {
    let mut decoder = CCITTFaxDecoder::new(8, false, false);

    // V(0) then EOFB
    // V(0) = "1"
    // EOFB = "000000000001000000000001" (24 bits)
    //
    // Byte sequence: 1 + 23 zeros + 1 =
    // 10000000 00000000 00000001 = 0x80 0x00 0x01
    // But EOFB is 24 bits: 000000000001000000000001
    // Let's do: V(0) followed by EOFB
    // "1" + "000000000001000000000001"
    // = 1000 0000 0000 1000 0000 0001 (but shifted)
    // Actually: 1 followed by 000000000001000000000001
    // Bits: 1 0000 0000 0001 0000 0000 0001
    // Bytes: 10000000 00010000 00000100 = 0x80 0x10 0x04

    // Let's just check that data after EOFB is ignored
    decoder.feedbytes(&[
        0b10000000, // V(0) for first scanline
        0b00000000, 0b00001000, 0b00000001, // EOFB
        0b11111111, // This should be ignored
    ]);

    let output = decoder.close();
    // Only one scanline should be decoded before EOFB
    assert_eq!(output.len(), 1, "EOFB should stop decoding");
}
