<?php

namespace WMS\Services;

/**
 * Servicio de generacion de codigo ZPL para impresoras Zebra.
 *
 * Genera etiquetas ZPL y preview HTML para:
 *   - Producto/Lote (recepcion)
 *   - Ubicacion (Bin)
 *   - Contenedor/LPN
 *   - Envio/Despacho
 *
 * Tambien puede enviar ZPL directo a impresora via TCP socket (puerto 9100).
 */
class ZplService
{
    private array $config;

    public function __construct()
    {
        $this->config = $this->loadConfig();
    }

    // ── Configuracion ───────────────────────────────────────────

    private function loadConfig(): array
    {
        $db = \WMS\Core\Database::getConnection();
        $defaults = [
            'default_printer_ip'   => '192.168.1.100',
            'default_printer_port' => 9100,
            'label_width_mm'       => 100,
            'label_height_mm'      => 74,
            'dpi'                  => 203,
            'darkness'             => 22,  // ~SD (0-30): densidad para impresion por IP
            'speed'                => 3,   // ^PR (ips): velocidad para impresion por IP
        ];

        try {
            $stmt = $db->query("SELECT config_key, config_value FROM system_config WHERE config_key LIKE 'printing.%'");
            $rows = $stmt->fetchAll(\PDO::FETCH_KEY_PAIR);
            foreach ($defaults as $k => $v) {
                $dbKey = 'printing.' . $k;
                if (isset($rows[$dbKey])) {
                    $defaults[$k] = is_numeric($v) ? (int) $rows[$dbKey] : $rows[$dbKey];
                }
            }
        } catch (\Throwable $e) {
            // Si no existe la tabla, usar defaults
        }

        return $defaults;
    }

    public function getConfig(): array
    {
        return $this->config;
    }

    // ── Etiqueta de Producto/Lote (Recepcion) ──────────────────

    /**
     * @param array $data {item_code, item_name, batch_number, supplier_lot, expiry_date, quantity, uom, reception_date}
     * @param int   $copies
     * @return array {zpl, preview_html}
     */
    public function productLabel(array $data, int $copies = 1): array
    {
        $itemCode     = $data['item_code'] ?? '';
        $itemName     = mb_substr($data['item_name'] ?? '', 0, 30);
        $batchNumber  = $data['batch_number'] ?? '';
        $supplierLot  = $data['supplier_lot'] ?? '';
        $expiryDate   = $data['expiry_date'] ?? '';
        $quantity     = $data['quantity'] ?? 0;
        $uom          = $data['uom'] ?? 'UN';
        // La unidad SIEMPRE sale del maestro del item (evita 'UN' hardcodeado en
        // etiquetas del RF/escritorio). Si el item no está en el maestro, se mantiene la recibida.
        if ($itemCode !== '') {
            try {
                $uStmt = \WMS\Core\Database::getConnection()->prepare(
                    "SELECT uom FROM items WHERE item_code = ? OR sap_item_code = ? LIMIT 1"
                );
                $uStmt->execute([$itemCode, $itemCode]);
                $masterUom = $uStmt->fetchColumn();
                if (!empty($masterUom)) { $uom = $masterUom; }
            } catch (\Throwable $e) { /* mantener uom recibida */ }
        }
        $receptionDate = $data['reception_date'] ?? date('Y-m-d');

        // Modo "etiqueta de ingreso" (vista #print_labels): sin lote -> muestra "Bultos: N"
        // en lugar del lote y el pie dice "ETIQUETA DE INGRESO". El resto de etiquetas (agenda/RF)
        // no envia entry_label y mantiene Lote + "DINAVISA Compliant - BPA".
        $isEntry = !empty($data['entry_label']);
        $bultos  = max(1, (int) ($data['bultos'] ?? 1));
        $footer  = $isEntry ? 'ETIQUETA DE INGRESO' : 'DINAVISA Compliant - BPA';
        $showFooter = empty($data['no_dinavisa']); // #agendas puede pedir ocultar el pie DINAVISA

        // Etiqueta 100x74 mm a 203 dpi = 800x592 dots — layout centrado.
        // Mismo offset fisico que la etiqueta de Bin: marco en x100 y ^LH4,40, porque la
        // impresora imprime mas arriba y mas a la izquierda que el ZPL. Interior 632 dots de ancho.
        $bcLen   = strlen($itemCode);
        $bcWidth = $bcLen <= 14 ? 3 : 2;
        $bcDots  = (11 * $bcLen + 35) * $bcWidth;
        $bcX     = 120 + max(0, (int) ((632 - $bcDots) / 2));

        // En modo ingreso se generan N etiquetas SECUENCIALES (Bulto 1/N, 2/N, ...).
        // En modo normal, una sola etiqueta con "Lote: ..." (las copias se aplican abajo).
        $seq    = $isEntry ? range(1, $bultos) : [0];
        $labels = [];
        foreach ($seq as $i) {
            $loteField = $isEntry ? ('Bulto ' . $i . '/' . $bultos) : ('Lote: ' . $batchNumber);
            $one  = "^XA\n";
            $one .= "^PW800^LL592^LH4,40^LT0^CI28\n";
            $one .= "^FO100,50^GB672,495,2^FS\n";
            $one .= "^FO120,68^A0N,50,50^FD{$itemCode}^FS\n";
            $one .= "^FO120,130^A0N,30,30^FB632,1,0,L,0^FD" . substr($itemName,0,38) . "^FS\n";
            $one .= "^FO120,172^GB632,2,2^FS\n";
            $one .= "^FO{$bcX},192^BY{$bcWidth},2,120^BCN,120,N,N,N^FD{$itemCode}^FS\n";
            $one .= "^FO120,330^GB632,2,2^FS\n";
            $one .= "^FO120,350^A0N,28,28^FD{$loteField}^FS\n";
            $one .= "^FO460,350^A0N,28,28^FDVto: {$expiryDate}^FS\n";
            $one .= "^FO120,395^A0N,28,28^FDCant: {$quantity} {$uom}^FS\n";
            $one .= "^FO460,395^A0N,28,28^FDLote Prov: " . substr($supplierLot,0,15) . "^FS\n";
            $one .= "^FO120,440^A0N,26,26^FDRecepcion: {$receptionDate}^FS\n";
            $one .= "^FO120,478^GB632,2,2^FS\n";
            $one .= "^FO120,496^A0N,22,22^FDCOMFAR Paraguay - WMS XPert^FS\n";
            if ($showFooter) {
                $one .= "^FO120,524^A0N,20,20^FD{$footer}^FS\n";
            }
            $one .= "^XZ";
            $labels[] = $one;
        }
        $zpl = implode("\n", $labels);

        // Las "copias" solo aplican al modo normal; en ingreso ya se generaron las N etiquetas.
        if (!$isEntry && $copies > 1) {
            $zpl = trim(str_repeat($zpl . "\n", $copies));
        }

        $preview = $this->productLabelPreview($itemCode, $itemName, $batchNumber, $supplierLot, $expiryDate, $quantity, $uom, $receptionDate);

        return ['zpl' => $zpl, 'preview_html' => $preview];
    }

    private function productLabelPreview(string $itemCode, string $itemName, string $batch, string $supplierLot, string $expiry, $qty, string $uom, string $recDate): string
    {
        $batchBars = $batch ? $this->textToBarcodeSvg($batch) : '';
        return <<<HTML
<div style="width:384px;height:192px;border:2px solid #333;border-radius:4px;padding:8px 12px;font-family:monospace;background:#fff;color:#000;position:relative;overflow:hidden">
  <div style="font-size:18px;font-weight:bold;margin-bottom:2px">{$itemCode}</div>
  <div style="font-size:14px;color:#444;margin-bottom:4px">{$itemName}</div>
  <div style="font-size:12px">Lote: <strong>{$batch}</strong></div>
  <div style="margin:4px 0">{$batchBars}</div>
  <div style="font-size:11px;display:flex;gap:16px;flex-wrap:wrap">
    <span>Lote Prov: {$supplierLot}</span>
    <span>Venc: {$expiry}</span>
  </div>
  <div style="font-size:11px;display:flex;gap:16px">
    <span>Cant: {$qty} {$uom}</span>
    <span>Recepcion: {$recDate}</span>
  </div>
</div>
HTML;
    }

    // ── Etiqueta de Ubicacion (Bin) ─────────────────────────────

    /**
     * @param array $data {bin_code, warehouse_name, zone_name}
     * @return array {zpl, preview_html}
     */
    public function binLabel(array $data): array
    {
        $binCode       = $data['bin_code'] ?? '';
        $warehouseName = $data['warehouse_name'] ?? '';
        $zoneName      = $data['zone_name'] ?? '';

        // Etiqueta 100x74 mm a 203 dpi = 800x592 dots — layout centrado, shift +80 horizontal y +60 vertical
        // para compensar offset fisico de la impresora (imprime mas arriba y mas a la izq que el ZPL).
        $codeLen = strlen($binCode);
        $codeFont = $codeLen <= 10 ? 64 : ($codeLen <= 14 ? 55 : ($codeLen <= 18 ? 46 : ($codeLen <= 22 ? 40 : 34)));
        $bcWidth = $codeLen <= 10 ? 4 : ($codeLen <= 14 ? 3 : 2);
        $bcDots = (11 * $codeLen + 35) * $bcWidth;
        $bcX = 120 + max(0, (int) ((632 - $bcDots) / 2));

        $zpl  = "^XA\n";
        $zpl .= "^PW800^LL592^LH4,40^LT0^CI28\n";
        $zpl .= "^FO100,80^GB672,412,2^FS\n";
        $zpl .= "^FO120,95^A0N,26,26^FB632,1,0,C,0^FDUBICACION^FS\n";
        $zpl .= "^FO120,128^A0N,{$codeFont},{$codeFont}^FB632,1,0,C,0^FD{$binCode}^FS\n";
        $zpl .= "^FO120,200^GB632,2,2^FS\n";
        $zpl .= "^FO{$bcX},215^BY{$bcWidth},2,90^BCN,90,Y,N,N^FD{$binCode}^FS\n";
        $zpl .= "^FO120,340^GB632,2,2^FS\n";
        $zpl .= "^FO120,353^A0N,24,24^FB632,1,0,C,0^FDAlmacen: " . substr($warehouseName,0,28) . "^FS\n";
        $zpl .= "^FO120,383^A0N,24,24^FB632,1,0,C,0^FDZona: " . substr($zoneName,0,30) . "^FS\n";
        $zpl .= "^FO120,430^GB632,2,2^FS\n";
        $zpl .= "^FO120,448^A0N,20,20^FB632,1,0,C,0^FDCOMFAR Paraguay - WMS XPert^FS\n";
        $zpl .= "^XZ";

        $barcode = $this->textToBarcodeSvg($binCode);
        $preview = <<<HTML
<div style="width:384px;height:192px;border:2px solid #333;border-radius:4px;padding:10px 14px;font-family:monospace;background:#fff;color:#000;overflow:hidden">
  <div style="font-size:13px;color:#555">{$warehouseName}</div>
  <div style="font-size:11px;color:#777;margin-bottom:6px">Zona: {$zoneName}</div>
  <div style="font-size:26px;font-weight:bold;margin-bottom:6px">{$binCode}</div>
  <div>{$barcode}</div>
</div>
HTML;

        return ['zpl' => $zpl, 'preview_html' => $preview];
    }

    // ── Etiqueta de Contenedor/LPN ──────────────────────────────

    /**
     * @param array $data {container_code, contents_summary}
     * @return array {zpl, preview_html}
     */
    public function containerLabel(array $data): array
    {
        $code     = $data['container_code'] ?? '';
        $contents = $data["contents_summary"] ?? "";
        $date     = date('Y-m-d H:i');

        // Etiqueta 100x74 mm a 203 dpi = 800x592 dots
        // Contenido viene como multilinea separada por \n: "PRODUCTO - Lote: X - Vence: Y"
        $contentLines = array_slice(array_filter(array_map('trim', explode("\n", $contents))), 0, 3);

        // Mismo offset fisico/geometria que la etiqueta de producto y bin (marco en x100, ^LH4,40).
        $bcLen   = strlen($code);
        $bcWidth = $bcLen <= 14 ? 3 : 2;
        $bcDots  = (11 * $bcLen + 35) * $bcWidth;
        $bcX     = 120 + max(0, (int) ((632 - $bcDots) / 2));

        $zpl  = "^XA\n";
        $zpl .= "^PW800^LL592^LH4,40^LT0^CI28\n";
        $zpl .= "^FO100,50^GB672,495,2^FS\n";
        $zpl .= "^FO120,68^A0N,32,32^FDLPN^FS\n";
        $zpl .= "^FO120,106^A0N,54,54^FD{$code}^FS\n";
        $zpl .= "^FO120,175^GB632,2,2^FS\n";
        $zpl .= "^FO{$bcX},195^BY{$bcWidth},2,115^BCN,115,N,N,N^FD{$code}^FS\n";
        $zpl .= "^FO120,335^GB632,2,2^FS\n";
        $zpl .= "^FO120,352^A0N,24,24^FDContenido:^FS\n";

        $y = 392;
        foreach ($contentLines as $ln) {
            $zpl .= "^FO120,{$y}^A0N,22,22^FD" . substr($ln, 0, 60) . "^FS\n";
            $y += 30;
        }

        $zpl .= "^FO120,478^GB632,2,2^FS\n";
        $zpl .= "^FO120,496^A0N,20,20^FDCOMFAR Paraguay - WMS XPert^FS\n";
        $zpl .= "^XZ";

        $barcode = $this->textToBarcodeSvg($code);
        $preview = <<<HTML
<div style="width:384px;height:192px;border:2px solid #333;border-radius:4px;padding:10px 14px;font-family:monospace;background:#fff;color:#000;overflow:hidden">
  <div style="font-size:15px;font-weight:bold;color:#555">LPN</div>
  <div style="font-size:22px;font-weight:bold;margin-bottom:4px">{$code}</div>
  <div style="margin-bottom:6px">{$barcode}</div>
  <div style="font-size:11px;color:#444">{$contents}</div>
  <div style="font-size:10px;color:#777">Fecha: {$date}</div>
</div>
HTML;

        return ['zpl' => $zpl, 'preview_html' => $preview];
    }

    // ── Etiqueta de Packing (caja/bulto) ───────────────────────

    /**
     * Etiqueta para identificar un bulto cerrado de packing.
     * Layout segun PNG IMPRESION_ETIQUETA_Packing.png:
     *  [CODE128 - LPN o OV]
     *  OV:      <doc_num>
     *  Bulto:   <n> de <total>
     *  Cliente: <name>
     *  Items:   <count> unidades
     *  Fecha:   <DD/MM/YYYY>
     *
     * @param array $data {barcode, ov, bulto_actual, bulto_total, customer_name, items_count, fecha}
     * @return array {zpl, preview_html}
     */
    public function packingLabel(array $data, int $copies = 1): array
    {
        $barcode       = (string)($data['barcode'] ?? $data['ov'] ?? '');
        $ov            = (string)($data['ov'] ?? '');
        $bultoActual   = (int)($data['bulto_actual'] ?? 1);
        $bultoTotal    = (int)($data['bulto_total'] ?? 1);
        $customer      = mb_substr((string)($data['customer_name'] ?? ''), 0, 28);
        $producto      = mb_substr((string)($data['producto'] ?? ''), 0, 32);
        $itemsCount    = $data['items_count'] ?? 0;
        $fecha         = $data['fecha'] ?? date('d/m/Y');

        // Etiqueta 100x74 mm a 203 dpi = 800x592 dots
        // Barcode 100 dots + 5 filas de datos (40 dot pitch) + footer
        // Mismo offset fisico/geometria que producto y bin (marco en x100, ^LH4,40).
        $bcLen   = strlen($barcode);
        $bcWidth = $bcLen <= 14 ? 3 : 2;
        $bcDots  = (11 * $bcLen + 35) * $bcWidth;
        $bcX     = 120 + max(0, (int) ((632 - $bcDots) / 2));

        $zpl  = "^XA\n";
        $zpl .= "^PW800^LL592^LH4,40^LT0^CI28\n";
        $zpl .= "^FO100,50^GB672,495,2^FS\n";
        // Barcode area (centrado)
        $zpl .= "^FO{$bcX},66^BY{$bcWidth},2,95^BCN,95,Y,N,N^FD{$barcode}^FS\n";
        $zpl .= "^FO120,210^GB632,2,2^FS\n";
        // Datos
        $zpl .= "^FO120,225^A0N,30,30^FDOV:^FS\n";
        $zpl .= "^FO320,225^A0N,30,30^FD{$ov}^FS\n";
        $zpl .= "^FO120,270^A0N,30,30^FDBulto:^FS\n";
        $zpl .= "^FO320,270^A0N,30,30^FD{$bultoActual} de {$bultoTotal}^FS\n";
        $zpl .= "^FO120,315^A0N,30,30^FDCliente:^FS\n";
        $zpl .= "^FO320,315^A0N,28,28^FD{$customer}^FS\n";
        $zpl .= "^FO120,360^A0N,30,30^FDItems:^FS\n";
        $zpl .= "^FO320,360^A0N,30,30^FD{$itemsCount} unidades^FS\n";
        $zpl .= "^FO120,405^A0N,30,30^FDFecha:^FS\n";
        $zpl .= "^FO320,405^A0N,30,30^FD{$fecha}^FS\n";
        $zpl .= "^FO120,478^GB632,2,2^FS\n";
        $zpl .= "^FO120,496^A0N,18,18^FDCOMFAR Paraguay - WMS XPert - Packing^FS\n";
        $zpl .= "^XZ";

        if ($copies > 1) {
            $zpl = trim(str_repeat($zpl . "\n", $copies));
        }

        $preview = $this->packingLabelPreview($barcode, $ov, $bultoActual, $bultoTotal, $customer, $producto, $itemsCount, $fecha);
        return ['zpl' => $zpl, 'preview_html' => $preview];
    }

    private function packingLabelPreview(string $barcode, string $ov, int $bAct, int $bTot, string $customer, string $producto, $itemsCount, string $fecha): string
    {
        $bars = $barcode ? $this->textToBarcodeSvg($barcode) : '';
        return <<<HTML
<div style="width:420px;border:2px solid #333;border-radius:4px;padding:14px 18px;font-family:monospace;background:#fff;color:#000">
  <div style="text-align:center;margin-bottom:6px">{$bars}</div>
  <div style="text-align:center;font-size:11px;color:#666;letter-spacing:2px;margin-bottom:10px">{$barcode}</div>
  <hr style="border:none;border-top:1px solid #999;margin:6px 0 10px">
  <div style="font-size:14px;line-height:1.7">
    <div><span style="display:inline-block;width:88px">OV:</span><strong>{$ov}</strong></div>
    <div><span style="display:inline-block;width:88px">Bulto:</span>{$bAct} de {$bTot}</div>
    <div><span style="display:inline-block;width:88px">Cliente:</span>{$customer}</div>
    <div><span style="display:inline-block;width:88px">Ítems:</span>{$itemsCount} unidades</div>
    <div><span style="display:inline-block;width:88px">Fecha:</span>{$fecha}</div>
  </div>
</div>
HTML;
    }

    // ── Etiqueta de Envio/Despacho ──────────────────────────────

    /**
     * @param array $data {customer_name, order_number, items_count}
     * @return array {zpl, preview_html}
     */
    public function deliveryLabel(array $data): array
    {
        $customer   = mb_substr($data['customer_name'] ?? '', 0, 35);
        $orderNum   = $data['order_number'] ?? '';
        $itemsCount = $data['items_count'] ?? 0;
        $date       = date('Y-m-d');

        $zpl  = "^XA\n";
        $zpl .= "^FO30,20^A0N,18,18^FDENVIO / DESPACHO^FS\n";
        $zpl .= "^FO30,50^A0N,28,28^FD{$customer}^FS\n";
        $zpl .= "^FO30,90^A0N,22,22^FDPedido: {$orderNum}^FS\n";
        $zpl .= "^FO30,120^BY2^BCN,80,Y,N,N^FD{$orderNum}^FS\n";
        $zpl .= "^FO30,220^A0N,20,20^FDItems: {$itemsCount}^FS\n";
        $zpl .= "^FO30,250^A0N,18,18^FDFecha: {$date}^FS\n";
        $zpl .= "^XZ";

        $barcode = $this->textToBarcodeSvg($orderNum);
        $preview = <<<HTML
<div style="width:384px;height:192px;border:2px solid #333;border-radius:4px;padding:10px 14px;font-family:monospace;background:#fff;color:#000;overflow:hidden">
  <div style="font-size:11px;color:#777;text-transform:uppercase;letter-spacing:1px">Envio / Despacho</div>
  <div style="font-size:18px;font-weight:bold;margin:4px 0">{$customer}</div>
  <div style="font-size:14px;margin-bottom:4px">Pedido: <strong>{$orderNum}</strong></div>
  <div style="margin-bottom:6px">{$barcode}</div>
  <div style="font-size:11px;display:flex;gap:16px">
    <span>Items: {$itemsCount}</span>
    <span>Fecha: {$date}</span>
  </div>
</div>
HTML;

        return ['zpl' => $zpl, 'preview_html' => $preview];
    }

    // ── Envio a impresora via TCP ───────────────────────────────

    /**
     * Envia codigo ZPL directamente a una impresora Zebra via socket TCP.
     *
     * @param string      $zpl       Codigo ZPL completo
     * @param string|null $printerIp IP de la impresora (null = default config)
     * @param int         $port      Puerto RAW (default 9100)
     * @return bool
     */
    public function sendToPrinter(string $zpl, ?string $printerIp = null, int $port = 9100): bool
    {
        $ip = $printerIp ?? $this->config['default_printer_ip'] ?? null;
        if (!$ip) {
            throw new \RuntimeException('No hay impresora configurada');
        }

        $socket = @fsockopen($ip, $port, $errno, $errstr, 5);
        if (!$socket) {
            throw new \RuntimeException("No se pudo conectar a la impresora {$ip}:{$port} - {$errstr}");
        }

        // Por IP el ZPL va crudo al puerto 9100: la impresora usa SU densidad/velocidad
        // guardadas, por eso sale clara/débil frente al camino USB (que aplica el driver Zebra).
        // Forzamos densidad (~SD) y velocidad (^PR) configurables para igualar la calidad USB.
        $darkness = max(0, min(30, (int) ($this->config['darkness'] ?? 22)));
        $speed    = max(2, min(12, (int) ($this->config['speed'] ?? 3)));
        $prefix   = "~SD{$darkness}\r\n^XA^PR{$speed},{$speed}^XZ\r\n";

        fwrite($socket, $prefix . $zpl);
        fclose($socket);

        return true;
    }

    // ── Generar ZPL a partir de label almacenado ────────────────

    /**
     * Genera ZPL a partir de los datos de un label existente en BD.
     *
     * @param array $label Registro de la tabla labels con content_data
     * @return array {zpl, preview_html}
     */
    public function generateFromLabel(array $label): array
    {
        $content = $label['content_data'] ?? [];
        $type    = $label['label_type'] ?? '';

        switch ($type) {
            case 'ITEM':
            case 'BATCH':
            case 'RECEIPT':
                return $this->productLabel([
                    'item_code'      => $content['item_code'] ?? '',
                    'item_name'      => $content['item_name'] ?? '',
                    'batch_number'   => $content['batch_number'] ?? '',
                    'supplier_lot'   => $content['supplier_lot'] ?? '',
                    'expiry_date'    => $content['expiry_date'] ?? '',
                    'quantity'       => $content['quantity'] ?? '',
                    'uom'            => $content['uom'] ?? 'UN',
                    'reception_date' => $content['expected_date'] ?? date('Y-m-d'),
                ]);

            case 'BIN':
                return $this->binLabel([
                    'bin_code'       => $content['bin_code'] ?? '',
                    'warehouse_name' => $content['warehouse_code'] ?? '',
                    'zone_name'      => $content['zone'] ?? '',
                ]);

            case 'CONTAINER':
                return $this->containerLabel([
                    'container_code'   => $content['container_code'] ?? '',
                    'contents_summary' => $content['container_type'] ?? '',
                ]);

            default:
                throw new \RuntimeException("Tipo de etiqueta '{$type}' no soportado para ZPL");
        }
    }

    // ── Helper: pseudo-barcode SVG para preview HTML ────────────

    private function textToBarcodeSvg(string $text): string
    {
        if (!$text) {
            return '';
        }
        $bars = '';
        $x = 0;
        $chars = str_split($text);
        foreach ($chars as $ch) {
            $code = ord($ch);
            // Pseudo Code128 - patron visual representativo
            $widths = [
                ($code % 3) + 1,
                2,
                (($code >> 2) % 3) + 1,
                1,
                (($code >> 4) % 2) + 1,
                2,
            ];
            $isBar = true;
            foreach ($widths as $w) {
                if ($isBar) {
                    $bars .= "<rect x=\"{$x}\" y=\"0\" width=\"{$w}\" height=\"40\" fill=\"#000\"/>";
                }
                $x += $w;
                $isBar = !$isBar;
            }
        }
        $totalW = $x + 4;
        return "<svg width=\"{$totalW}\" height=\"50\" viewBox=\"0 0 {$totalW} 50\" xmlns=\"http://www.w3.org/2000/svg\">{$bars}<text x=\"0\" y=\"48\" font-size=\"8\" font-family=\"monospace\">{$text}</text></svg>";
    }
}
