<?php

declare(strict_types=1);

namespace App\Domain\QrCode\Pattern;

/**
 * Implementiert das Version Information Pattern für QR-Codes ab Version 7
 */
class VersionInfoPattern implements PatternInterface
{
    /**
     * Lookup-Tabelle für die Version-Information (BCH-kodiert)
     * Diese 18-Bit-Werte enthalten die Versionsinformation mit Fehlerkorrektur
     */
    private const VERSION_INFO = [
        7 => 0b000111110010010100,
        8 => 0b001000010110111100,
        9 => 0b001001101010011001,
        10 => 0b001010010011010011,
        11 => 0b001011101111110110,
        12 => 0b001100011101100010,
        13 => 0b001101100001000111,
        14 => 0b001110011000001101,
        15 => 0b001111100100101000,
        16 => 0b010000101101111000,
        17 => 0b010001010001011101,
        18 => 0b010010101000010111,
        19 => 0b010011010100110010,
        20 => 0b010100100110100110,
        21 => 0b010101011010000011,
        22 => 0b010110100011001001,
        23 => 0b010111011111101100,
        24 => 0b011000111011000100,
        25 => 0b011001000111100001,
        26 => 0b011010111110101011,
        27 => 0b011011000010001110,
        28 => 0b011100110000011010,
        29 => 0b011101001100111111,
        30 => 0b011110110101110101,
        31 => 0b011111001001010000,
        32 => 0b100000100111010101,
        33 => 0b100001011011110000,
        34 => 0b100010100010111010,
        35 => 0b100011011110011111,
        36 => 0b100100101100001011,
        37 => 0b100101010000101110,
        38 => 0b100110101001100100,
        39 => 0b100111010101000001,
        40 => 0b101000110001101001,
    ];

    private int $version;

    public function __construct(int $version)
    {
        $this->version = $version;
    }

    public function apply(array &$matrix, int $size): void
    {
        // Version Information Pattern nur für QR-Codes ab Version 7
        if ($this->version < 7) {
            return;
        }

        $versionInfo = self::VERSION_INFO[$this->version] ?? 0;

        // Versionsinformation an zwei Positionen einfügen
        $this->placeVersionInfo($matrix, $versionInfo); // Platziere oben rechts
        $this->placeVersionInfoTransposed($matrix, $versionInfo); // Platziere unten links
    }

    /**
     * Platziert die Versionsinformation unterhalb des Finder Patterns oben rechts
     */
    private function placeVersionInfo(array &$matrix, int $versionInfo): void
    {
        // Position: unterhalb des rechten oberen Finder Patterns
        // 3 Zeilen x 6 Spalten = 18 Bits
        $row = 0;
        $col = $this->version * 4 + 6; // Rechts oben, die exakte Position hängt von der Version ab

        // 18 Bits des Versionsinformationsblocks setzen
        for ($i = 0; $i < 18; $i++) {
            // Bit von rechts nach links extrahieren (LSB zuerst)
            $bit = ($versionInfo >> $i) & 1;

            // Berechne Position: 6 Bits pro Zeile, 3 Zeilen
            $r = $row + ($i / 3);
            $c = $col - ($i % 3);

            $matrix[(int)$r][(int)$c] = $bit === 1;
        }
    }

    /**
     * Platziert die Versionsinformation rechts vom Finder Pattern unten links
     */
    private function placeVersionInfoTransposed(array &$matrix, int $versionInfo): void
    {
        // Position: rechts vom linken unteren Finder Pattern
        // 6 Zeilen x 3 Spalten = 18 Bits
        $row = $this->version * 4 + 6; // Unten links, die exakte Position hängt von der Version ab
        $col = 0;

        // 18 Bits des Versionsinformationsblocks setzen
        for ($i = 0; $i < 18; $i++) {
            // Bit von rechts nach links extrahieren (LSB zuerst)
            $bit = ($versionInfo >> $i) & 1;

            // Berechne Position: 3 Bits pro Zeile, 6 Zeilen (transponiert)
            $r = $row - ($i % 3);
            $c = $col + ($i / 3);

            $matrix[(int)$r][(int)$c] = $bit === 1;
        }
    }
}
