;;; qrencode.el --- QRCode encoder  -*- lexical-binding: t -*-

;; Copyright (C) 2021-2026 Free Software Foundation, Inc.

;; Author: Rüdiger Sonderfeld <ruediger@c-plusplus.net>
;; Keywords: qrcode comm
;; Version: 1.5-beta4
;; Package-Requires: ((emacs "25.1"))
;; Package: qrencode
;; URL: https://github.com/ruediger/qrencode-el

;; This file is NOT part of GNU Emacs.

;; This program is free software: you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation, either version 3 of the License, or
;; (at your option) any later version.

;; This program is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
;; GNU General Public License for more details.

;; You should have received a copy of the GNU General Public License
;; along with this program.  If not, see <https://www.gnu.org/licenses/>.

;;; Commentary:

;; qrencode provides a QR Code (ISO/IEC 18004:2015) encoder written
;; entirely in Emacs Lisp (elisp).  See README.org for full
;; documentation.
;;
;; Usage: Call `qrencode-region' to convert a region into a QR Code
;; and `qrencode-url-at-point' to convert a URL.  QR Codes are shown
;; in a separate buffer.

;;; Code:

;; backwards compatibility for Emacs <29.1
(eval-when-compile (require 'subr-x))

(require 'cl-lib)
(eval-when-compile (require 'easymenu))
(require 'seq)
(require 'thingatpt)

;;; Error correction
;; Reed solomon ECC implementation based on https://research.swtch.com/field

(defun qrencode--mul-no-lut (x y poly)
  "Carryless-multiply X and Y modulo POLY."
  (let ((z 0))
    (while (> x 0)
      (when (/= (logand x 1) 0)
        (setq z (logxor z y)))
      (setq x (ash x -1))
      (setq y (ash y +1))
      ;; Modulo operation: If we shift y too far we have to subtract
      ;; poly again.
      (when (/= (logand y #x100) 0)
        (setq y (logxor y poly))))
    z))

(defun qrencode--init-field (poly a)
  "Initialise a field LUT with POLY and A."
  (let ((log (make-vector 256 0))
        (exp (make-vector 510 0))
        (x 1))
    (dotimes (i 255)
      (aset exp i x)
      (aset exp (+ i 255) x)
      (aset log x i)
      (setq x (qrencode--mul-no-lut x a poly)))
    (aset log 0 255)
    (list log exp)))

(defun qrencode--field-exp (field e)
  "Return exponential E in FIELD."
  (if (< e 0)
      0
    (aref (cadr field) (% e 255))))

(defun qrencode--field-log (field l)
  "Return log of L in FIELD."
  (if (= l 0)
      -1
    (aref (car field) l)))

(defun qrencode--field-mul (field x y)
  "Multiply X with Y in FIELD."
  (if (or (= x 0) (= y 0))
      0
    (qrencode--field-exp field
                         (+ (qrencode--field-log field x)
                            (qrencode--field-log field y)))))

(defun qrencode--gen (field e)
  "Return generator polynomial of degree E and its log in FIELD."
  (let ((p (make-vector (1+ e) 0))
        (lp (make-vector (1+ e) 0)))  ; log(p)
    ;; calculate p
    (aset p e 1)
    (dotimes (i e)
      (let ((c (qrencode--field-exp field i)))
        (dotimes (j e)
          (aset p j (logxor
                     (qrencode--field-mul field (aref p j) c)
                     (aref p (1+ j)))))
        (aset p e (qrencode--field-mul field (aref p e) c))))

    ;; calculate log(p)
    (dotimes (i (1+ e))
      (let ((c (aref p i)))
        (if (= c 0)
            (aset lp i 255)
          (aset lp i (qrencode--field-log field c)))))

    (list p (seq-subseq lp 1))))

(defun qrencode--ecc (data c &optional field lgen)
  "Return ECC for DATA with length C.
Optionally provide a FIELD and LGEN (log of generator polynomial)."
  (setq field (or field (qrencode--init-field #x11d 2)))
  (setq lgen (or lgen (cadr (qrencode--gen field c))))
  (let ((p (vconcat data (make-vector c 0))))  ; Data padded with 0 bytes

    (dotimes (i (length data))
      (unless (= (aref p i) 0)
        (let ((exp (qrencode--field-log field (aref p i))))
          (dotimes (j (length lgen))
            (let ((lg (aref lgen j)))
              (when (/= lg 255)
                (aset p (+ i j 1)
                      (logxor (aref p (+ i j 1))
                              (aref (cadr field) (+ exp lg))))))))))
    (seq-subseq p (length data))))

;;; Util

(defun qrencode--size (version)
  "Return number of modules for VERSION."
  (cl-assert (<= 1 version 40) 'show-args "Version %d out of valid range [1, 40]" version)
  (+ (* (1- version) 4) 21))

;;; Data encoding
(defun qrencode--mode (mode)
  "Return encoding representation of MODE."
  (pcase mode
    ('byte 4)  ; 0100
    ;; TODO(#11): Support other encodings
    (other (error "Mode %s not supported" other))))

(defun qrencode--encode-byte (input version &optional mode)
  "Return INPUT encoded in byte format for QR Code size VERSION.
See Section 7.4 of ISO/IEC standard.  This code adds a 4 bit MODE
indicator and then the character count in either 16 bit (for
version > 9) or 8 bit, followed by input."
  (let* ((l (length input))
         (rest (logand l #xF)))
    (cl-assert (<= l (if (> version 9) 65535 255)))
    (vconcat
     (vector (logior (ash (qrencode--mode (or mode 'byte)) 4)
                     ;; Version <=9 use 8 bit, larger 16 bit for size
                     (ash l (if (<= version 9) -4 -12))))
     (when (> version 9)
       (vector (ash (logand l #xFF0) -4)))
     (cl-loop for d across input
              vconcat (vector (logior (ash rest 4) (ash d -4)))
              do (setq rest (logand d #xF)))
     (vector (ash rest 4)))))

;;; Basic patterns and templates handling
(defconst qrencode--finder-pattern
  '[[1 1 1 1 1 1 1]
    [1 0 0 0 0 0 1]
    [1 0 1 1 1 0 1]
    [1 0 1 1 1 0 1]
    [1 0 1 1 1 0 1]
    [1 0 0 0 0 0 1]
    [1 1 1 1 1 1 1]]
  "QRCode Finder Pattern.")

(defconst qrencode--alignment-pattern
  '[[1 1 1 1 1]
    [1 0 0 0 1]
    [1 0 1 0 1]
    [1 0 0 0 1]
    [1 1 1 1 1]]
  "QRCode Alignment Pattern.")

(defconst qrencode--alignment-pattern-placement
  '[nil
    nil
    [6 18]
    [6 22]
    [6 26]
    [6 30]
    [6 34]
    [6 22 38]
    [6 24 42]
    [6 26 46]
    [6 28 50]
    [6 30 54]
    [6 32 58]
    [6 34 62]
    [6 26 46 66]
    [6 26 48 70]
    [6 26 50 74]
    [6 30 54 78]
    [6 30 56 82]
    [6 30 58 86]
    [6 34 62 90]
    [6 28 50 72 94]
    [6 26 50 74 98]
    [6 30 54 78 102]
    [6 28 54 80 106]
    [6 32 58 84 110]
    [6 30 58 86 114]
    [6 34 62 90 118]
    [6 26 50 74 98 122]
    [6 30 54 78 102 126]
    [6 26 52 78 104 130]
    [6 30 56 82 108 134]
    [6 34 60 86 112 138]
    [6 30 58 86 114 142]
    [6 34 62 90 118 146]
    [6 30 54 78 102 126 150]
    [6 24 50 76 102 128 154]
    [6 28 54 80 106 132 158]
    [6 32 58 84 110 136 162]
    [6 26 54 82 110 138 166]
    [6 30 58 86 114 142 170]]
  "Placement of alignment pattern.  Vector index is the version.")

(defun qrencode--square (n &optional init)
  "Return a square of size N.
The square is initialised with INIT or 0."
  (let ((sq (make-vector n '[])))
    (dotimes (i n)
      (aset sq i (make-vector n (or init 0))))
    sq))

(defun qrencode--aaset (dst x y val)
  "Set in sequence of sequences DST position X (inner), Y (outer) to VAL."
  (aset (aref dst y) x val))

(defun qrencode--aaref (src x y)
  "Return from sequence of sequences SRC value at position X (inner), Y (outer)."
  (aref (aref src y) x))

(defun qrencode--copy-square (dst pattern x y)
  "Copy to DST from PATTERN at X and Y."
  (dotimes (i (length pattern))
    (dotimes (j (length (aref pattern i)))
      (qrencode--aaset dst (+ x j) (+ y i) (qrencode--aaref pattern j i)))))

(defun qrencode--set-rect (dst x y width height &optional value)
  "Set a rectangle in DST at X, Y of WIDTH and HEIGHT to VALUE."
  (dotimes (i height)
    (dotimes (j width)
      (qrencode--aaset dst (+ x j) (+ y i) (or value 1)))))

(defun qrencode--template (version)
  "Return basic QRCode template for VERSION."
  (let* ((size (qrencode--size version))
         (qrcode (qrencode--square size))
         (function-pattern (qrencode--square size)))  ; Keeps track of location of function patterns
    ;; Finder pattern
    ;; Top Left
    (qrencode--copy-square qrcode qrencode--finder-pattern 0 0)
    (qrencode--set-rect function-pattern 0 0 9 9) ; finder pattern + separator

    ;; Top Right
    (qrencode--copy-square qrcode qrencode--finder-pattern (- size 7) 0)
    (qrencode--set-rect function-pattern (- size 8) 0 8 8)  ; finder pattern + separator
    (when (>= version 7)
      (qrencode--set-rect function-pattern (- size 11) 0 3 6))  ; version info
    (qrencode--set-rect function-pattern (- size 8) 8 8 1) ; format info

    ;; Bottom Left
    (qrencode--copy-square qrcode qrencode--finder-pattern 0 (- size 7))
    (qrencode--set-rect function-pattern 0 (- size 8) 8 8)  ; finder pattern + separator
    (when (>= version 7)
      (qrencode--set-rect function-pattern 0 (- size 11) 6 3))  ; version info
    (qrencode--set-rect function-pattern 8 (- size 8) 1 8)  ; format info

    ;; Alignment patterns
    (let ((alignment-pattern (aref qrencode--alignment-pattern-placement version)))
      (when alignment-pattern
        ;; Alignment patterns are placed centred at all row/column
        ;; combinations.
        (seq-doseq (r alignment-pattern)
          (seq-doseq (c alignment-pattern)
            (unless (= 1 (qrencode--aaref function-pattern c r))
              (qrencode--copy-square qrcode qrencode--alignment-pattern (- c 2) (- r 2))
              (qrencode--set-rect function-pattern (- c 2) (- r 2) 5 5))))))


    ;; Timing pattern
    (cl-loop for i from 8 to (- size 9)
             do (qrencode--aaset qrcode 6 i (% (1+ i) 2))
             do (qrencode--aaset function-pattern 6 i 1)
             do (qrencode--aaset qrcode i 6 (% (1+ i) 2))
             do (qrencode--aaset function-pattern i 6 1))

    (cons qrcode function-pattern)))

(defun qrencode--nextpos (row column size up)
  "Return next possible ROW, COLUMN and UP value in square of SIZE."
  ;; A QRcode "column" (not to be confused with the column var here,
  ;; which is the x module position) is two modules (pixels).
  ;; Placement is from right to left and then upwards.
  ;;
  ;; Size or the QRCode is odd. So if a column number is even (except
  ;; if we are in column < 6 due to the timing pattern in col 6) the
  ;; go left.  Otherwise go upwards/downwards (depending on up) and
  ;; right.  Except if we hit the top/bottom then move a QCode column
  ;; (two modules) to the right and change direction (up).
  (when (= column 6)  ; Handle timing pattern column 6
    (setq column 5))

  (if (= (% column 2) (if (< column 6) 1 0))
      (if (<= column 0)
          (error "Overflowing pattern.  Data too large?")
        (list row (1- column) up))
    (if up
        (if (= row 0)
            (if (= column 0)
                (error "Overflowing pattern.  Data too large?")
              (list row (1- column) nil))
          (list (1- row) (1+ column) up))
      (if (>= row  (1- size))
          (if (= column 0)
              (error "Overflowing pattern.  Data too large?")
            (list row (1- column) t))
        (list (1+ row) (1+ column) up)))))

(defun qrencode--draw-data (qrcode function-pattern version data)
  "Draw DATA on QRCODE of VERSION and respecting FUNCTION-PATTERN."
  (let* ((size (qrencode--size version))
         (up t)
         (row (1- size))
         (column (1- size)))
    (cl-loop for byte across data and didx from 0
             do (cl-loop for bidx from 7 downto 0
                         do (qrencode--aaset qrcode column row (logand (ash byte (- bidx)) 1))
                         do (unless (and (= didx (1- (length data))) (= bidx 0))
                              ;; Find new position avoiding function-patterns.
                              (cl-loop do (pcase-let ((`(,nrow ,ncolumn ,nup) (qrencode--nextpos row column size up)))
                                            (setq row nrow
                                                  column ncolumn
                                                  up nup))
                                       while (= (qrencode--aaref function-pattern column row) 1)))))))


;;; Data masking

(defun qrencode--penalty-adjacency (qr)
  "Return penalty of QR code for rule 1: Adjacency.
Five or more adjacent modules of same colour."
  ;; 1. Adjacency:
  ;; More than 5 adjacent modules of same colour.
  (let ((size (length qr))
        (penalty 0)
        (N1 3))
    ;; Scan columns
    (let ((row 0) (col 0))
      (while (< row size)
        (while (< col (1- size))
          (if (= (qrencode--aaref qr col row)
                 (qrencode--aaref qr (1+ col) row))
              (let ((i 1))
                (while (and (< col (1- size)) (= (qrencode--aaref qr col row)
                                                 (qrencode--aaref qr (1+ col) row)))
                  (setq i (1+ i)
                        col (1+ col)))
                (when (>= i 5)
                  (setq penalty (+ penalty N1 (- i 5)))))
            (setq col (1+ col))))
        (setq row (1+ row)
              col 0)))
    ;; Scan rows
    (let ((row 0) (col 0))
      (while (< col size)
        (while (< row (1- size))
          (if (= (qrencode--aaref qr col row)
                 (qrencode--aaref qr col (1+ row)))
              (let ((i 1))
                (while (and (< row (1- size)) (= (qrencode--aaref qr col row)
                                                 (qrencode--aaref qr col (1+ row))))
                  (setq i (1+ i)
                        row (1+ row)))
                (when (>= i 5)
                  (setq penalty (+ penalty N1 (- i 5)))))
            (setq row (1+ row))))
        (setq col (1+ col)
              row 0)))
    penalty))

(defun qrencode--penalty-blocks (qr)
  "Return penalty for QR code for rule 2.
Penalty for block (2×2 block) of modules of the same colour."
  (let ((size (length qr))
        (N2 3))
    (cl-loop for row from 0 below (1- size)
             sum (cl-loop for col from 0 below (1- size)
                          when (= (qrencode--aaref qr col row)
                                  (qrencode--aaref qr col (1+ row))
                                  (qrencode--aaref qr (1+ col) row)
                                  (qrencode--aaref qr (1+ col) (1+ row)))
                          sum N2))))

(defun qrencode--penalty-11311 (qr)
  "Return penalty of QR code for rule 3: 1:1:3:1:1 pattern.
Pattern: 4 light modules before/after 1011101. I.e., 00001011101 or 10111010000."
  (let ((size (length qr))
        (N3 40)
        (penalty 0))
    (let ((row 0) (col 0))
      (while (< row size)
        (while (< col (- size 10))
          ;; Optimisation: Both patterns match in these spots
          (when (and (= 0 (qrencode--aaref qr (+ col 1) row))
                     (= 1 (qrencode--aaref qr (+ col 4) row))
                     (= 0 (qrencode--aaref qr (+ col 5) row))
                     (= 1 (qrencode--aaref qr (+ col 6) row))
                     (= 0 (qrencode--aaref qr (+ col 9) row))
                     (or (and
                          ;; Pattern beginning with 0
                          (= 0 (qrencode--aaref qr col row))
                          ;; 0
                          (= 0 (qrencode--aaref qr (+ col 2) row))
                          (= 0 (qrencode--aaref qr (+ col 3) row))
                          ;; 1
                          (= 1 (qrencode--aaref qr (+ col 7) row))
                          (= 1 (qrencode--aaref qr (+ col 8) row))
                          ;; 0
                          (= 1 (qrencode--aaref qr (+ col 10) row)))
                         (and
                          ;; Pattern ending with 0
                          (= 1 (qrencode--aaref qr col row))
                          ;; 0
                          (= 1 (qrencode--aaref qr (+ col 2) row))
                          (= 1 (qrencode--aaref qr (+ col 3) row))
                          ;; 1
                          (= 0 (qrencode--aaref qr (+ col 7) row))
                          (= 0 (qrencode--aaref qr (+ col 8) row))
                          ;; 0
                          (= 0(qrencode--aaref qr (+ col 10) row)))))
            (setq penalty (+ penalty N3)))
          (setq col (1+ col)))
        (setq row (1+ row)
              col 0)))

    (let ((row 0) (col 0))
      (while (< col size)
        (while (< row (- size 10))
          ;; Optimisation: Both patterns match in these spots
          (when (and (= 0 (qrencode--aaref qr col (+ row 1)))
                     (= 1 (qrencode--aaref qr col (+ row 4)))
                     (= 0 (qrencode--aaref qr col (+ row 5)))
                     (= 1 (qrencode--aaref qr col (+ row 6)))
                     (= 0 (qrencode--aaref qr col (+ row 9)))
                     (or (and
                          ;; Pattern beginning with 0
                          (= 0 (qrencode--aaref qr col row))
                          ;; 0
                          (= 0 (qrencode--aaref qr col (+ row 2)))
                          (= 0 (qrencode--aaref qr col (+ row 3)))
                          ;; 1
                          (= 1 (qrencode--aaref qr col (+ row 7)))
                          (= 1 (qrencode--aaref qr col (+ row 8)))
                          ;; 0
                          (= 1 (qrencode--aaref qr col (+ row 10))))
                         (and
                          ;; Pattern ending with 0
                          (= 1 (qrencode--aaref qr col row))
                          ;; 0
                          (= 1 (qrencode--aaref qr col (+ row 2)))
                          (= 1 (qrencode--aaref qr col (+ row 3)))
                          ;; 1
                          (= 0 (qrencode--aaref qr col (+ row 7)))
                          (= 0 (qrencode--aaref qr col (+ row 8)))
                          ;; 0
                          (= 0(qrencode--aaref qr col (+ row 10))))))
            (setq penalty (+ penalty N3)))
          (setq row (1+ row)))
        (setq col (1+ col)
              row 0)))
    penalty))

(defun qrencode--penalty-dark-light-ratio (qr)
  "Return penalty of QR code for rule 4: Ratio of dark to light."
  (let ((N4 10)
        (size (length qr))
        (dark (cl-loop for row across qr
                       sum (cl-loop for d across row sum d))))
    ;; Every 5% deviation from 50% dark/light ratio is penalised.
    ;; (* (floor (/ (abs (- (/ (float dark) (* size size)) 0.5)) 0.05)) N4)
    ;; Instead of using float we use integer arithmetic though to avoid
    ;; rounding cases.
    (* N4 (/ (abs (- (* 20 dark) (* 10 size size))) (* size size)))))

(defun qrencode--penalty (qr)
  "Return penalty (the higher the worse) for a given QR pattern."
  (+
   (qrencode--penalty-adjacency qr)          ;; Rule 1
   (qrencode--penalty-blocks qr)             ;; Rule 2
   (qrencode--penalty-11311 qr)              ;; Rule 3
   (qrencode--penalty-dark-light-ratio qr))) ;; Rule 4

(defconst qrencode--masks
  (vector (lambda (i j) (= (% (+ i j) 2) 0))
          (lambda (i _j) (= (% i 2) 0))
          (lambda (_i j) (= (% j 3) 0))
          (lambda (i j) (= (% (+ i j) 3) 0))
          (lambda (i j) (= (% (+ (/ i 2) (/ j 3)) 2) 0))
          (lambda (i j) (= (+ (% (* i j) 2) (% (* i j) 3)) 0))
          (lambda (i j) (= (% (+ (% (* i j) 2) (% (* i j) 3)) 2) 0))
          (lambda (i j) (= (% (+ (% (+ i j) 2) (% (* i j) 3)) 2) 0)))
  "Mask patterns.")

(defun qrencode--copy (seq)
  "Copy a sequence of sequence SEQ."
  (let ((sq (make-vector (length seq) '[])))
    (dotimes (i (length seq))
      (aset sq i (seq-copy (aref seq i))))
    sq))

(defun qrencode--apply-mask (qrcode function-pattern datamask)
  "Return a copy of QRCODE with DATAMASK applied except for FUNCTION-PATTERN."
  (let ((qr (qrencode--copy qrcode))
        (size (length qrcode))
        (m (aref qrencode--masks datamask)))
    (cl-loop for i below size
             do (cl-loop for j below size
                         unless (= (qrencode--aaref function-pattern j i) 1)
                         do (qrencode--aaset qr j i
                                             (logxor (qrencode--aaref qr j i)
                                                     (if (funcall m i j) 1 0)))))
    qr))

(defun qrencode--find-best-mask (qr function-pattern errcorr)
  "Return QR with best mask applied and mask number, avoiding FUNCTION-PATTERN.
This encodes ERRCORR and mask information."
  (let (bestqr (bestmask 0) (bestpenalty #xFFFFFFFF))
    (dotimes (mask (length qrencode--masks))
      (let ((newqr (qrencode--apply-mask qr function-pattern mask)))
        (qrencode--encode-info newqr errcorr mask)
        (let ((penalty (qrencode--penalty newqr)))
          (when (< penalty bestpenalty)
            (setq bestqr newqr
                  bestmask mask
                  bestpenalty penalty)))))
    (cons bestqr bestmask)))

;;; Version/Info encoding
(defun qrencode--bit-length (x)
  "Return the number of bits necessary to represent integer X in binary."
  (let ((r 0))
    (while (> x 0)
      (setq x (ash x -1)
            r (1+ r)))
    r))

(defun qrencode--mod (x y)
  "Return remainder of carry-less long division of X over Y."
  (let ((xbl (qrencode--bit-length x))
        (ybl (qrencode--bit-length y)))
    (unless (< xbl ybl)
      (cl-loop for i from (- xbl ybl) downto 0
               when (/= (logand x (ash 1 (+ i ybl -1)))  0)
               do (setq x (logxor x (ash y i)))))
    x))

(defun qrencode--bch-encode (data &optional mask)
  "Return DATA properly error correction encoded for info data.
Optionally provide a MASK or #x5412 is used."
  (logxor (ash data 10) (qrencode--mod (ash data 10)
                                       #x537)  ; 10100110111
          (or mask #x5412)))  ; 101010000010010

(defun qrencode--errcorr (errcorr)
  "Return info representation of error correction level ERRCORR."
  (pcase errcorr
    ('L 1)  ; 01
    ('M 0)  ; 00
    ('Q 3)  ; 11
    ('H 2)  ; 10
    (other (error "Unknown error correction level %s" other))))

(defun qrencode--encode-info (qr errcorr datamask)
  "Set info data on QR encoding ERRCORR and DATAMASK."
  (let* ((size (length qr))
         (info (qrencode--bch-encode (logior (ash (qrencode--errcorr errcorr) 3) datamask)))
         (r1 0) (c1 8)
         (r2 8) (c2 (1- size)))
    (dotimes (i 8)
      (let ((data (logand (ash info (- i)) 1)))
        (qrencode--aaset qr c1 r1 data)
        (setq r1 (1+ r1))
        (when (= r1 6)                  ; Avoid timing pattern
          (setq r1 (1+ r1)))
        (qrencode--aaset qr c2 r2 data)
        (setq c2 (1- c2))))

    (setq c1 (1- c1)
          r1 (1- r1)
          r2 (- size 7)
          c2 8)
    (qrencode--aaset qr c2 (1- r2) 1)   ; Dark module in corner
    (cl-loop for i from 8 to 14
             do (let ((data (logand (ash info (- i)) 1)))
                  (qrencode--aaset qr c1 r1 data)
                  (setq c1 (1- c1))
                  (when (= c1 6)        ; Avoid timing pattern
                    (setq c1 (1- c1)))
                  (qrencode--aaset qr c2 r2 data)
                  (setq r2 (1+ r2))))))

(defun qrencode--version-ecc (v)
  "Return version V info error correction code."
  (logior (ash v 12)
          (qrencode--mod (ash v 12)
                         #x1F25))) ; 1111100100101

(defun qrencode--encode-version (qr version)
  "Set on QR the VERSION data."
  (unless (< version 7)      ; only version >= 7 have version encoding
    (let ((e (qrencode--version-ecc version))
          (size (length qr)))
      (let ((c1 0) (r1 (- size 11))  ; Bottom left version location
            (c2 (- size 11)) (r2 0))  ; Top right version location
        (cl-loop for i from 0 to 17
                 ;; Calculate row/column.  A and B usage as row/column
                 ;; switch depend on bottom/top location.
                 do (let ((a (/ i 3)) (b (% i 3))
                          (val (logand (ash e (- i)) 1)))
                      (qrencode--aaset qr (+ c1 a) (+ r1 b) val)
                      (qrencode--aaset qr (+ c2 b) (+ r2 a) val)))))))

;; Analyse data: sizing etc.
(defun qrencode--char-count-bits (version mode)
  "Return the number of bits per character given VERSION and MODE."
  (or
   (cdr (assq mode
              (cond
               ((<= 1 version 9)
                '((numeric      . 10)
                  (alphanumeric .  9)
                  (byte         .  8)
                  (kanji        .  8)))
               ((<= 10 version 26)
                '((numeric      . 12)
                  (alphanumeric . 11)
                  (byte         . 16)
                  (kanji        . 10)))
               ((<= 27 version 40)
                '((numeric      . 14)
                  (alphanumeric . 13)
                  (byte         . 16)
                  (kanji        . 12)))
               (t (error "Unsupported version %d (range 1 to 40)" version)))))
   (error "Unknown mode %s" mode)))

(defun qrencode--length-in-version (n version mode)
  "Return length of a string of size N in VERSION and MODE."
  (+ n (ceiling (+ 4 (qrencode--char-count-bits version mode)) 8)))

(defconst qrencode--size-table
  [(26 . ((L . ( 7 3 1))
          (M . (10 2 1))
          (Q . (13 1 1))
          (H . (17 1 1))))
   (44 . ((L . (10 2 1))
          (M . (16 0 1))
          (Q . (22 0 1))
          (H . (28 0 1))))
   (70 . ((L . (15 1 1))
          (M . (26 0 1))
          (Q . (36 0 2))
          (H . (44 0 2))))
   (100 . ((L . (20 0 1))
           (M . (36 0 2))
           (Q . (52 0 2))
           (H . (64 0 4))))
   (134 . ((L . (26 0 1))  ; 5
           (M . (48 0 2))
           (Q . (72 0 ((2 . 15) . (2 . 16))))
           (H . (88 0 ((2 . 11) . (2 . 12))))))
   (172 . ((L . (36 0 2))
           (M . (64 0 4))
           (Q . (96 0 4))
           (H . (112 0 4))))
   (196 . ((L . (40 0 2))
           (M . (72 0 4))
           (Q . (108 0 ((2 . 14) . (4 . 15))))
           (H . (130 0 ((4 . 13) . (1 . 14))))))
   (242 . ((L . (48 0 2))
           (M . (88 0 ((2 . 38) . (2 . 39))))
           (Q . (132 0 ((4 . 18) . (2 . 19))))
           (H . (156 0 ((4 . 14) . (2 . 15))))))
   (292 . ((L . (60 0 2))
           (M . (110 0 ((3 . 36) . (2 . 37))))
           (Q . (160 0 ((4 . 16) . (4 . 17))))
           (H . (192 0 ((4 . 12) . (4 . 13))))))
   (346 . ((L . (72 0 ((2 . 68) . (2 . 69))))
           (M . (130 0 ((4 . 43) . (1 . 44))))
           (Q . (192 0 ((6 . 19) . (2 . 20))))
           (H . (224 0 ((6 . 15) . (2 . 16))))))
   (404 . ((L . (80 0 4))
           (M . (150 0 ((1 . 50) . (4 . 51))))
           (Q . (224 0 ((4 . 22) . (4 . 23))))
           (H . (264 0 ((3 . 12) . (8 . 13))))))
   (466 . ((L . (96 0 ((2 . 92) . (2 . 93))))
           (M . (176 0 ((6 . 36) . (2 . 37))))
           (Q . (260 0 ((4 . 20) . (6 . 21))))
           (H . (308 0 ((7 . 14) . (4 . 15))))))
   (532 . ((L . (104 0 4))
           (M . (198 0 ((8 . 37) . (1 . 38))))
           (Q . (288 0 ((8 . 20) . (4 . 21))))
           (H . (352 0 ((12 . 11) . (4 . 12))))))
   (581 . ((L . (120 0 ((3 . 115) . (1 . 116))))
           (M . (216 0 ((4 . 40) . (5 . 41))))
           (Q . (320 0 ((11 . 16) . (5 . 17))))
           (H . (384 0 ((11 . 12) . (5 . 13))))))
   (655 . ((L . (132 0 ((5 . 87) . (1 . 88))))
           (M . (240 0 ((5 . 41) . (5 . 42))))
           (Q . (360 0 ((5 . 24) . (7 . 25))))
           (H . (432 0 ((11 . 12) . (7 . 13))))))
   (733 . ((L . (144 0 ((5 . 98) . (1 . 99))))
           (M . (280 0 ((7 . 45) . (3 . 46))))
           (Q . (408 0 ((15 . 19) . (2 . 20))))
           (H . (480 0 ((3 . 15) . (13 . 16))))))
   (815 . ((L . (168 0 ((1 . 107) . (5 . 108))))
           (M . (308 0 ((10 . 46) . (1 . 47))))
           (Q . (448 0 ((1 . 22) . (15 . 23))))
           (H . (532 0 ((2 . 14) . (17 . 15))))))
   (901 . ((L . (180 0 ((5 . 120) . (1 . 121))))
           (M . (338 0 ((9 . 43) . (4 . 44))))
           (Q . (504 0 ((17 . 22) . (1 . 23))))
           (H . (588 0 ((2 . 14) . (19 . 15))))))
   (991 . ((L . (196 0 ((3 . 113) . (4 . 114))))
           (M . (364 0 ((3 . 44) . (11 . 45))))
           (Q . (546 0 ((17 . 21) . (4 . 22))))
           (H . (650 0 ((9 . 13) . (16 . 14))))))
   (1085 . ((L . (224 0 ((3 . 107) . (5 . 108))))
            (M . (416 0 ((3 . 41) . (13 . 42))))
            (Q . (600 0 ((15 . 24) . (5 . 25))))
            (H . (700 0 ((15 . 15) . (10 . 16))))))
   (1156 . ((L . (224 0 ((4 . 116) . (4 . 117))))
            (M . (442 0 17))
            (Q . (644 0 ((17 . 22) . (6 . 23))))
            (H . (750 0 ((19 . 16) . (6 . 17))))))
   (1258 . ((L . (252 0 ((2 . 111) . (7 . 112))))
            (M . (476 0 17))
            (Q . (690 0 ((7 . 24) . (16 . 25))))
            (H . (816 0 34))))
   (1364 . ((L . (270 0 ((4 . 121) . (5 . 122))))
            (M . (504 0 ((4 . 47) . (14 . 48))))
            (Q . (750 0 ((11 . 24) . (14 . 25))))
            (H . (900 0 ((16 . 15) . (14 . 16))))))
   (1474 . ((L . (300 0 ((6 . 117) . (4 . 118))))
            (M . (560 0 ((6 . 45) . (14 . 46))))
            (Q . (810 0 ((11 . 24) . (16 . 25))))
            (H . (960 0 ((30 . 16) . (2 . 17))))))
   (1588 . ((L . (312 0 ((8 . 106) . (4 . 107))))
            (M . (588 0 ((8 . 47) . (13 . 48))))
            (Q . (870 0 ((7 . 24) . (22 . 25))))
            (H . (1050 0 ((22 . 15) . (13 . 16))))))
   (1706 . ((L . (336 0 ((10 . 114) . (2 . 115))))
            (M . (644 0 ((19 . 46) . (4 . 47))))
            (Q . (952 0 ((28 . 22) . (6 . 23))))
            (H . (1110 0 ((33 . 16) . (4 . 17))))))
   (1828 . ((L . (360 0 ((8 . 122) . (4 . 123))))
            (M . (700 0 ((22 . 45) . (3 . 46))))
            (Q . (1020 0 ((8 . 23) . (26 . 24))))
            (H . (1200 0 ((12 . 15) . (28 . 16))))))
   (1921 . ((L . (390 0 ((3 . 117) . (10 . 118))))
            (M . (728 0 ((3 . 45) . (23 . 46))))
            (Q . (1050 0 ((4 . 24) . (31 . 25))))
            (H . (1260 0 ((11 . 15) . (31 . 16))))))
   (2051 . ((L . (420 0 ((7 . 116) . (7 . 117))))
            (M . (784 0 ((21 . 45) . (7 . 46))))
            (Q . (1140 0 ((1 . 23) . (37 . 24))))
            (H . (1350 0 ((19 . 15) . (26 . 16))))))
   (2185 . ((L . (450 0 ((5 . 115) . (10 . 116))))
            (M . (812 0 ((19 . 47) . (10 . 48))))
            (Q . (1200 0 ((15 . 24) . (25 . 25))))
            (H . (1440 0 ((23 . 15) . (25 . 16))))))
   (2323 . ((L . (480 0 ((13 . 115) . (3 . 116))))
            (M . (868 0 ((2 . 46) . (29 . 47))))
            (Q . (1290 0 ((42 . 24) . (1 . 25))))
            (H . (1530 0 ((23 . 15) . (28 . 16))))))
   (2465 . ((L . (510 0 17))
            (M . (924 0 ((10 . 46) . (23 . 47))))
            (Q . (1350 0 ((10 . 24) . (35 . 25))))
            (H . (1620 0 ((19 . 15) . (35 . 16))))))
   (2611 . ((L . (540 0 ((17 . 115) . (1 . 116))))
            (M . (980 0 ((14 . 46) . (21 . 47))))
            (Q . (1440 0 ((29 . 24) . (19 . 25))))
            (H . (1710 0 ((11 . 15) . (46 . 16))))))
   (2761 . ((L . (570 0 ((13 . 115) . (6 . 116))))
            (M . (1036 0 ((14 . 46) . (23 . 47))))
            (Q . (1530 0 ((44 . 24) . (7 . 25))))
            (H . (1800 0 ((59 . 16) . (1 . 17))))))
   (2876 . ((L . (570 0 ((12 . 121) . (7 . 122))))
            (M . (1064 0 ((12 . 47) . (26 . 48))))
            (Q . (1590 0 ((39 . 24) . (14 . 25))))
            (H . (1890 0 ((22 . 15) . (41 . 16))))))
   (3034 . ((L . (600 0 ((6 . 121) . (14 . 122))))
            (M . (1120 0 ((6 . 47) . (34 . 48))))
            (Q . (1680 0 ((46 . 24) . (10 . 25))))
            (H . (1980 0 ((2 . 15) . (64 . 16))))))
   (3196 . ((L . (630 0 ((17 . 122) . (4 . 123))))
            (M . (1204 0 ((29 . 46) . (14 . 47))))
            (Q . (1770 0 ((49 . 24) . (10 . 25))))
            (H . (2100 0 ((24 . 15) . (46 . 16))))))
   (3362 . ((L . (660 0 ((4 . 122) . (18 . 123))))
            (M . (1260 0 ((13 . 46) . (32 . 47))))
            (Q . (1860 0 ((48 . 24) . (14 . 25))))
            (H . (2220 0 ((42 . 15) . (32 . 16))))))
   (3532 . ((L . (720 0 ((20 . 117) . (4 . 118))))
            (M . (1316 0 ((40 . 47) . (7 . 48))))
            (Q . (1950 0 ((43 . 24) . (22 . 25))))
            (H . (2310 0 ((10 . 15) . (67 . 16))))))
   (3706 . ((L . (750 0 ((19 . 118) . (6 . 119))))
            (M . (1372 0 ((18 . 47) . (31 . 48))))
            (Q . (2040 0 ((34 . 24) . (34 . 25))))
            (H . (2430 0 ((20 . 15) . (61 . 16))))))]
  "List of size table.
Index is version number - 1 and content is cons of size to an
assoc list of error correction level to number of error
correction code words, p, and error correction blocks.  See Table
9 in ISO/IEC 18004:2015.")

(defun qrencode--find-version (n mode &optional errcorr)
  "Return cons of version and error correction based on data length N, MODE.
Provide ERRCORR if a specific error correction level is desired,
otherwise this will try to find the highest level in the smallest
version."
  (or (cl-loop named outer-loop
               for entry across qrencode--size-table and version from 1
               do (pcase-let ((`(,num-codewords . ,errlevels) entry)
                              (m (qrencode--length-in-version n version mode)))
                    (if errcorr
                        (when (>= (- num-codewords (cadr (assq errcorr errlevels))) m)
                          (cl-return-from outer-loop (cons version errcorr)))
                      (cl-loop for e in '(H Q M L) ; Go from highest err corr level to lowest
                               do (when (>= (- num-codewords (cadr (assq e errlevels))) m)
                                    (cl-return-from outer-loop (cons version e)))))))
      (user-error "No version found supporting %d in mode %s with error correction level %s" n mode errcorr)))

;;; Structuring
(defun qrencode--get-subseq (blocks n &optional off)
  "Return a list of cons of start and end of all subseqs BLOCKS with N bytes.
Optional offset OFF or 0."
  (setq off (or off 0))
  (cl-loop for i from 0 below blocks
           collect (cons (+ (* i n) off)
                         (+ (* (1+ i) n) off))))

(defun qrencode--get-blocks (version errcorr)
  "Return a list of all blocks (subseqs) for VERSION with ERRCORR level."
  (pcase-let* ((size-table (aref qrencode--size-table (1- version)))
               (num-codewords (car size-table))
               (`(,num-errcorr ,_p ,err-blocks) (cdr (assq errcorr (cdr size-table))))
               (num-words (- num-codewords num-errcorr)))
    (if (consp err-blocks)
        (pcase-let ((`(,first-block . ,second-block) err-blocks))
          (append
           (qrencode--get-subseq (car first-block) (cdr first-block))
           (qrencode--get-subseq (car second-block) (cdr second-block) (* (car first-block) (cdr first-block)))))
      (qrencode--get-subseq err-blocks (/ num-words err-blocks)))))

(defun qrencode--blocks (data version errcorr)
  "Return DATA split in blocks according to VERSION and ERRCORR level."
  (cl-loop for b in (qrencode--get-blocks version errcorr)
           vconcat (vector (seq-subseq data (car b) (cdr b)))))

;;; QRCode encoding
(defun qrencode (s &optional mode errcorr return-raw)
  "Encode string S into a QRCode.
Optionally specify MODE and ERRCORR level.  Only supported MODE
is `byte'.  If RETURN-RAW is set a raw vector version of the
QRCode is returned instead of a formatted string."
  ;; Following Section 7.1 from ISO/IEC 18004 2015

  (let (version raw-bytes data qr function-pattern)
    ;; Step 1: Analyse data
    ;; TODO(#11): find suitable mode. For now we only support byte
    (setq mode (or mode 'byte))
    ;; Signals in case of unsupported mode
    (qrencode--mode mode)
    ;; Convert Emacs internal encoded characters into raw UTF-8 bytes
    (setq raw-bytes (encode-coding-string s 'utf-8))
    ;; Find the version with the highest error correction to fit the data
    (pcase-let ((`(,ver . ,ec) (qrencode--find-version (length raw-bytes) mode errcorr)))
      (setq version ver
            errcorr ec))

    ;; Step 2: Encode data
    (setq data (qrencode--encode-byte raw-bytes version mode))
    ;; Add padding
    (let* ((size-table (aref qrencode--size-table (1- version)))
           (qrlen (car size-table))
           (errcorrlen (cadr (assq errcorr (cdr size-table))))
           (datalen (- qrlen errcorrlen))
           (padding [#xEC #x11]))
      (setq data (vconcat data
                          (cl-loop for i from 0 below (- datalen (length data))
                                   vconcat (vector (aref padding (% i 2))))))

      ;; Step 3: Error correction coding
      (let* ((datablocks (qrencode--blocks data version errcorr))
             (blockerrcorrlen (/ errcorrlen (length datablocks)))
             (field (qrencode--init-field #x11d 2))
             (lgen (cadr (qrencode--gen field blockerrcorrlen)))
             (errblocks (cl-loop for b across datablocks
                                 vconcat (vector (qrencode--ecc b blockerrcorrlen field lgen)))))

        ;; Step 4: Structure final message
        ;; Take from datablocks first until all data is taken and then add the error correction blocks.
        (setq data (vconcat (cl-loop for i from 0 below datalen
                                     vconcat (cl-loop for j from 0 below (length datablocks)
                                                      when (< i (length (aref datablocks j)))
                                                      vconcat (vector (seq-elt (aref datablocks j) i))))
                            (cl-loop for i from 0 below errcorrlen
                                     vconcat (cl-loop for j from 0 below (length errblocks)
                                                      when (< i (length (aref errblocks j)))
                                                      vconcat (vector (seq-elt (aref errblocks j) i))))))))

    ;; Step 5: Module placement
    (pcase-let ((`(,qrcode . ,fp) (qrencode--template version)))
      (qrencode--draw-data qrcode fp version data)
      (setq qr qrcode
            function-pattern fp))

    ;; Step 7: Format and version information.
    ;; The steps are out of order here since the masking gets impacted
    ;; by the format and version information.  We encode version here
    ;; the format information is encoded as part of find-best-mask.
    (qrencode--encode-version qr version)

    ;; Step 6: Data masking
    (pcase-let ((`(,qrcodemasked . ,_mask) (qrencode--find-best-mask qr function-pattern errcorr)))
      (setq qr qrcodemasked))

    (if return-raw
        qr
      (qrencode-format qr))))

(defun qrencode-format (qr)
  "Format QR using utf-8 blocks."
  (let* ((size (length qr))
         (sizeqz (+ size 8)))  ; size with quiet zone
    (concat (make-string sizeqz ? ) "\n"
            (make-string sizeqz ? ) "\n"
            (cl-loop for row from 0 below (1- size) by 2
                     concat "    "
                     concat (cl-loop for col from 0 below size
                                     concat (if (and (/= (qrencode--aaref qr col row) 0)
                                                     (/= (qrencode--aaref qr col (1+ row)) 0))
                                                "█"
                                              (if (and (/= (qrencode--aaref qr col row) 0)
                                                       (= (qrencode--aaref qr col (1+ row)) 0))
                                                  "▀"
                                                (if (and (= (qrencode--aaref qr col row) 0)
                                                         (/= (qrencode--aaref qr col (1+ row)) 0))
                                                    "▄"
                                                  " "))))
                     concat "    \n")
            "    "
            (cl-loop for col from 0 below size
                     concat (if (/= (qrencode--aaref qr col (1- size)) 0) "▀" " "))
            "    \n"
            (make-string sizeqz ? ) "\n"
            (make-string sizeqz ? ) "\n")))

(defun qrencode--repeat-string (s n &optional sep)
  "Return string S repeated N timed with optional separator SEP in between."
  (cl-loop for i from 1 to n
           concat s
           when (/= i n)
           concat (or sep "")))

(defun qrencode-format-as-netpbm (qr &optional pixel-size)
  "Format QR as NetPBM (bitmap) file.
Optionally specify PIXEL-SIZE (default is 3)."
  (declare (obsolete "This function and P1 export will be removed in version 2.0 in favour of faster P4 export." "1.5-beta4"))
  (let* ((size (length qr))
         (factor (or pixel-size 3))
         (nsize (* (+ size 8) factor)))
    (concat (format "P1\n%d %d\n" nsize nsize)
            ;; Quiet zone top
            (cl-loop for i from 0 below (* 4 factor)
                     concat (qrencode--repeat-string "0" nsize " ")
                     concat "\n")
            (cl-loop for row from 0 below size
                     concat (cl-loop for i from 1 to factor
                                     ;; Quiet zone left
                                     concat (qrencode--repeat-string "0 " (* 4 factor))
                                     ;; QR Code
                                     concat (cl-loop for col from 0 below size
                                                     concat (qrencode--repeat-string
                                                             (format "%d " (qrencode--aaref qr col row))
                                                             factor))
                                     ;; Quiet zone right
                                     concat (qrencode--repeat-string "0" (* 4 factor) " ")
                                     concat "\n"))
            ;; Quiet zone bottom
            (cl-loop for i from 0 below (* 4 factor)
                     concat (qrencode--repeat-string "0" nsize " ")
                     concat "\n"))))

(defun qrencode--write-as-netpbm-p4 (filename qr &optional pixel-size)
  "Write QR as NetPBM (bitmap, P4 binary) to FILENAME.
Optionally specify PIXEL-SIZE (default is 3)."
  (let* ((size (length qr))
         (factor (or pixel-size 3))
         (quiet-zone-size (* 4 factor))
         (nsize (* (+ size 8) factor))
         (bsize (/ (+ nsize 7) 8))  ; number of bytes. (+7 to round up)
         (coding-system-for-write 'binary))
   (with-temp-file filename
     (set-buffer-multibyte nil)
     (insert (format "P4\n%d %d\n" nsize nsize))
     ;; Quiet zone top
     (dotimes (_ quiet-zone-size)
       (dotimes (_ bsize) (insert 0)))
     ;; QR Code
     (dotimes (r size)
       (let ((row (make-vector bsize 0)))
         ;; Skip quiet zone left + right
         (cl-loop for px from quiet-zone-size below (- nsize quiet-zone-size)
                  when (/= 0 (qrencode--aaref qr (/ (- px quiet-zone-size) factor) r))
                  do (aset row (/ px 8)
                           (logior (aref row (/ px 8))
                                   (ash 1 (- 7 (% px 8))))))
         (dotimes (_ factor)
           (cl-loop for byte across row do (insert byte)))))
     ;; Quiet zone bottom
     (dotimes (_ quiet-zone-size)
       (dotimes (_ bsize) (insert 0))))))

(defgroup qrencode nil
  "QREncode: Encoder for QR Codes."
  :link '(url-link "https://github.com/ruediger/qrencode-el")
  :prefix "qrencode-"
  :group 'communication)

(defcustom qrencode-buffer-name "*QRCode*"
  "Name to use for QRCode buffer."
  :type 'string
  :group 'qrencode)

(defcustom qrencode-export-pixel-size 3
  "Pixel size used for NetPBM (Bitmap) export."
  :type 'integer
  :group 'qrencode)

(defcustom qrencode-post-export-functions nil
  "Abnormal hook run after QRCode file export.
FILENAME of the exported file is passed as parameter.  For
example this can be used to convert the output to a different
bitmap format."
  :type 'hook
  :package-version '(qrencode . "1.2-beta1")
  :group 'qrencode)

(defcustom qrencode-export-format 'p1
  "Export format.
P1 is the current default.  But P4 export is much faster and produces
smaller (binary) files.  In the next major release the default will
change to P4 and P1 support will be removed."
  :type '(choice
          (const :tag "NetPBM text (P1)" p1)
          (const :tag "NetPBM binary (P4)" p4))
  :package-version '(qrencode . "1.5-beta4")
  :group 'qrencode)

(defface qrencode-face
  '((t :foreground "black" :background "white"))
  "Face used for writing QRCodes."
  :group 'qrencode)

(defvar-local qrencode--raw-qr nil
  "Store raw QRCode content for further processing.")

(defun qrencode-export-buffer-to-file (filename)
  "Export QRCode as netpbm to FILENAME."
  (interactive "FFilename: ")
  (if (null qrencode--raw-qr)
      (error "No raw QRCode data found")
    (let ((qr qrencode--raw-qr))       ; save ref to buffer local var.
      (pcase qrencode-export-format
        ('p4
         (qrencode--write-as-netpbm-p4 filename qr qrencode-export-pixel-size))
        ((or 'p1 'nil)
         (with-temp-file filename
           (with-no-warnings ; Called function is obsolete but P1 is still default.
             (insert (qrencode-format-as-netpbm qr qrencode-export-pixel-size)))))
        (format (user-error "Unsupported `qrencode-export-format': %S" format)))
      (run-hook-with-args 'qrencode-post-export-functions filename)
      (message "Wrote QRCode to file %s" filename))))

(defvar qrencode-mode-map
  (let ((map (make-sparse-keymap)))
    (set-keymap-parent map special-mode-map)
    (define-key map "e" #'qrencode-export-buffer-to-file)
    map)
  "Keymap for `qrencode-mode' map.")

(easy-menu-define qrencode-mode-menu qrencode-mode-map
  "Menu for QREncode Mode."
  '("QR"
    ["Export Image" qrencode-export-buffer-to-file :help "Export QRCode as a NetPBM bitmap image."]))

(define-derived-mode qrencode-mode special-mode "QRCode"
  "Major mode for viewing QR Codes.
Commands:
\\{qrencode-mode-map}"
  :group 'qrencode)

(defun qrencode--encode-to-buffer (s)
  "Encode S as QR Code and insert into `qrencode-buffer-name`."
  (let ((buf (get-buffer-create qrencode-buffer-name)))
    (with-current-buffer buf
      (let ((inhibit-read-only t))
        (erase-buffer)
        (qrencode-mode)
        (setq-local line-spacing nil)  ; ensure no line spacing
        (setq-local qrencode--raw-qr (qrencode s nil nil 'return-raw))
        (insert (propertize (qrencode-format qrencode--raw-qr) 'face 'qrencode-face))
        (insert "\nEncoded Text:\n" s)
        (goto-char (point-min))))
    (pop-to-buffer buf)))


;;;###autoload
(defun qrencode-region (beg end)
  "Encode region between BEG and END into a QR code and show in a buffer."
  (interactive "r")
  (qrencode--encode-to-buffer (buffer-substring beg end)))

;;;###autoload
(defun qrencode-url-at-point ()
  "Encode any URL found at point."
  (interactive)
  (let ((url (thing-at-point-url-at-point)))
    (if (null url)
        (message "No URL found at point")
      (qrencode--encode-to-buffer url))))

;;;###autoload
(defun qrencode-string (str)
  "Encode STR.
When called interactively, read STR with `read-string'."
  (interactive "sString: ")
  (let ((trimmed-str (string-trim str)))
    (when (string-empty-p trimmed-str)
      (user-error "Empty string"))
    (qrencode--encode-to-buffer trimmed-str)))

(provide 'qrencode)

;;; qrencode.el ends here
