use std::fmt;
use crate::{
Exceptions,
common::{BitMatrix, Result},
qrcode::cpp_port::Type,
};
use super::{ErrorCorrectionLevel, FormatInformation};
use once_cell::sync::Lazy;
pub type VersionRef = &'static Version;
pub static VERSIONS: Lazy<Box<[Version]>> = Lazy::new(Version::buildVersions);
pub static MICRO_VERSIONS: Lazy<Box<[Version]>> = Lazy::new(Version::build_micro_versions);
pub static MODEL1_VERSIONS: Lazy<Box<[Version]>> = Lazy::new(Version::build_model1_versions);
pub static RMQR_VERSIONS: Lazy<Box<[Version]>> = Lazy::new(Version::build_rmqr_versions);
pub const VERSION_DECODE_INFO: [u32; 34] = [
0x07C94, 0x085BC, 0x09A99, 0x0A4D3, 0x0BBF6, 0x0C762, 0x0D847, 0x0E60D, 0x0F928, 0x10B78,
0x1145D, 0x12A17, 0x13532, 0x149A6, 0x15683, 0x168C9, 0x177EC, 0x18EC4, 0x191E1, 0x1AFAB,
0x1B08E, 0x1CC1A, 0x1D33F, 0x1ED75, 0x1F250, 0x209D5, 0x216F0, 0x228BA, 0x2379F, 0x24B0B,
0x2542E, 0x26A64, 0x27541, 0x28C69,
];
#[derive(Debug)]
pub struct Version {
versionNumber: u32,
alignmentPatternCenters: Box<[u32]>,
ecBlocks: Box<[ECBlocks]>,
totalCodewords: u32,
pub(crate) qr_type: Type,
}
impl Version {
pub(super) fn new(
versionNumber: u32,
alignmentPatternCenters: Box<[u32]>,
ecBlocks: [ECBlocks; 4],
) -> Self {
let mut total = 0;
let ecCodewords = ecBlocks[1].getECCodewordsPerBlock();
let ecbArray = ecBlocks[1].getECBlocks();
for ecb in ecbArray {
total += ecb.getCount() * (ecb.getDataCodewords() + ecCodewords);
}
Self {
versionNumber,
alignmentPatternCenters,
qr_type: if ecBlocks[0].getECCodewordsPerBlock() != 0 {
Type::Model2
} else {
Type::RectMicro
},
ecBlocks: Box::new(ecBlocks),
totalCodewords: total,
}
}
pub(super) fn without_alignment_patterns(
versionNumber: u32,
ecBlocks: Box<[ECBlocks]>,
) -> Self {
let mut total = 0;
let ecCodewords = ecBlocks[0].getECCodewordsPerBlock();
let ecbArray = ecBlocks[0].getECBlocks();
for ecb_array_element in ecbArray {
total +=
ecb_array_element.getCount() * (ecb_array_element.getDataCodewords() + ecCodewords);
}
let symbol_type = if ecBlocks[0].getECCodewordsPerBlock() < 7
|| ecBlocks[0].getECCodewordsPerBlock() == 8
{
Type::Micro
} else {
Type::Model1
};
Self {
versionNumber,
alignmentPatternCenters: Box::default(),
ecBlocks,
totalCodewords: total,
qr_type: symbol_type,
}
}
pub const fn getVersionNumber(&self) -> u32 {
self.versionNumber
}
pub const fn getAlignmentPatternCenters(&self) -> &[u32] {
&self.alignmentPatternCenters
}
pub const fn getTotalCodewords(&self) -> u32 {
self.totalCodewords
}
pub fn getDimensionForVersion(&self) -> u32 {
Self::DimensionOfVersion(self.versionNumber, self.qr_type == Type::Micro)
}
pub const fn getECBlocksForLevel(&self, ecLevel: ErrorCorrectionLevel) -> &ECBlocks {
if ecLevel.get_ordinal() as usize >= self.ecBlocks.len() {
return &self.ecBlocks[ecLevel.get_ordinal() as usize % self.ecBlocks.len()];
}
&self.ecBlocks[ecLevel.get_ordinal() as usize]
}
pub fn getProvisionalVersionForDimension(dimension: u32) -> Result<VersionRef> {
if dimension % 4 != 1 || dimension < 17 {
return Err(Exceptions::format_with("dimension incorrect"));
}
Self::getVersionForNumber((dimension - 17) / 4)
}
pub fn getVersionForNumber(versionNumber: u32) -> Result<VersionRef> {
if !(1..=40).contains(&versionNumber) {
return Err(Exceptions::illegal_argument_with("version out of spec"));
}
Ok(&VERSIONS[versionNumber as usize - 1])
}
pub fn decodeVersionInformation(versionBits: u32) -> Result<VersionRef> {
let mut bestDifference = u32::MAX;
let mut bestVersion = 0;
for i in 0..VERSION_DECODE_INFO.len() as u32 {
let targetVersion = VERSION_DECODE_INFO[i as usize];
if targetVersion == versionBits {
return Self::getVersionForNumber(i + 7);
}
let bitsDifference = FormatInformation::numBitsDiffering(versionBits, targetVersion);
if bitsDifference < bestDifference {
bestVersion = i + 7;
bestDifference = bitsDifference;
}
}
if bestDifference <= 3 {
return Self::getVersionForNumber(bestVersion);
}
Err(Exceptions::NOT_FOUND)
}
pub fn buildFunctionPattern(&self) -> Result<BitMatrix> {
if self.isRMQR() {
let size = Version::SymbolSize(self.versionNumber, Type::RectMicro);
let mut bitMatrix = BitMatrix::new(size.x as u32, size.y as u32)?;
bitMatrix.setRegion(0, 0, size.x as u32, 1)?; bitMatrix.setRegion(0, (size.y - 1) as u32, size.x as u32, 1)?; bitMatrix.setRegion(0, 1, 1, (size.y - 2) as u32)?; bitMatrix.setRegion((size.x - 1) as u32, 1, 1, (size.y - 2) as u32)?;
let max = self.alignmentPatternCenters.len(); for x in 0..max {
let cx = self.alignmentPatternCenters[x];
bitMatrix.setRegion(cx - 1, 1, 3, 2)?; bitMatrix.setRegion(cx - 1, (size.y - 3) as u32, 3, 2)?; bitMatrix.setRegion(cx, 3, 1, (size.y - 6) as u32)?; }
bitMatrix.setRegion(1, 1, 8 - 1, 8 - 1 - u32::from(size.y == 7))?; bitMatrix.setRegion(8, 1, 3, 5)?;
bitMatrix.setRegion(11, 1, 1, 3)?;
bitMatrix.setRegion((size.x - 5) as u32, (size.y - 5) as u32, 5 - 1, 5 - 1)?;
bitMatrix.setRegion((size.x - 8) as u32, (size.y - 6) as u32, 3, 5)?;
bitMatrix.setRegion((size.x - 5) as u32, (size.y - 6) as u32, 3, 1)?;
bitMatrix.set((size.x - 2) as u32, 1);
if size.y > 9 {
bitMatrix.set(1, (size.y - 2) as u32);
}
return Ok(bitMatrix);
}
let dimension = self.getDimensionForVersion();
let mut bitMatrix = BitMatrix::with_single_dimension(dimension)?;
bitMatrix.setRegion(0, 0, 9, 9)?;
if self.qr_type != Type::Micro {
bitMatrix.setRegion(dimension - 8, 0, 8, 9)?;
bitMatrix.setRegion(0, dimension - 8, 9, 8)?;
let max = self.alignmentPatternCenters.len();
for x in 0..max {
let i = self.alignmentPatternCenters[x] - 2;
for y in 0..max {
if (x != 0 || (y != 0 && y != max - 1)) && (x != max - 1 || y != 0) {
bitMatrix.setRegion(self.alignmentPatternCenters[y] - 2, i, 5, 5)?;
}
}
}
bitMatrix.setRegion(6, 9, 1, dimension - 17)?;
bitMatrix.setRegion(9, 6, dimension - 17, 1)?;
if self.versionNumber > 6 {
bitMatrix.setRegion(dimension - 11, 0, 3, 6)?;
bitMatrix.setRegion(0, dimension - 11, 6, 3)?;
}
} else {
bitMatrix.setRegion(9, 0, dimension - 9, 1)?;
bitMatrix.setRegion(0, 9, 1, dimension - 9)?;
}
Ok(bitMatrix)
}
}
impl fmt::Display for Version {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.versionNumber)
}
}
#[derive(Debug, Clone)]
pub struct ECBlocks {
ecCodewordsPerBlock: u32,
ecBlocks: Box<[ECB]>,
}
impl ECBlocks {
pub const fn new(ecCodewordsPerBlock: u32, ecBlocks: Box<[ECB]>) -> Self {
Self {
ecCodewordsPerBlock,
ecBlocks,
}
}
pub const fn getECCodewordsPerBlock(&self) -> u32 {
self.ecCodewordsPerBlock
}
pub fn getNumBlocks(&self) -> u32 {
let mut total = 0;
for ecBlock in self.ecBlocks.iter() {
total += ecBlock.getCount();
}
total
}
pub fn getTotalECCodewords(&self) -> u32 {
self.ecCodewordsPerBlock * self.getNumBlocks()
}
pub fn getECBlocks(&self) -> &[ECB] {
&self.ecBlocks
}
}
#[derive(Debug, Clone, Copy)]
pub struct ECB {
count: u32,
dataCodewords: u32,
}
impl ECB {
pub const fn new(count: u32, dataCodewords: u32) -> Self {
Self {
count,
dataCodewords,
}
}
pub const fn getCount(&self) -> u32 {
self.count
}
pub const fn getDataCodewords(&self) -> u32 {
self.dataCodewords
}
}