use crate::cells::{push_identity, push_money};
use crate::error::FormsError;
use crate::map::{Form6251Map, MoneyCell};
use crate::pdf;
use crate::verify::{verify_flat, FlatPlacement};
use btctax_core::tax::form6251::Form6251;
use btctax_core::tax::packet::ReturnHeader;
use btctax_core::Usd;
const COL_AMOUNT: usize = 0;
const F6251_CLUSTERS: &[(f32, f32)] = &[(504.0, 576.0)];
const GRP_P1: u32 = 0;
const GRP_P2: u32 = 1;
fn assert_paren_magnitudes(v: &[(&str, Usd)]) -> Result<(), FormsError> {
for (line, x) in v {
if *x < Usd::ZERO {
return Err(FormsError::Geometry(format!(
"Form 6251 line {line} is a PARENTHESIZED box holding {x}: the form's own parentheses \
supply the minus sign, so a negative value renders as a positive number on a filed \
return. Pass the magnitude."
)));
}
}
Ok(())
}
pub fn fill_form_6251_with_map(
f: &Form6251,
header: &ReturnHeader,
map: &Form6251Map,
) -> Result<Vec<u8>, FormsError> {
let f = &f.printed();
let l2b = f.line2b.abs();
assert_paren_magnitudes(&[("2b", l2b)])?;
let line1 = match f.line1 {
btctax_core::tax::form6251::Form6251Line1::Y2024 { line1 } => line1,
_ => {
return Err(FormsError::Geometry(
"Form 6251 line 1 is not in its TY2024 shape, but this is the TY2024 map, which \
prints a single line 1. The 2025 revision splits it into 1a/1b (60 numbered boxes, \
and a line 4 that combines lines 1b through 3) — that needs its own map, not this \
one, and filling this one would drop a sub-line off a filed form."
.into(),
));
}
};
let mut doc = pdf::load(pdf::f6251_pdf(map.year)?)?;
let blank_fields = pdf::collect_fields(&doc)?;
let mut writes: Vec<(String, pdf::FieldValue)> = Vec::new();
let mut placements: Vec<FlatPlacement> = Vec::new();
let p1: [(&MoneyCell, Usd); 12] = [
(&map.line1, line1),
(&map.line2a, f.line2a),
(&map.line2b, l2b), (&map.line3, f.line3),
(&map.line4, f.line4),
(&map.line5, f.line5),
(&map.line6, f.line6),
(&map.line7, f.line7),
(&map.line8, f.line8),
(&map.line9, f.line9),
(&map.line10, f.line10),
(&map.line11, f.line11),
];
for (ord, (cell, value)) in p1.iter().enumerate() {
push_money(
&mut writes,
&mut placements,
cell,
*value,
COL_AMOUNT,
Some((GRP_P1, ord as u32)),
);
}
if f.part_iii_completed {
let p3: [(&MoneyCell, Usd); 29] = [
(&map.line12, f.line12),
(&map.line13, f.line13),
(&map.line14, f.line14),
(&map.line15, f.line15),
(&map.line16, f.line16),
(&map.line17, f.line17),
(&map.line18, f.line18),
(&map.line19, f.line19),
(&map.line20, f.line20),
(&map.line21, f.line21),
(&map.line22, f.line22),
(&map.line23, f.line23),
(&map.line24, f.line24),
(&map.line25, f.line25),
(&map.line26, f.line26),
(&map.line27, f.line27),
(&map.line28, f.line28),
(&map.line29, f.line29),
(&map.line30, f.line30),
(&map.line31, f.line31),
(&map.line32, f.line32),
(&map.line33, f.line33),
(&map.line34, f.line34),
(&map.line35, f.line35),
(&map.line36, f.line36),
(&map.line37, f.line37),
(&map.line38, f.line38),
(&map.line39, f.line39),
(&map.line40, f.line40),
];
for (ord, (cell, value)) in p3.iter().enumerate() {
push_money(
&mut writes,
&mut placements,
cell,
*value,
COL_AMOUNT,
Some((GRP_P2, ord as u32)),
);
}
}
push_identity(
&mut writes,
&mut placements,
&map.identity,
&header.name_line,
&header.taxpayer.ssn,
&blank_fields,
)?;
let index = pdf::index(&blank_fields);
pdf::drop_xfa_and_set_needappearances(&mut doc)?;
pdf::apply_writes(&mut doc, &index, &writes)?;
pdf::strip_nondeterminism(&mut doc);
let bytes = pdf::save(&mut doc)?;
let check = pdf::load(&bytes)?;
let fields = pdf::collect_fields(&check)?;
verify_flat(&check, &fields, &placements, F6251_CLUSTERS)?;
Ok(bytes)
}