use std::collections::HashMap;
use crate::{
common::{BitArray, Result},
point, BarcodeFormat, Exceptions, RXingResult, RXingResultMetadataType,
RXingResultMetadataValue,
};
use super::{upc_ean_reader, UPCEANReader, STAND_IN};
#[derive(Default)]
pub struct UPCEANExtension2Support {
decodeMiddleCounters: [u32; 4],
}
impl UPCEANExtension2Support {
pub fn decodeRow(
&self,
rowNumber: u32,
row: &BitArray,
extensionStartRange: &[u32; 3],
) -> Result<RXingResult> {
let mut result = String::new();
let end = self.decodeMiddle(row, extensionStartRange, &mut result)?;
let resultString = result;
let extensionData = Self::parseExtensionString(&resultString);
let mut extensionRXingResult = RXingResult::new(
&resultString,
Vec::new(),
vec![
point(
(extensionStartRange[0] + extensionStartRange[1]) as f32 / 2.0,
rowNumber as f32,
),
point(end as f32, rowNumber as f32),
],
BarcodeFormat::UPC_EAN_EXTENSION,
);
if let Some(ed) = extensionData {
extensionRXingResult.putAllMetadata(ed);
}
Ok(extensionRXingResult)
}
fn decodeMiddle(
&self,
row: &BitArray,
startRange: &[u32; 3],
resultString: &mut String,
) -> Result<u32> {
let mut counters = self.decodeMiddleCounters;
counters.fill(0);
let end = row.get_size();
let mut rowOffset = startRange[1] as usize;
let mut checkParity = 0;
let mut x = 0;
while x < 2 && rowOffset < end {
let bestMatch = STAND_IN.decodeDigit(
row,
&mut counters,
rowOffset,
&upc_ean_reader::L_AND_G_PATTERNS,
)?;
resultString
.push(char::from_u32('0' as u32 + bestMatch as u32 % 10).ok_or(Exceptions::PARSE)?);
rowOffset += counters.iter().sum::<u32>() as usize;
if bestMatch >= 10 {
checkParity |= 1 << (1 - x);
}
if x != 1 {
rowOffset = row.getNextSet(rowOffset);
rowOffset = row.getNextUnset(rowOffset);
}
x += 1;
}
if resultString.chars().count() != 2 {
return Err(Exceptions::NOT_FOUND);
}
if resultString
.parse::<u32>()
.map_err(|e| Exceptions::parse_with(format!("could not parse {resultString}: {e}")))?
% 4
!= checkParity
{
return Err(Exceptions::NOT_FOUND);
}
Ok(rowOffset as u32)
}
fn parseExtensionString(
raw: &str,
) -> Option<HashMap<RXingResultMetadataType, RXingResultMetadataValue>> {
if raw.chars().count() != 2 {
return None;
}
let mut result = HashMap::new();
result.insert(
RXingResultMetadataType::ISSUE_NUMBER,
RXingResultMetadataValue::IssueNumber(raw.parse::<i32>().ok()?),
);
Some(result)
}
}