use crate::common::Result;
use crate::oned::telepen_common;
use crate::BarcodeFormat;
use regex::Regex;
use rxing_one_d_proc_derive::OneDWriter;
use once_cell::sync::Lazy;
use super::OneDimensionalCodeWriter;
static RE: Lazy<Regex> = Lazy::new(|| Regex::new(r"^01|10$|01*0|00|1").unwrap());
#[derive(OneDWriter, Default)]
pub struct TelepenWriter;
impl OneDimensionalCodeWriter for TelepenWriter {
fn encode_oned(&self, contents: &str) -> Result<Vec<bool>> {
self.encode_oned_with_hints(contents, &EncodeHints::default())
}
fn encode_oned_with_hints(
&self,
contents: &str,
hints: &crate::EncodeHints,
) -> Result<Vec<bool>> {
let mut decodedContents = contents.to_string();
if matches!(hints.TelepenAsNumeric, Some(true)) {
decodedContents = telepen_common::numeric_to_ascii(contents)?;
}
let checksum = telepen_common::calculate_checksum(&decodedContents);
let mut binary = String::new();
binary = self.add_to_binary('_', binary);
for c in decodedContents.chars() {
if c as u32 > 127 {
return Err(Exceptions::illegal_argument_with(
"Telepen only supports ASCII characters.",
));
}
binary = self.add_to_binary(c, binary)
}
binary = self.add_to_binary(checksum, binary);
binary = self.add_to_binary('z', binary);
let matches: Vec<&str> = RE.find_iter(&binary).map(|m| m.as_str()).collect();
let mut result = vec![false; 2000];
let mut resultPosition = 0;
for b in matches {
match b {
"010" => {
result[resultPosition] = true;
result[resultPosition + 1] = true;
result[resultPosition + 2] = true;
result[resultPosition + 3] = false;
result[resultPosition + 4] = false;
result[resultPosition + 5] = false;
resultPosition += 6;
}
"00" => {
result[resultPosition] = true;
result[resultPosition + 1] = true;
result[resultPosition + 2] = true;
result[resultPosition + 3] = false;
resultPosition += 4;
}
"1" => {
result[resultPosition] = true;
result[resultPosition + 1] = false;
resultPosition += 2;
}
"01" => {
result[resultPosition] = true;
result[resultPosition + 1] = false;
result[resultPosition + 2] = false;
result[resultPosition + 3] = false;
resultPosition += 4;
}
"10" => {
result[resultPosition] = true;
result[resultPosition + 1] = false;
result[resultPosition + 2] = false;
result[resultPosition + 3] = false;
resultPosition += 4;
}
"0" => {
return Err(Exceptions::illegal_argument_with(
"Invalid bit combination!",
));
}
_ => {
result[resultPosition] = true;
result[resultPosition + 1] = false;
result[resultPosition + 2] = false;
result[resultPosition + 3] = false;
resultPosition += 4;
for _j in 2..b.len() - 2 {
result[resultPosition] = true;
result[resultPosition + 1] = false;
resultPosition += 2;
}
result[resultPosition] = true;
result[resultPosition + 1] = false;
result[resultPosition + 2] = false;
result[resultPosition + 3] = false;
resultPosition += 4;
}
}
}
Ok(result[0..resultPosition - 1].to_vec())
}
fn getSupportedWriteFormats(&self) -> Option<Vec<crate::BarcodeFormat>> {
Some(vec![BarcodeFormat::TELEPEN])
}
}
impl TelepenWriter {
fn get_bits(&self, byte: u8) -> Vec<bool> {
let mut bits = vec![false; 8];
let mut oneCount = 0;
for (i, bit) in bits.iter_mut().enumerate().take(8) {
let mask = 1 << i;
*bit = (mask & byte) > 0;
if *bit {
oneCount += 1;
}
}
bits[7] = oneCount % 2 != 0;
bits
}
fn add_to_binary(&self, c: char, mut binary: String) -> String {
let byte = c as u8;
let bits = self.get_bits(byte);
for bit in bits.iter().take(8) {
binary.push(if *bit { '1' } else { '0' });
}
binary
}
}
#[cfg(test)]
mod TelepenWriterTestCase {
use crate::{
common::{bit_matrix_test_case, BitMatrix},
BarcodeFormat, EncodeHintValue, EncodeHints, Writer,
};
use super::TelepenWriter;
#[test]
#[should_panic]
fn testAsciiOnly() {
encode("АБВГДЕЁЖ");
}
#[test]
fn testEncode() {
doTest(
"Hello world!",
concat!(
"00000",
"10101010101110001110111000111000101110001000100011101010100010001110101010001000101010101000100011101110111000101010101000101000101010101000100011100010001010001110101010001000111010111010101010111011101011101110001010111010111000101010101",
"00000",
),
false
);
doTest(
"11058474",
concat!(
"00000",
"101010101011100010001000111000101110111011100010101010101000100010111000100010001010111010001000111000101010101",
"00000",
),
true
);
}
fn doTest(input: &str, expected: &str, numeric: bool) {
let result: BitMatrix = if numeric {
encode_with_hints(input)
} else {
encode(input)
};
assert_eq!(expected, bit_matrix_test_case::matrix_to_string(&result));
}
fn encode(input: &str) -> BitMatrix {
TelepenWriter
.encode(input, &BarcodeFormat::TELEPEN, 0, 0)
.expect("must encode")
}
fn encode_with_hints(input: &str) -> BitMatrix {
let hints = EncodeHints::default().with(EncodeHintValue::TelepenAsNumeric(true));
TelepenWriter
.encode_with_hints(input, &BarcodeFormat::TELEPEN, 0, 0, &hints)
.expect("must encode")
}
}