use std::rc::Rc;
use crate::common::{CharacterSet, Result};
use crate::{Dimension, Exceptions};
use super::{
ASCIIEncoder, Base256Encoder, C40Encoder, EdifactEncoder, Encoder, EncoderContext,
SymbolInfoLookup, SymbolShapeHint, TextEncoder, X12Encoder,
};
#[allow(dead_code)]
const DEFAULT_ENCODING: CharacterSet = CharacterSet::ISO8859_1;
const PAD: u8 = 129;
pub const LATCH_TO_C40: u8 = 230;
pub const LATCH_TO_BASE256: u8 = 231;
pub const UPPER_SHIFT: u8 = 235;
const MACRO_05: u8 = 236;
const MACRO_06: u8 = 237;
pub const LATCH_TO_ANSIX12: u8 = 238;
pub const LATCH_TO_TEXT: u8 = 239;
pub const LATCH_TO_EDIFACT: u8 = 240;
pub const C40_UNLATCH: u8 = 254;
pub const X12_UNLATCH: u8 = 254;
pub const MACRO_05_HEADER: &str = "[)>\u{001E}05\u{001D}";
pub const MACRO_06_HEADER: &str = "[)>\u{001E}06\u{001D}";
pub const MACRO_TRAILER: &str = "\u{001E}\u{0004}";
pub const ASCII_ENCODATION: usize = 0;
pub const C40_ENCODATION: usize = 1;
pub const TEXT_ENCODATION: usize = 2;
pub const X12_ENCODATION: usize = 3;
pub const EDIFACT_ENCODATION: usize = 4;
pub const BASE256_ENCODATION: usize = 5;
fn randomize253State(codewordPosition: u32) -> String {
let pseudoRandom = ((149 * codewordPosition) % 253) + 1;
let tempVariable = PAD as u32 + pseudoRandom;
if tempVariable <= 254 {
char::from_u32(tempVariable)
} else {
char::from_u32(tempVariable - 254)
}
.unwrap_or(char::default())
.to_string()
}
pub fn encodeHighLevel(msg: &str) -> Result<String> {
encodeHighLevelWithDimensionForceC40(msg, SymbolShapeHint::FORCE_NONE, None, None, false)
}
pub fn encodeHighLevelSIL(
msg: &str,
symbol_lookup: Option<Rc<SymbolInfoLookup>>,
) -> Result<String> {
encodeHighLevelWithDimensionForceC40WithSymbolInfoLookup(
msg,
SymbolShapeHint::FORCE_NONE,
None,
None,
false,
symbol_lookup,
)
}
pub fn encodeHighLevelWithDimension(
msg: &str,
shape: SymbolShapeHint,
minSize: Option<Dimension>,
maxSize: Option<Dimension>,
) -> Result<String> {
encodeHighLevelWithDimensionForceC40(msg, shape, minSize, maxSize, false)
}
pub fn encodeHighLevelWithDimensionForceC40WithSymbolInfoLookup(
msg: &str,
shape: SymbolShapeHint,
minSize: Option<Dimension>,
maxSize: Option<Dimension>,
forceC40: bool,
symbol_lookup: Option<Rc<SymbolInfoLookup>>,
) -> Result<String> {
let c40Encoder = Rc::new(C40Encoder::new());
let encoders: [Rc<dyn Encoder>; 6] = [
Rc::new(ASCIIEncoder::new()),
c40Encoder.clone(),
Rc::new(TextEncoder::new()),
Rc::new(X12Encoder::new()),
Rc::new(EdifactEncoder::new()),
Rc::new(Base256Encoder::new()),
];
let mut context = if let Some(symbol_table) = symbol_lookup {
EncoderContext::with_symbol_info_lookup(msg, symbol_table)?
} else {
EncoderContext::new(msg)?
};
context.setSymbolShape(shape);
context.setSizeConstraints(minSize, maxSize);
if msg.starts_with(MACRO_05_HEADER) && msg.ends_with(MACRO_TRAILER) {
context.writeCodeword(MACRO_05);
context.setSkipAtEnd(2);
context.pos += MACRO_05_HEADER.chars().count() as u32;
} else if msg.starts_with(MACRO_06_HEADER) && msg.ends_with(MACRO_TRAILER) {
context.writeCodeword(MACRO_06);
context.setSkipAtEnd(2);
context.pos += MACRO_06_HEADER.chars().count() as u32;
}
let mut encodingMode = ASCII_ENCODATION;
if forceC40 {
c40Encoder.encodeMaximalC40(&mut context)?;
encodingMode = context.getNewEncoding().ok_or(Exceptions::ILLEGAL_STATE)?;
context.resetEncoderSignal();
}
while context.hasMoreCharacters() {
encoders[encodingMode].encode(&mut context)?;
if context.getNewEncoding().is_some() {
encodingMode = context.getNewEncoding().ok_or(Exceptions::ILLEGAL_STATE)?;
context.resetEncoderSignal();
}
}
let len = context.getCodewordCount();
context.updateSymbolInfo();
let capacity = context
.getSymbolInfo()
.ok_or(Exceptions::ILLEGAL_STATE)?
.getDataCapacity();
if len < capacity as usize
&& encodingMode != ASCII_ENCODATION
&& encodingMode != BASE256_ENCODATION
&& encodingMode != EDIFACT_ENCODATION
{
context.writeCodeword(0xfe); }
if context.getCodewords().chars().count() < capacity as usize {
context.writeCodeword(PAD)
}
while context.getCodewords().chars().count() < capacity as usize {
context.writeCodewords(&randomize253State(
context.getCodewords().chars().count() as u32 + 1,
))
}
Ok(context.getCodewords().to_owned())
}
pub fn encodeHighLevelWithDimensionForceC40(
msg: &str,
shape: SymbolShapeHint,
minSize: Option<Dimension>,
maxSize: Option<Dimension>,
forceC40: bool,
) -> Result<String> {
encodeHighLevelWithDimensionForceC40WithSymbolInfoLookup(
msg, shape, minSize, maxSize, forceC40, None,
)
}
pub fn lookAheadTest(msg: &str, startpos: u32, currentMode: u32) -> usize {
let newMode = lookAheadTestIntern(msg, startpos, currentMode);
if currentMode as usize == X12_ENCODATION && newMode == X12_ENCODATION {
let endpos = (startpos + 3).min(msg.chars().count() as u32);
for i in startpos..endpos {
if let Some(c) = msg.chars().nth(i as usize) {
if !isNativeX12(c) {
return ASCII_ENCODATION;
}
}
}
} else if currentMode as usize == EDIFACT_ENCODATION && newMode == EDIFACT_ENCODATION {
let endpos = (startpos + 4).min(msg.chars().count() as u32);
for i in startpos..endpos {
if let Some(c) = msg.chars().nth(i as usize) {
if !isNativeEDIFACT(c) {
return ASCII_ENCODATION;
}
}
}
}
newMode
}
fn lookAheadTestIntern(msg: &str, startpos: u32, currentMode: u32) -> usize {
if startpos as usize >= msg.chars().count() {
return currentMode as usize;
}
let mut charCounts: [f32; 6];
if currentMode == ASCII_ENCODATION as u32 {
charCounts = [0.0, 1.0, 1.0, 1.0, 1.0, 1.25];
} else {
charCounts = [1.0, 2.0, 2.0, 2.0, 2.0, 2.25];
charCounts[currentMode as usize] = 0.0;
}
let mut charsProcessed = 0;
let mut mins = [0u8; 6];
let mut intCharCounts = [0u32; 6];
loop {
if (startpos + charsProcessed) == msg.chars().count() as u32 {
mins.fill(0);
intCharCounts.fill(0);
let min = findMinimums(&charCounts, &mut intCharCounts, u32::MAX, &mut mins);
let minCount = getMinimumCount(&mins);
if intCharCounts[ASCII_ENCODATION] == min {
return ASCII_ENCODATION;
}
if minCount == 1 {
if mins[BASE256_ENCODATION] > 0 {
return BASE256_ENCODATION;
}
if mins[EDIFACT_ENCODATION] > 0 {
return EDIFACT_ENCODATION;
}
if mins[TEXT_ENCODATION] > 0 {
return TEXT_ENCODATION;
}
if mins[X12_ENCODATION] > 0 {
return X12_ENCODATION;
}
}
return C40_ENCODATION;
}
let Some(c) = msg.chars().nth((startpos + charsProcessed) as usize) else {
break 0;
};
charsProcessed += 1;
if isDigit(c) {
charCounts[ASCII_ENCODATION] += 0.5;
} else if isExtendedASCII(c) {
charCounts[ASCII_ENCODATION] = charCounts[ASCII_ENCODATION].ceil();
charCounts[ASCII_ENCODATION] += 2.0;
} else {
charCounts[ASCII_ENCODATION] = charCounts[ASCII_ENCODATION].ceil();
charCounts[ASCII_ENCODATION] += 1.0;
}
if isNativeC40(c) {
charCounts[C40_ENCODATION] += 2.0 / 3.0;
} else if isExtendedASCII(c) {
charCounts[C40_ENCODATION] += 8.0 / 3.0;
} else {
charCounts[C40_ENCODATION] += 4.0 / 3.0;
}
if isNativeText(c) {
charCounts[TEXT_ENCODATION] += 2.0 / 3.0;
} else if isExtendedASCII(c) {
charCounts[TEXT_ENCODATION] += 8.0 / 3.0;
} else {
charCounts[TEXT_ENCODATION] += 4.0 / 3.0;
}
if isNativeX12(c) {
charCounts[X12_ENCODATION] += 2.0 / 3.0;
} else if isExtendedASCII(c) {
charCounts[X12_ENCODATION] += 13.0 / 3.0;
} else {
charCounts[X12_ENCODATION] += 10.0 / 3.0;
}
if isNativeEDIFACT(c) {
charCounts[EDIFACT_ENCODATION] += 3.0 / 4.0;
} else if isExtendedASCII(c) {
charCounts[EDIFACT_ENCODATION] += 17.0 / 4.0;
} else {
charCounts[EDIFACT_ENCODATION] += 13.0 / 4.0;
}
if isSpecialB256(c) {
charCounts[BASE256_ENCODATION] += 4.0;
} else {
charCounts[BASE256_ENCODATION] += 1.0;
}
if charsProcessed >= 4 {
mins.fill(0);
intCharCounts.fill(0);
findMinimums(&charCounts, &mut intCharCounts, u32::MAX, &mut mins);
if intCharCounts[ASCII_ENCODATION]
< min5(
intCharCounts[BASE256_ENCODATION],
intCharCounts[C40_ENCODATION],
intCharCounts[TEXT_ENCODATION],
intCharCounts[X12_ENCODATION],
intCharCounts[EDIFACT_ENCODATION],
)
{
return ASCII_ENCODATION;
}
if intCharCounts[BASE256_ENCODATION] < intCharCounts[ASCII_ENCODATION]
|| intCharCounts[BASE256_ENCODATION] + 1
< min4(
intCharCounts[C40_ENCODATION],
intCharCounts[TEXT_ENCODATION],
intCharCounts[X12_ENCODATION],
intCharCounts[EDIFACT_ENCODATION],
)
{
return BASE256_ENCODATION;
}
if intCharCounts[EDIFACT_ENCODATION] + 1
< min5(
intCharCounts[BASE256_ENCODATION],
intCharCounts[C40_ENCODATION],
intCharCounts[TEXT_ENCODATION],
intCharCounts[X12_ENCODATION],
intCharCounts[ASCII_ENCODATION],
)
{
return EDIFACT_ENCODATION;
}
if intCharCounts[TEXT_ENCODATION] + 1
< min5(
intCharCounts[BASE256_ENCODATION],
intCharCounts[C40_ENCODATION],
intCharCounts[EDIFACT_ENCODATION],
intCharCounts[X12_ENCODATION],
intCharCounts[ASCII_ENCODATION],
)
{
return TEXT_ENCODATION;
}
if intCharCounts[X12_ENCODATION] + 1
< min5(
intCharCounts[BASE256_ENCODATION],
intCharCounts[C40_ENCODATION],
intCharCounts[EDIFACT_ENCODATION],
intCharCounts[TEXT_ENCODATION],
intCharCounts[ASCII_ENCODATION],
)
{
return X12_ENCODATION;
}
if intCharCounts[C40_ENCODATION] + 1
< min4(
intCharCounts[ASCII_ENCODATION],
intCharCounts[BASE256_ENCODATION],
intCharCounts[EDIFACT_ENCODATION],
intCharCounts[TEXT_ENCODATION],
)
{
if intCharCounts[C40_ENCODATION] < intCharCounts[X12_ENCODATION] {
return C40_ENCODATION;
}
if intCharCounts[C40_ENCODATION] == intCharCounts[X12_ENCODATION] {
let mut _p = startpos + charsProcessed + 1;
for tc in msg.chars() {
if isX12TermSep(tc) {
return X12_ENCODATION;
}
if !isNativeX12(tc) {
break;
}
_p += 1;
}
return C40_ENCODATION;
}
}
}
}
}
fn min5(f1: u32, f2: u32, f3: u32, f4: u32, f5: u32) -> u32 {
min4(f1, f2, f3, f4).min(f5)
}
fn min4(f1: u32, f2: u32, f3: u32, f4: u32) -> u32 {
f1.min(f2.min(f3.min(f4)))
}
fn findMinimums(
charCounts: &[f32; 6],
intCharCounts: &mut [u32; 6],
min: u32,
mins: &mut [u8],
) -> u32 {
let mut min = min;
for i in 0..6 {
intCharCounts[i] = charCounts[i].ceil() as u32;
let current = intCharCounts[i]; if min > current {
min = current;
mins.fill(0);
}
if min == current {
mins[i] += 1;
}
}
min
}
fn getMinimumCount(mins: &[u8]) -> u32 {
mins.iter().take(6).sum::<u8>() as u32
}
pub fn isDigit(ch: char) -> bool {
ch.is_ascii_digit()
}
pub fn isExtendedASCII(ch: char) -> bool {
(ch as u8) >= 128 }
pub fn isNativeC40(ch: char) -> bool {
(ch == ' ') || ch.is_ascii_digit() || ch.is_ascii_uppercase()
}
pub fn isNativeText(ch: char) -> bool {
(ch == ' ') || ch.is_ascii_digit() || ch.is_ascii_lowercase()
}
pub fn isNativeX12(ch: char) -> bool {
isX12TermSep(ch) || (ch == ' ') || ch.is_ascii_digit() || ch.is_ascii_uppercase()
}
fn isX12TermSep(ch: char) -> bool {
(ch == '\r') || (ch == '*')
|| (ch == '>')
}
pub fn isNativeEDIFACT(ch: char) -> bool {
(' '..='^').contains(&ch)
}
fn isSpecialB256(_ch: char) -> bool {
false }
pub fn determineConsecutiveDigitCount(msg: &str, startpos: u32) -> u32 {
let len = msg.chars().count(); let mut idx = startpos;
while (idx as usize) < len && isDigit(msg.chars().nth(idx as usize).unwrap_or_default()) {
idx += 1;
}
idx - startpos
}
pub fn illegalCharacter(c: char) -> Result<()> {
Err(Exceptions::illegal_argument_with(format!(
"Illegal character: {c} (0x{c})"
)))
}