<?php
declare(strict_types=1);

/**
 * Fast + stable Code128 barcode generator (PNG) with file caching.
 * Compatible params:
 *  - text=INV001
 *  - codetype=code128 (only)
 *  - size=24 (height)
 *  - orientation=horizontal (only)
 *  - print=true|false (optional: show text)
 *  - sizefactor=1..10 (bar thickness scale)
 */

$text        = isset($_GET['text']) ? trim((string)$_GET['text']) : '';
$code_type   = strtolower($_GET['codetype'] ?? 'code128');
$orientation = strtolower($_GET['orientation'] ?? 'horizontal');
$size        = (int)($_GET['size'] ?? 60);          // barcode height
$printText   = (isset($_GET['print']) && $_GET['print'] === 'true');
$scale       = (int)($_GET['sizefactor'] ?? 2);     // bar thickness scale

// ---- Validate ----
if ($text === '') {
    http_response_code(400);
    header('Content-Type: text/plain; charset=utf-8');
    echo "Missing text";
    exit;
}
if (strlen($text) > 100) { // abuse protection
    http_response_code(413);
    header('Content-Type: text/plain; charset=utf-8');
    echo "Text too long";
    exit;
}
if ($code_type !== 'code128' && $code_type !== 'code128b') {
    http_response_code(400);
    header('Content-Type: text/plain; charset=utf-8');
    echo "Only codetype=code128 supported";
    exit;
}
if ($orientation !== 'horizontal') {
    http_response_code(400);
    header('Content-Type: text/plain; charset=utf-8');
    echo "Only orientation=horizontal supported";
    exit;
}

$size  = max(20, min($size, 200));
$scale = max(1, min($scale, 10));

// ---- Cache (file) ----
$cacheDir = __DIR__ . '/barcode-cache';
if (!is_dir($cacheDir)) {
    @mkdir($cacheDir, 0755, true);
}
$cacheKey  = hash('sha256', $code_type.'|'.$text.'|'.$size.'|'.$scale.'|'.($printText ? '1' : '0'));
$cacheFile = $cacheDir . '/' . $cacheKey . '.png';

// 30 days
$maxAge = 60 * 60 * 24 * 30;

// Serve from disk cache if available
if (is_file($cacheFile) && (time() - filemtime($cacheFile)) < $maxAge) {
    header('Content-Type: image/png');
    header('Cache-Control: public, max-age='.$maxAge.', immutable');
    header('Content-Length: ' . filesize($cacheFile));
    readfile($cacheFile);
    exit;
}

// ---- Code128 patterns (same logic as your old code, but optimized for speed) ----
// NOTE: Keep this mapping stable; checksum depends on index.
$patterns = [
" "=>"212222","!"=>"222122","\""=>"222221","#"=>"121223","$"=>"121322","%"=>"131222","&"=>"122213","'"=>"122312",
"("=>"132212",")"=>"221213","*"=>"221312","+"=>"231212",","=>"112232","-"=>"122132","."=>"122231","/"=>"113222",
"0"=>"123122","1"=>"123221","2"=>"223211","3"=>"221132","4"=>"221231","5"=>"213212","6"=>"223112","7"=>"312131",
"8"=>"311222","9"=>"321122",":"=>"321221",";"=>"312212","<"=>"322112","="=>"322211",">"=>"212123","?"=>"212321",
"@"=>"232121","A"=>"111323","B"=>"131123","C"=>"131321","D"=>"112313","E"=>"132113","F"=>"132311","G"=>"211313",
"H"=>"231113","I"=>"231311","J"=>"112133","K"=>"112331","L"=>"132131","M"=>"113123","N"=>"113321","O"=>"133121",
"P"=>"313121","Q"=>"211331","R"=>"231131","S"=>"213113","T"=>"213311","U"=>"213131","V"=>"311123","W"=>"311321",
"X"=>"331121","Y"=>"312113","Z"=>"312311","["=>"332111","\\"=>"314111","]"=>"221411","^"=>"431111","_"=>"111224",
"`"=>"111422","a"=>"121124","b"=>"121421","c"=>"141122","d"=>"141221","e"=>"112214","f"=>"112412","g"=>"122114",
"h"=>"122411","i"=>"142112","j"=>"142211","k"=>"241211","l"=>"221114","m"=>"413111","n"=>"241112","o"=>"134111",
"p"=>"111242","q"=>"121142","r"=>"121241","s"=>"114212","t"=>"124112","u"=>"124211","v"=>"411212","w"=>"421112",
"x"=>"421211","y"=>"212141","z"=>"214121","{"=>"412121","|"=>"111143","}"=>"111341","~"=>"131141","DEL"=>"114113",
"FNC 3"=>"114311","FNC 2"=>"411113","SHIFT"=>"411311","CODE C"=>"113141","FNC 4"=>"114131","CODE A"=>"311141",
"FNC 1"=>"411131","Start A"=>"211412","Start B"=>"211214","Start C"=>"211232","Stop"=>"2331112"
];

$keys = array_keys($patterns);
$vals = array_flip($keys);

// Build encoded string with checksum
$checksum = 104; // Start B
$encoded  = '';

// Validate characters + build
$len = strlen($text);
for ($i = 0; $i < $len; $i++) {
    $ch = $text[$i];
    if (!isset($patterns[$ch])) {
        // Unsupported char in mapping
        http_response_code(400);
        header('Content-Type: text/plain; charset=utf-8');
        echo "Unsupported character: " . $ch;
        exit;
    }
    $encoded .= $patterns[$ch];
    $checksum += ($vals[$ch] * ($i + 1));
}

// checksum mod 103
$checkIndex = $checksum % 103;
$encoded .= $patterns[$keys[$checkIndex]];

// StartB + encoded + Stop
$encoded = $patterns["Start B"] . $encoded . $patterns["Stop"];

// ---- Compute width ----
$totalModules = 20; // quiet zone approximation
$encLen = strlen($encoded);
for ($i = 0; $i < $encLen; $i++) {
    $totalModules += (int)$encoded[$i];
}

// Margins for print stability
$quiet = 10;  // left/right padding modules
$topPad = 6;
$bottomPad = $printText ? 20 : 6;

$imgW = ($totalModules + ($quiet * 2)) * $scale;
$imgH = $size + $topPad + $bottomPad;

// ---- Draw image (truecolor) ----
$image = imagecreatetruecolor($imgW, $imgH);
imagealphablending($image, true);
imagesavealpha($image, true);

$white = imagecolorallocate($image, 255, 255, 255);
$black = imagecolorallocate($image, 0, 0, 0);
imagefilledrectangle($image, 0, 0, $imgW, $imgH, $white);

// Draw bars
$x = $quiet * $scale;
$y1 = $topPad;
$y2 = $topPad + $size;

for ($pos = 0; $pos < $encLen; $pos++) {
    $w = (int)$encoded[$pos] * $scale;
    if (($pos % 2) === 0) {
        // bar
        imagefilledrectangle($image, $x, $y1, $x + $w - 1, $y2, $black);
    }
    $x += $w;
}

// Optional text
if ($printText) {
    // Use built-in font (fast). You can replace with imagettftext if you want.
    imagestring($image, 5, 10, $topPad + $size + 2, $text, $black);
}

// Output buffer
ob_start();
imagepng($image);
$png = (string)ob_get_clean();
imagedestroy($image);

// Save cache (best-effort)
if (is_dir($cacheDir) && is_writable($cacheDir)) {
    @file_put_contents($cacheFile, $png, LOCK_EX);
}

// Serve with cache headers
header('Content-Type: image/png');
header('Cache-Control: public, max-age='.$maxAge.', immutable');
header('Content-Length: ' . strlen($png));
echo $png;
