//! QR code encoding functionality.
//!
//! This module provides the core logic for encoding data into QR codes, supporting the QR Code Model
//! 2 specification. It includes structs and functions for creating QR codes with customizable versions
//! (1–40), error correction levels, and data modes (numeric, alphanumeric, byte, ECI).
#![forbid(unsafe_code)]
#![allow(unused_assignments)]
#![allow(dead_code)]
use core::convert::TryFrom;

/*---- QrCode functionality ----*/

/// A QR Code symbol, representing a square grid of dark and light modules.
///
/// This struct supports QR Code Model 2, covering versions 1 to 40, all four error correction levels,
/// and four encoding modes (numeric, alphanumeric, byte, ECI). Instances are immutable after creation.
///
/// # Creation
///
/// - High-level: Use [`encode_text`] or [`encode_binary`].
/// - Mid-level: Use [`encode_segments_to_codewords`] and [`encode_codewords`].
/// - Low-level: Directly construct with [`encode_codewords`].
///
/// # Example
///
/// ```rust
/// use qirust::qrcode::{QrCode, QrCodeEcc, Version};
///
/// let mut outbuffer = vec![0u8; Version::MAX.buffer_len()];
/// let mut tempbuffer = vec![0u8; Version::MAX.buffer_len()];
///
/// let qr = QrCode::encode_text(
///     "Hello, World!",
///     &mut tempbuffer,
///     &mut outbuffer,
///     QrCodeEcc::Low,
///     Version::MIN,
///     Version::MAX,
///     None,
///     true,
/// ).unwrap();
///
/// println!("Version: {}", qr.version().value());
/// ```
pub struct QrCode<'a> {
    // The width and height of this QR Code, measured in modules, between
    // 21 and 177 (inclusive). This is equal to version * 4 + 17.
    size: &'a mut u8,

    // The modules of this QR Code (0 = light, 1 = dark), packed bitwise into bytes.
    // Immutable after constructor finishes. Accessed through get_module().
    modules: &'a mut [u8],
}

impl<'a> QrCode<'a> {
    /*---- Static factory functions (high level) ----*/

    /// Encodes a text string into a QR code.
    ///
    /// Automatically selects the smallest version within the given range that can hold the data.
    /// If `boostecl` is `true`, the error correction level may be increased if it doesn't increase
    /// the version. The `mask` can be `None` for automatic selection (slower) or a value from 0 to 7.
    ///
    /// # Parameters
    ///
    /// - `text`: The text to encode.
    /// - `tempbuffer`: Temporary buffer, at least [`Version::MAX.buffer_len`] bytes.
    /// - `outbuffer`: Output buffer, at least [`Version::MAX.buffer_len`] bytes.
    /// - `ecl`: Error correction level.
    /// - `minversion`: Minimum QR code version.
    /// - `maxversion`: Maximum QR code version.
    /// - `mask`: Optional mask pattern.
    /// - `boostecl`: Whether to boost error correction if possible.
    ///
    /// # Returns
    ///
    /// A `Result` containing the QR code or a [`DataTooLong`] error if the data is too long.
    ///
    /// # Example
    ///
    /// ```rust
    /// use qirust::qrcode::{QrCode, QrCodeEcc, Version};
    ///
    /// let mut outbuffer = vec![0u8; Version::MAX.buffer_len()];
    /// let mut tempbuffer = vec![0u8; Version::MAX.buffer_len()];
    ///
    /// let qr = QrCode::encode_text(
    ///     "Hello, World!",
    ///     &mut tempbuffer,
    ///     &mut outbuffer,
    ///     QrCodeEcc::Low,
    ///     Version::MIN,
    ///     Version::MAX,
    ///     None,
    ///     true,
    /// ).unwrap();
    /// ```
    pub fn encode_text<'b>(
        text: &str,
        tempbuffer: &'b mut [u8],
        mut outbuffer: &'a mut [u8],
        ecl: QrCodeEcc,
        minversion: Version,
        maxversion: Version,
        mask: Option<Mask>,
        boostecl: bool
    ) -> Result<QrCode<'a>, DataTooLong> {
        let minlen: usize = outbuffer.len().min(tempbuffer.len());
        outbuffer = &mut outbuffer[..minlen];

        let textlen: usize = text.len(); // In bytes
        if textlen == 0 {
            let (datacodewordslen, ecl, version) = QrCode::encode_segments_to_codewords(
                &[],
                outbuffer,
                ecl,
                minversion,
                maxversion,
                boostecl
            )?;
            return Ok(
                Self::encode_codewords(outbuffer, datacodewordslen, tempbuffer, ecl, version, mask)
            );
        }

        use QrSegmentMode::*;
        let buflen: usize = outbuffer.len();
        let seg: QrSegment = if
            QrSegment::is_numeric(text) &&
            QrSegment::calc_buffer_size(Numeric, textlen).map_or(false, |x| x <= buflen)
        {
            QrSegment::make_numeric(text, tempbuffer)
        } else if
            QrSegment::is_alphanumeric(text) &&
            QrSegment::calc_buffer_size(Alphanumeric, textlen).map_or(false, |x| x <= buflen)
        {
            QrSegment::make_alphanumeric(text, tempbuffer)
        } else if QrSegment::calc_buffer_size(Byte, textlen).map_or(false, |x| x <= buflen) {
            QrSegment::make_bytes(text.as_bytes())
        } else {
            return Err(DataTooLong::SegmentTooLong);
        };
        let (datacodewordslen, ecl, version) = QrCode::encode_segments_to_codewords(
            &[seg],
            outbuffer,
            ecl,
            minversion,
            maxversion,
            boostecl
        )?;
        Ok(Self::encode_codewords(outbuffer, datacodewordslen, tempbuffer, ecl, version, mask))
    }

    /// Encodes binary data into a QR code.
    ///
    /// Similar to [`encode_text`], but for arbitrary byte data. The input data must fit within the
    /// specified version range and error correction level.
    ///
    /// # Parameters
    ///
    /// - `dataandtempbuffer`: Buffer containing data (first `datalen` bytes) and temporary space.
    /// - `datalen`: Length of the data to encode.
    /// - `outbuffer`: Output buffer, at least [`Version::MAX.buffer_len`] bytes.
    /// - `ecl`: Error correction level.
    /// - `minversion`: Minimum QR code version.
    /// - `maxversion`: Maximum QR code version.
    /// - `mask`: Optional mask pattern.
    /// - `boostecl`: Whether to boost error correction if possible.
    ///
    /// # Returns
    ///
    /// A `Result` containing the QR code or a [`DataTooLong`] error.
    pub fn encode_binary<'b>(
        dataandtempbuffer: &'b mut [u8],
        datalen: usize,
        mut outbuffer: &'a mut [u8],
        ecl: QrCodeEcc,
        minversion: Version,
        maxversion: Version,
        mask: Option<Mask>,
        boostecl: bool
    ) -> Result<QrCode<'a>, DataTooLong> {
        assert!(datalen <= dataandtempbuffer.len(), "Invalid data length");
        let minlen: usize = outbuffer.len().min(dataandtempbuffer.len());
        outbuffer = &mut outbuffer[..minlen];

        if
            QrSegment::calc_buffer_size(QrSegmentMode::Byte, datalen).map_or(
                true,
                |x| x > outbuffer.len()
            )
        {
            return Err(DataTooLong::SegmentTooLong);
        }
        let seg: QrSegment = QrSegment::make_bytes(&dataandtempbuffer[..datalen]);
        let (datacodewordslen, ecl, version) = QrCode::encode_segments_to_codewords(
            &[seg],
            outbuffer,
            ecl,
            minversion,
            maxversion,
            boostecl
        )?;
        Ok(
            Self::encode_codewords(
                outbuffer,
                datacodewordslen,
                dataandtempbuffer,
                ecl,
                version,
                mask
            )
        )
    }

    /*---- Static factory functions (mid level) ----*/

    /// Returns an intermediate state representing the given segments
    /// with the given encoding parameters being encoded into codewords.
    ///
    /// The smallest possible QR Code version within the given range is automatically
    /// chosen for the output. Iff boostecl is `true`, then the ECC level of the result
    /// may be higher than the ecl argument if it can be done without increasing the
    /// version. The mask number is either between 0 to 7 (inclusive) to force that
    /// mask, or `None` to automatically choose an appropriate mask (which may be slow).
    ///
    /// This function exists to allow segments to use parts of a temporary buffer,
    /// then have the segments be encoded to an output buffer, then invalidate all the segments,
    /// and finally have the output buffer and temporary buffer be encoded to a QR Code.
    pub fn encode_segments_to_codewords(
        segs: &[QrSegment],
        outbuffer: &'a mut [u8],
        mut ecl: QrCodeEcc,
        minversion: Version,
        maxversion: Version,
        boostecl: bool
    ) -> Result<(usize, QrCodeEcc, Version), DataTooLong> {
        assert!(minversion <= maxversion, "Invalid value");
        assert!(
            outbuffer.len() >= QrCode::get_num_data_codewords(maxversion, ecl),
            "Invalid buffer length"
        );

        // Find the minimal version number to use
        let mut version: Version = minversion;
        let datausedbits: usize = loop {
            let datacapacitybits: usize = QrCode::get_num_data_codewords(version, ecl) * 8; // Number of data bits available
            let dataused: Option<usize> = QrSegment::get_total_bits(segs, version);
            if dataused.map_or(false, |n| n <= datacapacitybits) {
                break dataused.unwrap(); // This version number is found to be suitable
            } else if version >= maxversion {
                // All versions in the range could not fit the given data
                return Err(match dataused {
                    None => DataTooLong::SegmentTooLong,
                    Some(n) => DataTooLong::DataOverCapacity(n, datacapacitybits),
                });
            } else {
                version = Version::new(version.value() + 1);
            }
        };

        // Increase the error correction level while the data still fits in the current version number
        for &newecl in &[QrCodeEcc::Medium, QrCodeEcc::Quartile, QrCodeEcc::High] {
            // From low to high
            if boostecl && datausedbits <= QrCode::get_num_data_codewords(version, newecl) * 8 {
                ecl = newecl;
            }
        }

        // Concatenate all segments to create the data bit string
        let datacapacitybits: usize = QrCode::get_num_data_codewords(version, ecl) * 8;
        let mut bb = BitBuffer::new(&mut outbuffer[..datacapacitybits / 8]);
        for seg in segs {
            bb.append_bits(seg.mode.mode_bits(), 4);
            bb.append_bits(
                u32::try_from(seg.numchars).unwrap(),
                seg.mode.num_char_count_bits(version)
            );
            for i in 0..seg.bitlength {
                let bit: u8 = (seg.data[i >> 3] >> (7 - (i & 7))) & 1;
                bb.append_bits(bit.into(), 1);
            }
        }
        debug_assert_eq!(bb.length, datausedbits);

        // Add terminator and pad up to a byte if applicable
        let numzerobits: usize = core::cmp::min(4, datacapacitybits - bb.length);
        bb.append_bits(0, u8::try_from(numzerobits).unwrap());
        let numzerobits: usize = bb.length.wrapping_neg() & 7;
        bb.append_bits(0, u8::try_from(numzerobits).unwrap());
        debug_assert_eq!(bb.length % 8, 0);

        // Pad with alternating bytes until data capacity is reached
        for &padbyte in [0xec, 0x11].iter().cycle() {
            if bb.length >= datacapacitybits {
                break;
            }
            bb.append_bits(padbyte, 8);
        }
        Ok((bb.length / 8, ecl, version))
    }

    /*---- Constructor (low level) ----*/

    /// Creates a new QR Code with the given version number,
    /// error correction level, data codeword bytes, and mask number.
    ///
    /// This is a low-level API that most users should not use directly.
    /// A mid-level API is the `encode_segments_to_codewords()` function.
    pub fn encode_codewords<'b>(
        mut datacodewordsandoutbuffer: &'a mut [u8],
        datacodewordslen: usize,
        mut tempbuffer: &'b mut [u8],
        ecl: QrCodeEcc,
        version: Version,
        mut msk: Option<Mask>
    ) -> QrCode<'a> {
        datacodewordsandoutbuffer = &mut datacodewordsandoutbuffer[..version.buffer_len()];
        tempbuffer = &mut tempbuffer[..version.buffer_len()];

        // Compute ECC
        let rawcodewords: usize = QrCode::get_num_raw_data_modules(version) / 8;
        assert!(datacodewordslen <= rawcodewords);
        let (data, temp) = datacodewordsandoutbuffer.split_at_mut(datacodewordslen);
        let allcodewords = Self::add_ecc_and_interleave(data, version, ecl, temp, tempbuffer);

        // Draw modules
        let mut result: QrCode = QrCode::<'a>::function_modules_marked(
            datacodewordsandoutbuffer,
            version
        );
        result.draw_codewords(allcodewords);
        result.draw_light_function_modules();
        let funcmods: QrCode = QrCode::<'b>::function_modules_marked(tempbuffer, version); // Just a grid, not a real QR Code

        // Do masking
        if msk.is_none() {
            // Automatically choose best mask
            let mut minpenalty = i32::MAX;
            for i in 0u8..8 {
                let i = Mask::new(i);
                result.apply_mask(&funcmods, i);
                result.draw_format_bits(ecl, i);
                let penalty: i32 = result.get_penalty_score();
                if penalty < minpenalty {
                    msk = Some(i);
                    minpenalty = penalty;
                }
                result.apply_mask(&funcmods, i); // Undoes the mask due to XOR
            }
        }
        let msk: Mask = msk.unwrap();
        result.apply_mask(&funcmods, msk); // Apply the final choice of mask
        result.draw_format_bits(ecl, msk); // Overwrite old format bits
        result
    }

    /*---- Public methods ----*/

    /// Returns this QR Code's version, in the range [1, 40].
    pub fn version(&self) -> Version {
        Version::new((*self.size - 17) / 4)
    }

    /// Returns this QR Code's size, in the range [21, 177].
    pub fn size(&self) -> i32 {
        i32::from(*self.size)
    }

    /// Returns this QR Code's error correction level.
    pub fn error_correction_level(&self) -> QrCodeEcc {
        let index =
            (usize::from(self.get_module_bounded(0, 8)) << 1) |
            (usize::from(self.get_module_bounded(1, 8)) << 0);
        use QrCodeEcc::*;
        [Medium, Low, High, Quartile][index]
    }

    /// Returns this QR Code's mask, in the range [0, 7].
    pub fn mask(&self) -> Mask {
        Mask::new(
            (u8::from(self.get_module_bounded(2, 8)) << 2) |
                (u8::from(self.get_module_bounded(3, 8)) << 1) |
                (u8::from(self.get_module_bounded(4, 8)) << 0)
        )
    }

    /// Returns the color of the module at the given coordinates.
    ///
    /// Returns `true` for dark modules and `false` for light modules. Coordinates outside the QR
    /// code's bounds return `false`.
    ///
    /// # Parameters
    ///
    /// - `x`: X-coordinate (0 is left).
    /// - `y`: Y-coordinate (0 is top).
    pub fn get_module(&self, x: i32, y: i32) -> bool {
        let range = 0..self.size();
        range.contains(&x) && range.contains(&y) && self.get_module_bounded(x as u8, y as u8)
    }

    // Returns the color of the module at the given coordinates, which must be in bounds.
    fn get_module_bounded(&self, x: u8, y: u8) -> bool {
        let range = 0..*self.size;
        assert!(range.contains(&x) && range.contains(&y));
        let index = usize::from(y) * usize::from(*self.size) + usize::from(x);
        let byteindex: usize = index >> 3;
        let bitindex: usize = index & 7;
        get_bit(self.modules[byteindex].into(), bitindex as u8)
    }

    // Sets the color of the module at the given coordinates, doing nothing if out of bounds.
    fn set_module_unbounded(&mut self, x: i32, y: i32, isdark: bool) {
        let range = 0..self.size();
        if range.contains(&x) && range.contains(&y) {
            self.set_module_bounded(x as u8, y as u8, isdark);
        }
    }

    // Sets the color of the module at the given coordinates, which must be in bounds.
    fn set_module_bounded(&mut self, x: u8, y: u8, isdark: bool) {
        let range = 0..*self.size;
        assert!(range.contains(&x) && range.contains(&y));
        let index = usize::from(y) * usize::from(*self.size) + usize::from(x);
        let byteindex: usize = index >> 3;
        let bitindex: usize = index & 7;
        if isdark {
            self.modules[byteindex] |= 1u8 << bitindex;
        } else {
            self.modules[byteindex] &= !(1u8 << bitindex);
        }
    }

    /*---- Error correction code generation ----*/

    // Appends error correction bytes to each block of the given data array, then interleaves
    // bytes from the blocks, stores them in the output array, and returns a slice of resultbuf.
    // temp is used as a temporary work area and will be clobbered by this function.
    fn add_ecc_and_interleave<'b>(
        data: &[u8],
        ver: Version,
        ecl: QrCodeEcc,
        temp: &mut [u8],
        resultbuf: &'b mut [u8]
    ) -> &'b [u8] {
        assert_eq!(data.len(), QrCode::get_num_data_codewords(ver, ecl));

        // Calculate parameter numbers
        let numblocks: usize = QrCode::table_get(&NUM_ERROR_CORRECTION_BLOCKS, ver, ecl);
        let blockecclen: usize = QrCode::table_get(&ECC_CODEWORDS_PER_BLOCK, ver, ecl);
        let rawcodewords: usize = QrCode::get_num_raw_data_modules(ver) / 8;
        let numshortblocks: usize = numblocks - (rawcodewords % numblocks);
        let shortblockdatalen: usize = rawcodewords / numblocks - blockecclen;
        let result = &mut resultbuf[..rawcodewords];

        // Split data into blocks, calculate ECC, and interleave
        // (not concatenate) the bytes into a single sequence
        let rs = ReedSolomonGenerator::new(blockecclen);
        let mut dat: &[u8] = data;
        let ecc: &mut [u8] = &mut temp[..blockecclen]; // Temporary storage
        for i in 0..numblocks {
            let datlen: usize = shortblockdatalen + usize::from(i >= numshortblocks);
            rs.compute_remainder(&dat[..datlen], ecc);
            let mut k: usize = i;
            for j in 0..datlen {
                // Copy data
                if j == shortblockdatalen {
                    k -= numshortblocks;
                }
                result[k] = dat[j];
                k += numblocks;
            }
            let mut k: usize = data.len() + i;
            for j in 0..blockecclen {
                // Copy ECC
                result[k] = ecc[j];
                k += numblocks;
            }
            dat = &dat[datlen..];
        }
        debug_assert_eq!(dat.len(), 0);
        result
    }

    /*---- Drawing function modules ----*/

    // Creates a QR Code grid with light modules for the given
    // version's size, then marks every function module as dark.
    fn function_modules_marked(outbuffer: &'a mut [u8], ver: Version) -> Self {
        assert_eq!(outbuffer.len(), ver.buffer_len());
        let parts: (&mut u8, &mut [u8]) = outbuffer.split_first_mut().unwrap();
        let mut result = Self {
            size: parts.0,
            modules: parts.1,
        };
        let size: u8 = ver.value() * 4 + 17;
        *result.size = size;
        result.modules.fill(0);

        // Fill horizontal and vertical timing patterns
        result.fill_rectangle(6, 0, 1, size);
        result.fill_rectangle(0, 6, size, 1);

        // Fill 3 finder patterns (all corners except bottom right) and format bits
        result.fill_rectangle(0, 0, 9, 9);
        result.fill_rectangle(size - 8, 0, 8, 9);
        result.fill_rectangle(0, size - 8, 9, 8);

        // Fill numerous alignment patterns
        let mut alignpatposbuf = [0u8; 7];
        let alignpatpos: &[u8] = result.get_alignment_pattern_positions(&mut alignpatposbuf);
        for (i, pos0) in alignpatpos.iter().enumerate() {
            for (j, pos1) in alignpatpos.iter().enumerate() {
                // Don't draw on the three finder corners
                if
                    !(
                        (i == 0 && j == 0) ||
                        (i == 0 && j == alignpatpos.len() - 1) ||
                        (i == alignpatpos.len() - 1 && j == 0)
                    )
                {
                    result.fill_rectangle(pos0 - 2, pos1 - 2, 5, 5);
                }
            }
        }

        // Fill version blocks
        if ver.value() >= 7 {
            result.fill_rectangle(size - 11, 0, 3, 6);
            result.fill_rectangle(0, size - 11, 6, 3);
        }

        result
    }

    // Draws light function modules and possibly some dark modules onto this QR Code, without changing
    // non-function modules. This does not draw the format bits. This requires all function modules to be previously
    // marked dark (namely by function_modules_marked()), because this may skip redrawing dark function modules.
    fn draw_light_function_modules(&mut self) {
        // Draw horizontal and vertical timing patterns
        let size: u8 = *self.size;
        for i in (7..size - 7).step_by(2) {
            self.set_module_bounded(6, i, false);
            self.set_module_bounded(i, 6, false);
        }

        // Draw 3 finder patterns (all corners except bottom right; overwrites some timing modules)
        for dy in -4i32..=4 {
            for dx in -4i32..=4 {
                let dist: i32 = dx.abs().max(dy.abs());
                if dist == 2 || dist == 4 {
                    self.set_module_unbounded(3 + dx, 3 + dy, false);
                    self.set_module_unbounded(i32::from(size) - 4 + dx, 3 + dy, false);
                    self.set_module_unbounded(3 + dx, i32::from(size) - 4 + dy, false);
                }
            }
        }

        // Draw numerous alignment patterns
        let mut alignpatposbuf = [0u8; 7];
        let alignpatpos: &[u8] = self.get_alignment_pattern_positions(&mut alignpatposbuf);
        for (i, &pos0) in alignpatpos.iter().enumerate() {
            for (j, &pos1) in alignpatpos.iter().enumerate() {
                if
                    (i == 0 && j == 0) ||
                    (i == 0 && j == alignpatpos.len() - 1) ||
                    (i == alignpatpos.len() - 1 && j == 0)
                {
                    continue; // Don't draw on the three finder corners
                }
                for dy in -1..=1 {
                    for dx in -1..=1 {
                        self.set_module_bounded(
                            (i32::from(pos0) + dx) as u8,
                            (i32::from(pos1) + dy) as u8,
                            dx == 0 && dy == 0
                        );
                    }
                }
            }
        }

        // Draw version blocks
        let ver = u32::from(self.version().value()); // uint6, in the range [7, 40]
        if ver >= 7 {
            // Calculate error correction code and pack bits
            let bits: u32 = {
                let mut rem: u32 = ver;
                for _ in 0..12 {
                    rem = (rem << 1) ^ ((rem >> 11) * 0x1f25);
                }
                (ver << 12) | rem // uint18
            };
            debug_assert_eq!(bits >> 18, 0);

            // Draw two copies
            for i in 0u8..18 {
                let bit: bool = get_bit(bits, i);
                let a: u8 = size - 11 + (i % 3);
                let b: u8 = i / 3;
                self.set_module_bounded(a, b, bit);
                self.set_module_bounded(b, a, bit);
            }
        }
    }

    // Draws two copies of the format bits (with its own error correction code) based
    // on the given mask and error correction level. This always draws all modules of
    // the format bits, unlike draw_light_function_modules() which might skip dark modules.
    fn draw_format_bits(&mut self, ecl: QrCodeEcc, mask: Mask) {
        // Calculate error correction code and pack bits
        let bits: u32 = {
            // errcorrlvl is uint2, mask is uint3
            let data = u32::from((ecl.format_bits() << 3) | mask.value());
            let mut rem: u32 = data;
            for _ in 0..10 {
                rem = (rem << 1) ^ ((rem >> 9) * 0x537);
            }
            ((data << 10) | rem) ^ 0x5412 // uint15
        };
        debug_assert_eq!(bits >> 15, 0);

        // Draw first copy
        for i in 0..6 {
            self.set_module_bounded(8, i, get_bit(bits, i));
        }
        self.set_module_bounded(8, 7, get_bit(bits, 6));
        self.set_module_bounded(8, 8, get_bit(bits, 7));
        self.set_module_bounded(7, 8, get_bit(bits, 8));
        for i in 9..15 {
            self.set_module_bounded(14 - i, 8, get_bit(bits, i));
        }

        // Draw second copy
        let size: u8 = *self.size;
        for i in 0..8 {
            self.set_module_bounded(size - 1 - i, 8, get_bit(bits, i));
        }
        for i in 8..15 {
            self.set_module_bounded(8, size - 15 + i, get_bit(bits, i));
        }
        self.set_module_bounded(8, size - 8, true); // Always dark
    }

    // Sets every module in the range [left : left + width] * [top : top + height] to dark.
    fn fill_rectangle(&mut self, left: u8, top: u8, width: u8, height: u8) {
        for dy in 0..height {
            for dx in 0..width {
                self.set_module_bounded(left + dx, top + dy, true);
            }
        }
    }

    /*---- Drawing data modules and masking ----*/

    // Draws the raw codewords (including data and ECC) onto this QR Code. This requires the initial state of
    // the QR Code to be dark at function modules and light at codeword modules (including unused remainder bits).
    fn draw_codewords(&mut self, data: &[u8]) {
        assert_eq!(
            data.len(),
            QrCode::get_num_raw_data_modules(self.version()) / 8,
            "Illegal argument"
        );

        let size: i32 = self.size();
        let mut i: usize = 0; // Bit index into the data
        // Do the funny zigzag scan
        let mut right: i32 = size - 1;
        while right >= 1 {
            // Index of right column in each column pair
            if right == 6 {
                right = 5;
            }
            for vert in 0..size {
                // Vertical counter
                for j in 0..2 {
                    let x = (right - j) as u8; // Actual x coordinate
                    let upward: bool = ((right + 1) & 2) == 0;
                    let y = (if upward { size - 1 - vert } else { vert }) as u8; // Actual y coordinate
                    if !self.get_module_bounded(x, y) && i < data.len() * 8 {
                        self.set_module_bounded(
                            x,
                            y,
                            get_bit(data[i >> 3].into(), 7 - ((i as u8) & 7))
                        );
                        i += 1;
                    }
                    // If this QR Code has any remainder bits (0 to 7), they were assigned as
                    // 0/false/light by the constructor and are left unchanged by this method
                }
            }
            right -= 2;
        }
        debug_assert_eq!(i, data.len() * 8);
    }

    // XORs the codeword modules in this QR Code with the given mask pattern
    // and given pattern of function modules. The codeword bits must be drawn
    // before masking. Due to the arithmetic of XOR, calling apply_mask() with
    // the same mask value a second time will undo the mask. A final well-formed
    // QR Code needs exactly one (not zero, two, etc.) mask applied.
    fn apply_mask(&mut self, functionmodules: &QrCode, mask: Mask) {
        for y in 0..*self.size {
            for x in 0..*self.size {
                if functionmodules.get_module_bounded(x, y) {
                    continue;
                }
                let invert: bool = {
                    let x = i32::from(x);
                    let y = i32::from(y);
                    match mask.value() {
                        0 => (x + y) % 2 == 0,
                        1 => y % 2 == 0,
                        2 => x % 3 == 0,
                        3 => (x + y) % 3 == 0,
                        4 => (x / 3 + y / 2) % 2 == 0,
                        5 => ((x * y) % 2) + ((x * y) % 3) == 0,
                        6 => (((x * y) % 2) + ((x * y) % 3)) % 2 == 0,
                        7 => (((x + y) % 2) + ((x * y) % 3)) % 2 == 0,
                        _ => unreachable!(),
                    }
                };
                self.set_module_bounded(x, y, self.get_module_bounded(x, y) ^ invert);
            }
        }
    }

    // Calculates and returns the penalty score based on state of this QR Code's current modules.
    // This is used by the automatic mask choice algorithm to find the mask pattern that yields the lowest score.
    fn get_penalty_score(&self) -> i32 {
        let mut result: i32 = 0;
        let size: u8 = *self.size;

        // Adjacent modules in row having same color, and finder-like patterns
        for y in 0..size {
            let mut runcolor = false;
            let mut runx: i32 = 0;
            let mut runhistory = FinderPenalty::new(size);
            for x in 0..size {
                if self.get_module_bounded(x, y) == runcolor {
                    runx += 1;
                    if runx == 5 {
                        result += PENALTY_N1;
                    } else if runx > 5 {
                        result += 1;
                    }
                } else {
                    runhistory.add_history(runx);
                    if !runcolor {
                        result += runhistory.count_patterns() * PENALTY_N3;
                    }
                    runcolor = self.get_module_bounded(x, y);
                    runx = 1;
                }
            }
            result += runhistory.terminate_and_count(runcolor, runx) * PENALTY_N3;
        }
        // Adjacent modules in column having same color, and finder-like patterns
        for x in 0..size {
            let mut runcolor = false;
            let mut runy: i32 = 0;
            let mut runhistory = FinderPenalty::new(size);
            for y in 0..size {
                if self.get_module_bounded(x, y) == runcolor {
                    runy += 1;
                    if runy == 5 {
                        result += PENALTY_N1;
                    } else if runy > 5 {
                        result += 1;
                    }
                } else {
                    runhistory.add_history(runy);
                    if !runcolor {
                        result += runhistory.count_patterns() * PENALTY_N3;
                    }
                    runcolor = self.get_module_bounded(x, y);
                    runy = 1;
                }
            }
            result += runhistory.terminate_and_count(runcolor, runy) * PENALTY_N3;
        }

        // 2*2 blocks of modules having same color
        for y in 0..size - 1 {
            for x in 0..size - 1 {
                let color: bool = self.get_module_bounded(x, y);
                if
                    color == self.get_module_bounded(x + 1, y) &&
                    color == self.get_module_bounded(x, y + 1) &&
                    color == self.get_module_bounded(x + 1, y + 1)
                {
                    result += PENALTY_N2;
                }
            }
        }

        // Balance of dark and light modules
        let dark = self.modules
            .iter()
            .map(|x| x.count_ones())
            .sum::<u32>() as i32;
        let total = i32::from(size) * i32::from(size); // Note that size is odd, so dark/total != 1/2
        // Compute the smallest integer k >= 0 such that (45-5k)% <= dark/total <= (55+5k)%
        let k: i32 = ((dark * 20 - total * 10).abs() + total - 1) / total - 1;
        debug_assert!(0 <= k && k <= 9);
        result += k * PENALTY_N4;
        debug_assert!(0 <= result && result <= 2568888); // Non-tight upper bound based on default values of PENALTY_N1, ..., N4
        result
    }

    /*---- Private helper functions ----*/

    // Calculates and stores an ascending list of positions of alignment patterns
    // for this version number, returning a slice of resultbuf.
    // Each position is in the range [0,177), and are used on both the x and y axes.
    // This could be implemented as lookup table of 40 variable-length lists of unsigned bytes.
    fn get_alignment_pattern_positions<'b>(&self, resultbuf: &'b mut [u8; 7]) -> &'b [u8] {
        let ver: u8 = self.version().value();
        if ver == 1 {
            &resultbuf[..0]
        } else {
            let numalign: u8 = ver / 7 + 2;
            let step: u8 = if ver == 32 {
                26
            } else {
                ((ver * 4 + numalign * 2 + 1) / (numalign * 2 - 2)) * 2
            };
            let result = &mut resultbuf[..usize::from(numalign)];
            for i in 0..numalign - 1 {
                result[usize::from(i)] = *self.size - 7 - i * step;
            }
            *result.last_mut().unwrap() = 6;
            result.reverse();
            result
        }
    }

    // Returns the number of data bits that can be stored in a QR Code of the given version number, after
    // all function modules are excluded. This includes remainder bits, so it might not be a multiple of 8.
    // The result is in the range [208, 29648]. This could be implemented as a 40-entry lookup table.
    fn get_num_raw_data_modules(ver: Version) -> usize {
        let ver = usize::from(ver.value());
        let mut result: usize = (16 * ver + 128) * ver + 64;
        if ver >= 2 {
            let numalign: usize = ver / 7 + 2;
            result -= (25 * numalign - 10) * numalign - 55;
            if ver >= 7 {
                result -= 36;
            }
        }
        debug_assert!((208..=29648).contains(&result));
        result
    }

    // Returns the number of 8-bit data (i.e. not error correction) codewords contained in any
    // QR Code of the given version number and error correction level, with remainder bits discarded.
    // This stateless pure function could be implemented as a (40*4)-cell lookup table.
    fn get_num_data_codewords(ver: Version, ecl: QrCodeEcc) -> usize {
        QrCode::get_num_raw_data_modules(ver) / 8 -
            QrCode::table_get(&ECC_CODEWORDS_PER_BLOCK, ver, ecl) *
                QrCode::table_get(&NUM_ERROR_CORRECTION_BLOCKS, ver, ecl)
    }

    // Returns an entry from the given table based on the given values.
    fn table_get(table: &'static [[i8; 41]; 4], ver: Version, ecl: QrCodeEcc) -> usize {
        table[ecl.ordinal()][usize::from(ver.value())] as usize
    }
}

impl PartialEq for QrCode<'_> {
    fn eq(&self, other: &QrCode<'_>) -> bool {
        *self.size == *other.size && *self.modules == *other.modules
    }
}

impl Eq for QrCode<'_> {}

/*---- Helper struct for add_ecc_and_interleave() ----*/

struct ReedSolomonGenerator {
    // Polynomial coefficients are stored from highest to lowest power, excluding the leading term which is always 1.
    // For example the polynomial x^3 + 255x^2 + 8x + 93 is stored as the uint8 array [255, 8, 93].
    divisor: [u8; 30],

    // The degree of the divisor polynomial, in the range [1, 30].
    degree: usize,
}

impl ReedSolomonGenerator {
    // Creates a Reed-Solomon ECC generator polynomial for the given degree. This could be
    // implemented as a lookup table over all possible parameter values, instead of as an algorithm.
    fn new(degree: usize) -> Self {
        let mut result = Self {
            divisor: [0u8; 30],
            degree: degree,
        };
        assert!((1..=result.divisor.len()).contains(&degree), "Degree out of range");
        let divisor: &mut [u8] = &mut result.divisor[..degree];
        divisor[degree - 1] = 1; // Start off with the monomial x^0

        // Compute the product polynomial (x - r^0) * (x - r^1) * (x - r^2) * ... * (x - r^{degree-1}),
        // and drop the highest monomial term which is always 1x^degree.
        // Note that r = 0x02, which is a generator element of this field GF(2^8/0x11D).
        let mut root: u8 = 1;
        for _ in 0..degree {
            // Unused variable i
            // Multiply the current product by (x - r^i)
            for j in 0..degree {
                divisor[j] = Self::multiply(divisor[j], root);
                if j + 1 < divisor.len() {
                    divisor[j] ^= divisor[j + 1];
                }
            }
            root = Self::multiply(root, 0x02);
        }
        result
    }

    // Returns the Reed-Solomon error correction codeword for the given data polynomial and this divisor polynomial.
    fn compute_remainder(&self, data: &[u8], result: &mut [u8]) {
        assert_eq!(result.len(), self.degree);
        result.fill(0);
        for b in data {
            // Polynomial division
            let factor: u8 = b ^ result[0];
            result.copy_within(1.., 0);
            result[result.len() - 1] = 0;
            for (x, &y) in result.iter_mut().zip(self.divisor.iter()) {
                *x ^= Self::multiply(y, factor);
            }
        }
    }

    // Returns the product of the two given field elements modulo GF(2^8/0x11D).
    // All inputs are valid. This could be implemented as a 256*256 lookup table.
    fn multiply(x: u8, y: u8) -> u8 {
        // Russian peasant multiplication
        let mut z: u8 = 0;
        for i in (0..8).rev() {
            z = (z << 1) ^ ((z >> 7) * 0x1d);
            z ^= ((y >> i) & 1) * x;
        }
        z
    }
}

/*---- Helper struct for get_penalty_score() ----*/

struct FinderPenalty {
    qr_size: i32,
    run_history: [i32; 7],
}

impl FinderPenalty {
    pub fn new(size: u8) -> Self {
        Self {
            qr_size: i32::from(size),
            run_history: [0; 7],
        }
    }

    // Pushes the given value to the front and drops the last value.
    pub fn add_history(&mut self, mut currentrunlength: i32) {
        if self.run_history[0] == 0 {
            currentrunlength += self.qr_size; // Add light border to initial run
        }
        let len: usize = self.run_history.len();
        self.run_history.copy_within(0..len - 1, 1);
        self.run_history[0] = currentrunlength;
    }

    // Can only be called immediately after a light run is added, and returns either 0, 1, or 2.
    pub fn count_patterns(&self) -> i32 {
        let rh = &self.run_history;
        let n = rh[1];
        debug_assert!(n <= self.qr_size * 3);
        let core = n > 0 && rh[2] == n && rh[3] == n * 3 && rh[4] == n && rh[5] == n;
        i32::from(core && rh[0] >= n * 4 && rh[6] >= n) +
            i32::from(core && rh[6] >= n * 4 && rh[0] >= n)
    }

    // Must be called at the end of a line (row or column) of modules.
    pub fn terminate_and_count(mut self, currentruncolor: bool, mut currentrunlength: i32) -> i32 {
        if currentruncolor {
            // Terminate dark run
            self.add_history(currentrunlength);
            currentrunlength = 0;
        }
        currentrunlength += self.qr_size; // Add light border to final run
        self.add_history(currentrunlength);
        self.count_patterns()
    }
}

/*---- Constants and tables ----*/

// For use in get_penalty_score(), when evaluating which mask is best.
const PENALTY_N1: i32 = 3;
const PENALTY_N2: i32 = 3;
const PENALTY_N3: i32 = 40;
const PENALTY_N4: i32 = 10;

static ECC_CODEWORDS_PER_BLOCK: [[i8; 41]; 4] = [
    // Version: (note that index 0 is for padding, and is set to an illegal value)
    //0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40    Error correction level
    [
        -1, 7, 10, 15, 20, 26, 18, 20, 24, 30, 18, 20, 24, 26, 30, 22, 24, 28, 30, 28, 28, 28, 28, 30,
        30, 26, 28, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30,
    ], // Low
    [
        -1, 10, 16, 26, 18, 24, 16, 18, 22, 22, 26, 30, 22, 22, 24, 24, 28, 28, 26, 26, 26, 26, 28, 28,
        28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28,
    ], // Medium
    [
        -1, 13, 22, 18, 26, 18, 24, 18, 22, 20, 24, 28, 26, 24, 20, 30, 24, 28, 28, 26, 30, 28, 30, 30,
        30, 30, 28, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30,
    ], // Quartile
    [
        -1, 17, 28, 22, 16, 22, 28, 26, 26, 24, 28, 24, 28, 22, 24, 24, 30, 28, 28, 26, 28, 30, 24, 30,
        30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30,
    ], // High
];

static NUM_ERROR_CORRECTION_BLOCKS: [[i8; 41]; 4] = [
    // Version: (note that index 0 is for padding, and is set to an illegal value)
    //0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40    Error correction level
    [
        -1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 4, 4, 4, 4, 4, 6, 6, 6, 6, 7, 8, 8, 9, 9, 10, 12, 12, 12,
        13, 14, 15, 16, 17, 18, 19, 19, 20, 21, 22, 24, 25,
    ], // Low
    [
        -1, 1, 1, 1, 2, 2, 4, 4, 4, 5, 5, 5, 8, 9, 9, 10, 10, 11, 13, 14, 16, 17, 17, 18, 20, 21,
        23, 25, 26, 28, 29, 31, 33, 35, 37, 38, 40, 43, 45, 47, 49,
    ], // Medium
    [
        -1, 1, 1, 2, 2, 4, 4, 6, 6, 8, 8, 8, 10, 12, 16, 12, 17, 16, 18, 21, 20, 23, 23, 25, 27, 29,
        34, 34, 35, 38, 40, 43, 45, 48, 51, 53, 56, 59, 62, 65, 68,
    ], // Quartile
    [
        -1, 1, 1, 2, 4, 4, 4, 5, 6, 8, 8, 11, 11, 16, 16, 18, 16, 19, 21, 25, 25, 25, 34, 30, 32, 35,
        37, 40, 42, 45, 48, 51, 54, 57, 60, 63, 66, 70, 74, 77, 81,
    ], // High
];

/*---- QrCodeEcc functionality ----*/

/// Error correction level for a QR code.
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug)]
pub enum QrCodeEcc {
    /// Tolerates ~7% erroneous codewords.
    Low,
    /// Tolerates ~15% erroneous codewords.
    Medium,
    /// Tolerates ~25% erroneous codewords.
    Quartile,
    /// Tolerates ~30% erroneous codewords.
    High,
}

impl QrCodeEcc {
    // Returns an unsigned 2-bit integer (in the range 0 to 3).
    fn ordinal(self) -> usize {
        use QrCodeEcc::*;
        match self {
            Low => 0,
            Medium => 1,
            Quartile => 2,
            High => 3,
        }
    }

    // Returns an unsigned 2-bit integer (in the range 0 to 3).
    fn format_bits(self) -> u8 {
        use QrCodeEcc::*;
        match self {
            Low => 1,
            Medium => 0,
            Quartile => 3,
            High => 2,
        }
    }
}

/*---- QrSegment functionality ----*/

/// A segment of data in a QR code.
///
/// Supports numeric, alphanumeric, byte, or ECI modes. Segments are immutable and created using
/// factory functions like [`make_numeric`], [`make_alphanumeric`], or [`make_bytes`].
pub struct QrSegment<'a> {
    // The mode indicator of this segment. Accessed through mode().
    mode: QrSegmentMode,

    // The length of this segment's unencoded data. Measured in characters for
    // numeric/alphanumeric/kanji mode, bytes for byte mode, and 0 for ECI mode.
    // Not the same as the data's bit length. Accessed through num_chars().
    numchars: usize,

    // The data bits of this segment, packed in bitwise big endian.
    data: &'a [u8],

    // The number of valid data bits used in the buffer. Requires bitlength <= data.len() * 8.
    // The character count (numchars) must agree with the mode and the bit buffer length.
    bitlength: usize,
}

impl<'a> QrSegment<'a> {
    /// Creates a segment for binary data in byte mode.
    ///
    /// # Parameters
    ///
    /// - `data`: The byte data to encode.
    ///
    /// # Returns
    ///
    /// A new `QrSegment` in byte mode.
    pub fn make_bytes(data: &'a [u8]) -> Self {
        QrSegment::new(QrSegmentMode::Byte, data.len(), data, data.len().checked_mul(8).unwrap())
    }

    /// Creates a segment for a string of decimal digits in numeric mode.
    ///
    /// # Parameters
    ///
    /// - `text`: A string containing only digits (0–9).
    /// - `buf`: A buffer for storing encoded data.
    ///
    /// # Panics
    ///
    /// Panics if `text` contains non-digit characters.
    pub fn make_numeric(text: &str, buf: &'a mut [u8]) -> Self {
        let mut bb = BitBuffer::new(buf);
        let mut accumdata: u32 = 0;
        let mut accumcount: u8 = 0;
        for b in text.bytes() {
            assert!((b'0'..=b'9').contains(&b), "String contains non-numeric characters");
            accumdata = accumdata * 10 + u32::from(b - b'0');
            accumcount += 1;
            if accumcount == 3 {
                bb.append_bits(accumdata, 10);
                accumdata = 0;
                accumcount = 0;
            }
        }
        if accumcount > 0 {
            // 1 or 2 digits remaining
            bb.append_bits(accumdata, accumcount * 3 + 1);
        }
        QrSegment::new(QrSegmentMode::Numeric, text.len(), bb.data, bb.length)
    }

    /// Creates a segment for alphanumeric text.
    ///
    /// Allowed characters: 0–9, A–Z (uppercase), space, `$`, `%`, `*`, `+`, `-`, `.`, `/`, `:`.
    ///
    /// # Parameters
    ///
    /// - `text`: The alphanumeric text to encode.
    /// - `buf`: A buffer for storing encoded data.
    ///
    /// # Panics
    ///
    /// Panics if `text` contains invalid characters.
    pub fn make_alphanumeric(text: &str, buf: &'a mut [u8]) -> Self {
        let mut bb = BitBuffer::new(buf);
        let mut accumdata: u32 = 0;
        let mut accumcount: u8 = 0;
        for c in text.chars() {
            let i: usize = ALPHANUMERIC_CHARSET.find(c).expect(
                "String contains unencodable characters in alphanumeric mode"
            );
            accumdata = accumdata * 45 + u32::try_from(i).unwrap();
            accumcount += 1;
            if accumcount == 2 {
                bb.append_bits(accumdata, 11);
                accumdata = 0;
                accumcount = 0;
            }
        }
        if accumcount > 0 {
            // 1 character remaining
            bb.append_bits(accumdata, 6);
        }
        QrSegment::new(QrSegmentMode::Alphanumeric, text.len(), bb.data, bb.length)
    }

    /// Returns a segment representing an Extended Channel Interpretation
    /// (ECI) designator with the given assignment value.
    pub fn make_eci(assignval: u32, buf: &'a mut [u8]) -> Self {
        let mut bb = BitBuffer::new(buf);
        if assignval < 1 << 7 {
            bb.append_bits(assignval, 8);
        } else if assignval < 1 << 14 {
            bb.append_bits(0b10, 2);
            bb.append_bits(assignval, 14);
        } else if assignval < 1_000_000 {
            bb.append_bits(0b110, 3);
            bb.append_bits(assignval, 21);
        } else {
            panic!("ECI assignment value out of range");
        }
        QrSegment::new(QrSegmentMode::Eci, 0, bb.data, bb.length)
    }

    /*---- Constructor (low level) ----*/

    /// Creates a new QR Code segment with the given attributes and data.
    ///
    /// The character count (numchars) must agree with the mode and
    /// the bit buffer length, but the constraint isn't checked.
    pub fn new(mode: QrSegmentMode, numchars: usize, data: &'a [u8], bitlength: usize) -> Self {
        assert!(bitlength == 0 || (bitlength - 1) / 8 < data.len());
        Self { mode, numchars, data, bitlength }
    }

    /*---- Instance field getters ----*/

    /// Returns the mode indicator of this segment.
    pub fn mode(&self) -> QrSegmentMode {
        self.mode
    }

    /// Returns the character count field of this segment.
    pub fn num_chars(&self) -> usize {
        self.numchars
    }

    /*---- Other static functions ----*/

    /// Returns the number of bytes needed for the data buffer of a segment
    /// containing the given number of characters using the given mode, or None if the
    /// internal calculation of the number of needed bits exceeds usize::MAX. Notes:
    ///
    /// - It is okay for the user to allocate more bytes for the buffer than needed.
    /// - For byte mode, numchars measures the number of bytes, not Unicode code points.
    /// - For ECI mode, numchars must be 0, and the worst-case number of bytes is returned.
    ///   An actual ECI segment can have shorter data. For non-ECI modes, the result is exact.
    pub fn calc_buffer_size(mode: QrSegmentMode, numchars: usize) -> Option<usize> {
        let temp = Self::calc_bit_length(mode, numchars)?;
        Some(temp / 8 + usize::from(temp % 8 != 0)) // ceil(temp / 8)
    }

    // Returns the number of data bits needed to represent a segment
    // containing the given number of characters using the given mode,
    // or None if the the number of needed bits exceeds usize::MAX. Notes:
    // - For byte mode, numchars measures the number of bytes, not Unicode code points.
    // - For ECI mode, numchars must be 0, and the worst-case number of bits is returned.
    //   An actual ECI segment can have shorter data. For non-ECI modes, the result is exact.
    fn calc_bit_length(mode: QrSegmentMode, numchars: usize) -> Option<usize> {
        // Returns ceil((numer / denom) * numchars)
        let mul_frac_ceil = |numer: usize, denom: usize|
            Some(numchars)
                .and_then(|x| x.checked_mul(numer))
                .and_then(|x| x.checked_add(denom - 1))
                .map(|x| x / denom);

        use QrSegmentMode::*;
        match mode {
            Numeric => mul_frac_ceil(10, 3),
            Alphanumeric => mul_frac_ceil(11, 2),
            Byte => mul_frac_ceil(8, 1),
            Kanji => mul_frac_ceil(13, 1),
            Eci => {
                assert_eq!(numchars, 0);
                Some(3 * 8)
            }
        }
    }

    // Calculates and returns the number of bits needed to encode the given
    // segments at the given version. The result is None if a segment has too many
    // characters to fit its length field, or the total bits exceeds usize::MAX.
    fn get_total_bits(segs: &[Self], version: Version) -> Option<usize> {
        let mut result: usize = 0;
        for seg in segs {
            let ccbits: u8 = seg.mode.num_char_count_bits(version);
            // ccbits can be as large as 16, but usize can be as small as 16
            if let Some(limit) = (1usize).checked_shl(ccbits.into()) {
                if seg.numchars >= limit {
                    return None; // The segment's length doesn't fit the field's bit width
                }
            }
            result = result.checked_add(4 + usize::from(ccbits))?;
            result = result.checked_add(seg.bitlength)?;
        }
        Some(result)
    }

    /// Tests whether the given string can be encoded as a segment in numeric mode.
    /// A string is encodable iff each character is in the range 0 to 9.
    pub fn is_numeric(text: &str) -> bool {
        text.chars().all(|c| ('0'..='9').contains(&c))
    }

    /// Tests whether the given string can be encoded as a segment in alphanumeric mode.
    /// A string is encodable iff each character is in the following set: 0 to 9, A to Z
    /// (uppercase only), space, dollar, percent, asterisk, plus, hyphen, period, slash, colon.
    pub fn is_alphanumeric(text: &str) -> bool {
        text.chars().all(|c| ALPHANUMERIC_CHARSET.contains(c))
    }
}

// The set of all legal characters in alphanumeric mode,
// where each character value maps to the index in the string.
static ALPHANUMERIC_CHARSET: &str = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ $%*+-./:";

/*---- QrSegmentMode functionality ----*/

/// Describes the encoding mode of a QR segment.
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub enum QrSegmentMode {
    Numeric,
    Alphanumeric,
    Byte,
    Kanji,
    Eci,
}

impl QrSegmentMode {
    // Returns an unsigned 4-bit integer value (range 0 to 15)
    // representing the mode indicator bits for this mode object.
    fn mode_bits(self) -> u32 {
        use QrSegmentMode::*;
        match self {
            Numeric => 0x1,
            Alphanumeric => 0x2,
            Byte => 0x4,
            Kanji => 0x8,
            Eci => 0x7,
        }
    }

    // Returns the bit width of the character count field for a segment in this mode
    // in a QR Code at the given version number. The result is in the range [0, 16].
    fn num_char_count_bits(self, ver: Version) -> u8 {
        use QrSegmentMode::*;
        (
            match self {
                Numeric => [10, 12, 14],
                Alphanumeric => [9, 11, 13],
                Byte => [8, 16, 16],
                Kanji => [8, 10, 12],
                Eci => [0, 0, 0],
            }
        )[usize::from((ver.value() + 7) / 17)]
    }
}

/*---- BitBuffer functionality ----*/

/// A buffer for appending bits.
pub struct BitBuffer<'a> {
    data: &'a mut [u8],

    length: usize,
}

impl<'a> BitBuffer<'a> {
    // Creates a bit buffer based on the given byte array.
    pub fn new(buffer: &'a mut [u8]) -> Self {
        Self {
            data: buffer,
            length: 0,
        }
    }

    // Returns the length of this bit buffer, in bits.
    pub fn len(&self) -> usize {
        self.length
    }

    // Appends the given number of low-order bits of the given value to this byte-based
    // bit buffer, increasing the bit length. Requires 0 <= numBits <= 31 and val < 2^numBits.
    pub fn append_bits(&mut self, val: u32, len: u8) {
        assert!(len <= 31 && (val >> len) == 0);
        assert!(usize::from(len) <= usize::MAX - self.length);
        for i in (0..len).rev() {
            let index: usize = self.length >> 3;
            let shift: u8 = 7 - ((self.length as u8) & 7);
            let bit: u8 = ((val >> i) as u8) & 1;
            if shift == 7 {
                self.data[index] = bit << shift;
            } else {
                self.data[index] |= bit << shift;
            }
            self.length += 1;
        }
    }
}

/*---- Miscellaneous values ----*/

/// Error type for when data exceeds QR code capacity.
///
/// Ways to handle this exception include:
///
/// - Decrease the error correction level if it was greater than `QrCodeEcc::Low`.
/// - Increase the maxversion argument if it was less than `Version::MAX`.
/// - Split the text data into better or optimal segments in order to reduce the number of bits required.
/// - Change the text or binary data to be shorter.
/// - Change the text to fit the character set of a particular segment mode (e.g. alphanumeric).
/// - Propagate the error upward to the caller/user.
#[derive(Debug, Clone)]
pub enum DataTooLong {
    /// A segment is too long for the chosen mode.
    SegmentTooLong,
    /// Data length exceeds capacity.
    DataOverCapacity(usize, usize),
}

impl core::fmt::Display for DataTooLong {
    fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
        match *self {
            Self::SegmentTooLong => write!(f, "Segment too long"),
            Self::DataOverCapacity(datalen, maxcapacity) =>
                write!(f, "Data length = {} bits, Max capacity = {} bits", datalen, maxcapacity),
        }
    }
}

/// A QR code version (1–40).
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug)]
pub struct Version(u8);

impl Version {
    /// The minimum version number supported in the QR Code Model 2 standard.
    pub const MIN: Version = Version(1);

    /// The maximum version number supported in the QR Code Model 2 standard.
    pub const MAX: Version = Version(40);

    /// Creates a version object from the given number.
    ///
    /// Panics if the number is outside the range [1, 40].
    pub const fn new(ver: u8) -> Self {
        assert!(
            Version::MIN.value() <= ver && ver <= Version::MAX.value(),
            "Version number out of range"
        );
        Self(ver)
    }

    /// Returns the value, which is in the range [1, 40].
    pub const fn value(self) -> u8 {
        self.0
    }

    /// Returns the minimum length required for the output and temporary
    /// buffers when creating a QR Code of this version number.
    pub const fn buffer_len(self) -> usize {
        let sidelen = (self.0 as usize) * 4 + 17;
        (sidelen * sidelen + 7) / 8 + 1
    }
}

/// A mask pattern (0–7).
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug)]
pub struct Mask(u8);

impl Mask {
    /// Creates a mask object from the given number.
    ///
    /// Panics if the number is outside the range [0, 7].
    pub const fn new(mask: u8) -> Self {
        assert!(mask <= 7, "Mask value out of range");
        Self(mask)
    }

    /// Returns the value, which is in the range [0, 7].
    pub const fn value(self) -> u8 {
        self.0
    }
}

// Returns true iff the i'th bit of x is set to 1.
fn get_bit(x: u32, i: u8) -> bool {
    ((x >> i) & 1) != 0
}

// Tests whether the i'th bit of x is set to 1.
#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_is_numeric() {
        assert_eq!(QrSegment::is_numeric("1234567890"), true);
        assert_eq!(QrSegment::is_numeric("1234abc"), false);
    }

    #[test]
    fn test_is_alphanumeric() {
        assert_eq!(QrSegment::is_alphanumeric("HELLO WORLD"), true);
        assert_eq!(QrSegment::is_alphanumeric("Hello World"), false);
    }
}
