# qrcode.py Provides the QRMap widget to display the output of uQR library.

# Released under the MIT License (MIT). See LICENSE.
# Copyright (c) 2022 Peter Hinch
from framebuf import FrameBuffer, MONO_HLSB
from gui.core.ugui import Widget
from gui.core.colors import *
from gui.core.ugui import ssd
from uQR import QRCode

from utime import ticks_diff, ticks_ms

class QRMap(Widget):
    @staticmethod
    def len_side(version):
        return 4 * version + 17

    @staticmethod
    def make_buffer(version, scale):
        side = QRMap.len_side(version) * scale
        width = (side >> 3) + int(side & 7 > 0)  # Width in bytes
        return bytearray(side * width)
        
    def __init__(self, writer, row, col, version=4, scale=1, *, bdcolor=RED, buf=None):
        self._version = version
        self._scale = scale
        self._iside = self.len_side(version)  # Dimension of unscaled QR image less border
        side = self._iside * scale
        # Widget allows 4 * scale border around each edge
        border = 4 * scale
        wside = side + 2 * border  # Widget dimension
        super().__init__(writer, row, col, wside, wside, BLACK, WHITE, bdcolor, False)
        super()._set_callbacks(self._update, ())
        if buf is None:
            buf = QRMap.make_buffer(version, scale)
        self._fb = FrameBuffer(buf, side, side, MONO_HLSB)
        self._irow = row + border
        self._icol = col + border
        self._qr = QRCode(version, border=0)

    def show(self):
        if super().show(False):  # Show white border
            palette = ssd.palette
            palette.bg(self.bgcolor)
            palette.fg(self.fgcolor)
            ssd.blit(self._fb, self._icol, self._irow, -1, palette)

    def _update(self, _):  # Runs when value changes
        t = ticks_ms()
        qr = self._qr
        qr.clear()
        qr.add_data(self._value)
        matrix = qr.get_matrix()  # 750ms. Rest of the routine adds 50ms
        if qr.version != self._version:
            raise ValueError("Text too long for QR version.")
        wd = self._iside
        s = self._scale
        for row in range(wd):
            for col in range(wd):
                v = matrix[row][col]
                for nc in range(s):
                    for nr in range(s):
                        self._fb.pixel(col * s + nc, row * s + nr, v)
