<?php

use TCPDF;

if (!isset($baseDir)) {
	$baseDir = $_SERVER['DOCUMENT_ROOT'];
}

require_once $baseDir . '/../scripts/session_functions.php';

ini_wkvs_session();

check_user_permission('kampfrichter');

verify_csrf();

if (!isset($baseDir)) {
    $baseDir = $_SERVER['DOCUMENT_ROOT'];
}

$type = 'kr';
$data = require $baseDir . '/../scripts/db/db-verbindung-script.php';

if ($data['success'] === false) {
    header('Content-Type: application/json');
	echo json_encode(['success' => false, 'message' => $data['message']]);
	exit;
}

require $baseDir . '/../scripts/db/db-tables.php';
require $baseDir . '/../scripts/db/db-functions.php';

$wkName = db_get_var($mysqli, "SELECT `value` FROM $db_tabelle_var WHERE `name` = ?", ['wkName']);

$wkName = ($wkName === null) ? 'Keinen Wettkampfname angegeben. Setzen unter: /intern/wk-leitung/einstellungen' : $wkName;

$normalFontSize = 11;
$marginSide = 10;
$marginTop = 10;
$minMarginBottomTable = 20;
$format = 'A4';
$orientation = 'L';

$programm = trim($_POST['prog'] ?? 'P6A');
$buttontype = trim($_POST['type'] ?? 'downloadRangliste');

$allowedOperations = ['downloadRangliste', 'updateServerRangliste'];

if (!in_array($buttontype, $allowedOperations)) {
    header('Content-Type: application/json');
    echo json_encode(["success" => false, "message" => "Invalide Operation"]);
    exit;
}

$upperprogramm = strtoupper($programm);

$stmt = $mysqli->prepare("SELECT `id`, `name` FROM $db_tabelle_disziplinen ORDER BY start_index ASC");

if (!$stmt->execute()) {
  http_response_code(400);
  exit;
}

$result = $stmt->get_result();
$geraete = $result->fetch_all(MYSQLI_ASSOC);
$disciplines = array_map(
    'strtolower',
    array_column($geraete, 'name')
);

$stmt->close();

// Determine current year
$current_year = date('Y');
$monat = date('n');
if ($monat > 6) $current_year++;

// Prepare SQL statement
$stmt = $mysqli->prepare("
    SELECT * FROM $db_tabelle_teilnehmende 
    WHERE LOWER(programm) = LOWER(?) 
      AND (bezahltoverride = ? OR (bezahlt = ? OR bezahltoverride = ?))
    ORDER BY rang ASC
");
$bezahlt1 = '4';
$bezahlt2 = '4';
$bezahlt3 = '0';
$stmt->bind_param('ssss', $programm, $bezahlt1, $bezahlt2, $bezahlt3);
$stmt->execute();

$result = $stmt->get_result();
$personen = $result->fetch_all(MYSQLI_ASSOC);


// Close statement
$stmt->close();

$programmId = db_get_var($mysqli, "SELECT `id` FROM $db_tabelle_kategorien WHERE LOWER(`programm`) = LOWER(?)", [$programm]);
$cache = require $baseDir . '/../scripts/cache/noten-config-calculating-cache.php';
$ascArrayAnzahlLaeufeJSON = $cache['anzahl_laeufe_json'];

// 1. Get the IDs from the first query results
$turnerinnenIds = array_column($personen, 'id');

$rangNote = intval(db_get_var($mysqli, "SELECT `value` FROM $db_tabelle_var WHERE `name` = ?", ['rangNote']));

if ($rangNote === 0) {
    header('Content-Type: application/json');
    echo json_encode(["success" => false, "message" => "Keine Note für Rangvergabe ausgewählt"]);
	http_response_code(400);
	exit;
}

$current_wk_id = (int) db_get_variable($mysqli, $db_tabelle_var, ['current_wk_id']) ?: 0;

if (!empty($turnerinnenIds)) {
    // 2. Create a string of placeholders: ?,?,?
    $placeholders = implode(',', array_fill(0, count($turnerinnenIds), '?'));

    // 3. Prepare the IN statement
    $sqlNoten = "SELECT * FROM $db_tabelle_wertungen WHERE person_id IN ($placeholders) AND `wk_id` = ?";
    $stmtNoten = $mysqli->prepare($sqlNoten);

    // --- FIX STARTS HERE ---
    
    // 1. Combine all values into one flat array for binding
    $paramsArray = array_merge($turnerinnenIds, [$current_wk_id]);

    // 2. Build the types string
    // 'i' for every ID, plus 'i' (or 's') for the year
    $types = str_repeat('i', count($turnerinnenIds)) . 's';

    // 3. Unpack the combined array into the bind_param function
    $stmtNoten->bind_param($types, ...$paramsArray);

    // --- FIX ENDS HERE ---

    $stmtNoten->execute();
    $notenResult = $stmtNoten->get_result();
    $notenEntries = $notenResult->fetch_all(MYSQLI_ASSOC);
    
    $stmtNoten->close(); // Close inside the IF to avoid errors if IDs were empty
} else {
    $notenEntries = [];
}

$indexedNotenEntries = [];
foreach ($notenEntries as $sn) {
	if (!isset($indexedNotenEntries[$sn['person_id']][$sn['note_bezeichnung_id']][$sn['geraet_id']])) {
		$indexedNotenEntries[$sn['person_id']][$sn['note_bezeichnung_id']][$sn['geraet_id']] = [];
	}
	$indexedNotenEntries[$sn['person_id']][$sn['note_bezeichnung_id']][$sn['geraet_id']][$sn['run_number']] = $sn['value'];
}

$orderBestRang = db_get_var($mysqli, "SELECT `value` FROM $db_tabelle_var WHERE `name` = ?", ['orderBestRang']);

$okValuesOrderBestRang = ["ASC", "DESC"];

if (!in_array($orderBestRang, $okValuesOrderBestRang)) {
    header('Content-Type: application/json');
    echo json_encode(["success" => false, "message" => "Invalide Sortierung der Ränge"]);
	http_response_code(400);
	exit;
}

$alleNoten = db_select($mysqli, $db_tabelle_wertungstypen, "id, default_value, nullstellen, pro_geraet, geraete_json, zeige_auf_rangliste, groesse_auf_rangliste, name", "zeige_auf_rangliste = ? OR id = ?", ['1', $rangNote]);

$displayedNoten = [];

$alleNotenIndexed = array_column($alleNoten, null, 'id');

foreach ($alleNotenIndexed as $key => $sN) {
	if (intval($sN['zeige_auf_rangliste']) === 1) { 
		$displayedNoten[$key] = $sN; 
	}
}

$ascArrayDefaultValues = array_column($alleNoten, 'default_value', 'id');
$ascArrayGeraeteJSON = array_column($alleNoten, 'geraete_json', 'id');

foreach ($ascArrayGeraeteJSON as $key => $saagj) {
    $ascArrayGeraeteJSON[$key] = json_decode($saagj, true) ?? [];
}

// 1. Initialize the structure for every person
$indexedNotenArray = [];
$sortArray = [];

$notenGeraeteArray = array_merge($geraete, array(array("id" => 0)));

foreach ($personen as $sp) {
    $pId = $sp['id'];
    foreach ($displayedNoten as $an) {
        $nId = $an['id'];
        $isProGeraet = (intval($an['pro_geraet']) === 1);
        $allowedGeraete = $ascArrayGeraeteJSON[$nId] ?? [];

        foreach ($notenGeraeteArray as $g) {
            $gId = $g['id'];
            
            if ($isProGeraet) {
                if ($gId === 0) continue; 
            } else {
                if (!in_array($gId, $allowedGeraete)) continue;
            }

            $indexedNotenArray[$pId][$gId][$nId] = $indexedNotenEntries[$pId][$nId][$gId] ?? [$an['default_value']];
        }
        
        if (intval($nId) === $rangNote) {
            $val = $indexedNotenArray[$pId][0][$nId] ?? [$an['default_value']];
            $sortArray[$pId] = is_array($val) ? end($val) : $val; // fallback to the last run for sorting if needed, or total should just be one run
        }
    }
}

$wkWebpageDomain = db_get_var($mysqli, "SELECT `value` FROM $db_tabelle_var WHERE `name` = ?", ['linkWebseite']);

// Load TCPDF
require $baseDir . '/../composer/vendor/autoload.php';

// Optional: load custom font
$fontfile = $baseDir . '/wp-content/uploads/fonts/Inter-Regular.ttf'; // adjust path
if (file_exists($fontfile)) {
    $fontname = TCPDF_FONTS::addTTFfont($fontfile, 'TrueTypeUnicode', '', 32);
}

class RanglistePDF extends TCPDF
{
	public $current_year;
	public $programm;
	public $upperprogramm;
	public $columns;
	public $disciplines;
    public $wkName;
    public $marginSide;
    public $marginTop;
    public $pdfHeight;
    public $pdfWidth;
    public $wkWebpageDomain;
	// Page header

	public $headerBottomY = 0;

	public function Header()
	{
        $this->SetFillColor(255, 255, 255);

        $this->Rect(0, 0, $this->pdfWidth, $this->pdfHeight, 'F');

        $arrayTitles = [];
		$image_file = $_SERVER['DOCUMENT_ROOT'] . '/intern/img/logo-normal.png';
		$this->SetY($this->marginTop);
        $this->SetX(($this->marginSide ?? 10));
		$this->SetFont('freesans', '', 20);
		$this->Cell(0, 0, 'Rangliste ' . $this->wkName . ' ' . $this->current_year, 0, 1, 'L', 0, '', 0, false, 'T', 'M');
		$preimageX = $this->GetX();
		$preimageY = $this->GetY();
		$this->Image($image_file, $this->pdfWidth - 10 - $this->marginSide, $this->marginTop, '', 10, 'png', '', 'T');

		$this->SetY($preimageY); 
        $this->SetX($this->marginSide ?? 10); // Force back to left margin
        $this->SetFont('freesans', '', 11);
        $this->Cell(0, 10, 'Programm: ' . $this->upperprogramm, 0, 1, 'L');

		$this->Ln(5);

		$columns = $this->columns;
		$startY = $this->GetY();
		$this->SetFont('', 'B');

        $this->SetX($this->marginSide ?? 10);

		foreach ($columns as $col) {
            $isDuplicate = in_array($col['header'], $arrayTitles);

            $title = $isDuplicate ? '' : $col['header'];

            if (!$isDuplicate) {
                $arrayTitles[] = $col['header'];
            }

			if (($col['rotate'] ?? false) && !$isDuplicate) {
				$x = $this->GetX();
				$y = $this->GetY() + 7;

				$this->StartTransform();
				$this->Rotate(45, $x, $y);

				$lineY = $startY + 7;
				if (($col['id'] ?? '') === 'gesamt') {
					$this->SetLineWidth(0.6);
				} else {
					$this->SetLineWidth(0.2);
				}
				
				$this->Line($x, $lineY, $x + ($col['width_header'] ?? 0), $lineY);
				$this->SetLineWidth(0.2);

				$this->Cell($col['width_header'] ?? 0, 7, $title ?? '', 0, 0, 'C');
				$this->StopTransform();
				$this->SetX($x + ($col['max_width'] ?? 0));
			} else {
				$this->Cell($col['max_width'] ?? 0, 7, $title ?? '', 0, 0, $col['align'] ?? 'L');
			}
		}

		$this->Ln();
		$this->headerBottomY = $this->GetY();
	}


	// Page footer
	public function Footer()
	{
		$this->SetLineWidth(0.6);
		$this->Line($this->marginSide ?? 10, $this->headerBottomY, $this->pdfWidth - ($this->marginSide ?? 10), $this->headerBottomY);

		$this->SetLineWidth(0.2);

		$this->SetY(0);
		$this->SetX(0);

		$this->SetFillColor(255, 255, 255); // white

		$this->Cell(($this->marginSide ?? 10), $this->pdfHeight, '', 0, 0, 'L', true);

		$this->SetY(0);
		$this->SetX($this->pdfWidth - ($this->marginSide ?? 10));

		$this->Cell(($this->marginSide ?? 10), $this->pdfHeight, '', 0, 0, 'L', true);

        $this->SetY(-15);
		$this->SetFont('freesans', 'I', 8);
		$this->Cell(0, 10, 'Seite ' . $this->getAliasNumPage() . ' von ' . $this->getAliasNbPages(), 0, false, 'C', 0, '', 0, false, 'T', 'M');
		$this->SetFont('', '');
		$this->Cell(0, 10, ($this->wkWebpageDomain ?? $_SERVER['HTTP_HOST']), 0, 0, 'R', false, 'https://' . ($this->wkWebpageDomain ?? $_SERVER['HTTP_HOST']), 0, false, 'T', 'M');
	}
}

$pdf = new RanglistePDF($orientation, 'mm', $format, true, 'UTF-8', false);

$pdfHeight = $pdf->getPageHeight();
$pdfWidth = $pdf->getPageWidth();

$pdf->current_year = $current_year;
$pdf->wkName = $wkName;
$pdf->wkWebpageDomain = $wkWebpageDomain;
$pdf->programm = $programm;
$pdf->upperprogramm = $upperprogramm;
$pdf->marginSide = $marginSide;
$pdf->marginTop = $marginTop;
$pdf->pdfHeight = $pdfHeight;
$pdf->pdfWidth = $pdfWidth;

$pdf->SetCreator(PDF_CREATOR);
$pdf->SetAuthor($wkName);
$pdf->SetTitle($wkName . "_Ergebnisse_" . $programm . "_" . $current_year . ".pdf");

$pdf->SetAutoPageBreak(FALSE);


$pdf->SetFont('freesans', '', 11);

// Define columns dynamically
$columns = [
    ['id' => 'rang', 'header' => 'Rang', 'align' => 'C', 'flex' => false, 'type' => 'rank'],
    ['id' => 'name', 'header' => 'Name', 'align' => 'L', 'flex' => true, 'type' => 'field', 'field' => 'name'],
    ['id' => 'vorname', 'header' => 'Vorname', 'align' => 'L', 'flex' => true, 'type' => 'field', 'field' => 'vorname'],
    ['id' => 'geburtsdatum', 'header' => 'Jg.', 'align' => 'C', 'flex' => false, 'type' => 'year', 'field' => 'geburtsdatum'],
    ['id' => 'verein', 'header' => 'Verein', 'align' => 'L', 'flex' => true, 'type' => 'field', 'field' => 'verein'],
];

// Add notes dynamically per device
foreach ($geraete as $g) {
    foreach ($displayedNoten as $noteDef) {
        $neni = $noteDef['id'];
        $isProGeraet = (intval($noteDef['pro_geraet']) === 1);
        $allowedGeraete = $ascArrayGeraeteJSON[$neni] ?? [];

        if ($isProGeraet || in_array($g['id'], $allowedGeraete)) {
            $columns[] = [
                'id' => 'note_' . $g['id'] . '_' . $neni,
                'header' => $g['name'],
                'align' => 'C',
                'flex' => false,
                'rotate' => true,
                'type' => 'note',
                'geraet_id' => $g['id'],
                'note_bezeichnung_id' => $neni,
                'nullstellen' => $noteDef['nullstellen'] ?? 2,
                'font_size' => intval($noteDef['groesse_auf_rangliste']) ?? 0
            ];
        }
    }
}

// Add Gesamt (Total) column
$columns[] = [
    'id' => 'gesamt',
    'header' => $alleNotenIndexed[$rangNote]['name'] ?? 'Total',
    'align' => 'C',
    'flex' => false,
    'rotate' => true,
    'type' => 'note',
    'geraet_id' => 0,
    'note_bezeichnung_id' => $rangNote,
    'nullstellen' => $alleNotenIndexed[$rangNote]['nullstellen'] ?? 2,
    'font_size' => intval($alleNotenIndexed[$rangNote]['groesse_auf_rangliste']) ?? 0
];



$padding = 4;

unset($col);

// 1. Calculate initial max widths based on headers
foreach ($columns as &$col) {
    $col['width_header'] = $pdf->GetStringWidth($col['header']) + ($padding * 2);
    if ($col['type'] !== 'note') {
        $col['max_width'] = $col['width_header'];
    } else {
        $col['max_width'] = 0;
    }
}

// 2. Fill missing defaults and update max widths based on data
foreach ($personen as $sp) {
    $pId = $sp['id'];
    foreach ($indexedNotenArray[$pId] as $sG => $currentNoten) {
        foreach ($displayedNoten as $noteDef) {
            $neni = $noteDef['id'];
            if (isset($currentNoten[$neni])) {
                $runs = $currentNoten[$neni];
                if (is_array($runs)) {
                    $runsConfig = $ascArrayAnzahlLaeufeJSON[$neni] ?? [];
                    $runsCount = $runsConfig[$sG][$programmId] ?? $runsConfig[$sG]['all'] ?? $runsConfig["default"] ?? 1;
                    $defaultValue = $ascArrayDefaultValues[$neni] ?? 0;
                    
                    for ($r = 1; $r <= $runsCount - 1; $r++) {
                        if (!isset($runs[$r])) {
                            $runs[$r] = $defaultValue;
                        }
                    }
                    $indexedNotenArray[$pId][$sG][$neni] = $runs;
                    
                    ksort($runs);
                    $maxLenStr = "";
                    foreach ($runs as $rVal) {
                        $str = number_format((float)$rVal, $noteDef['nullstellen']);
                        // Approximate max width by string length, GetStringWidth will do the real check later
                        if (strlen($str) > strlen($maxLenStr)) {
                            $maxLenStr = $str;
                        }
                    }
                    $val = $maxLenStr;
                } else {
                    $val = number_format((float)$runs, $noteDef['nullstellen']);
                }
            } else {
                // Logic for "Pro Gerät" vs "Specific/Total"
                $isProGeraet = (intval($noteDef['pro_geraet']) === 1);
                $allowedGeraete = $ascArrayGeraeteJSON[$neni] ?? [];
                
                if ($isProGeraet) {
                    if ($sG === 0) continue; 
                } else {
                    if (!in_array($sG, $allowedGeraete)) continue;
                }

                $defaultValue = $ascArrayDefaultValues[$neni] ?? 0;
                
                $runsConfig = $ascArrayAnzahlLaeufeJSON[$neni] ?? [];
                $runsCount = $runsConfig[$sG][$programmId] ?? $runsConfig[$sG]['all'] ?? $runsConfig["default"] ?? 1;
                
                $valArray = [];
                $maxLenStr = "";
                for ($r = 1; $r <= $runsCount - 1; $r++) {
                    $valArray[$r] = $defaultValue;
                    $str = number_format((float)$defaultValue, $noteDef['nullstellen']);
                    if (strlen($str) > strlen($maxLenStr)) {
                        $maxLenStr = $str;
                    }
                }
                
                $val = $maxLenStr;
                $indexedNotenArray[$pId][$sG][$neni] = $valArray;

                if ($neni === $rangNote && $sG === 0) {
                    $sortArray[$pId] = $defaultValue;
                }
            }

            // Update column width if this note is displayed
            foreach ($columns as &$col) {
                if ($col['type'] === 'note' && $col['geraet_id'] == $sG && $col['note_bezeichnung_id'] == $neni) {

                    $customFontSize = (isset($col['font_size']) && $col['font_size'] != 0);

                    $cpadding = $padding;

                    if ($customFontSize) {
                        $pdf->SetFont('', '', $col['font_size']);
                        $cpadding = ($col['font_size'] / $normalFontSize) * $padding;
                    }

                    $width = $pdf->GetStringWidth($val) + ($cpadding * 2);
                    if ($width > $col['max_width']) {
                        $col['max_width'] = $width;
                    }

                    if ($customFontSize) {
                        $pdf->SetFont('', '', $normalFontSize);
                    }
                }
            }
            unset($col);
        }
    }

    // Also update widths for static fields
    foreach ($columns as &$col) {
        if ($col['type'] === 'field' || $col['type'] === 'year' || $col['type'] === 'rank') {
            $text = '';
            if ($col['type'] === 'rank') {
                $text = '999'; // Placeholder for rank width
            } elseif ($col['type'] === 'year') {
                $text = '0000';
            } elseif (isset($sp[$col['field']])) {
                $text = $sp[$col['field']];
            }
            $width = $pdf->GetStringWidth($text) + ($padding * 2);
            if ($width > $col['max_width']) {
                $col['max_width'] = $width;
            }
        }
    }
    unset($col);
}

function calculateRanks(array $scoreArray, $direction = 'DESC') {
    // 1. Sort the scores while preserving keys
    if ($direction === 'DESC') {
        arsort($scoreArray); // High scores first
    } else {
        asort($scoreArray);  // Low scores first
    }

    $ranks = [];
    $currentRank = 0;
    $lastScore = null;
    $skipped = 0;

    foreach ($scoreArray as $pId => $score) {
        if ($score !== $lastScore) {
            $currentRank += ($skipped + 1);
            $skipped = 0;
        } else {
            $skipped++;
        }
        
        $ranks[$pId] = $currentRank;
        $lastScore = $score;
    }
    return $ranks;
}


// 3. Finalize Ranks
$ranks = calculateRanks($sortArray, $orderBestRang);

// 4. Distribute Flexible Widths
$tablew = $pdfWidth - (2 * $marginSide); // total table width (A4 landscape in mm minus margins)
$fixedWidth = 0;
$flexCols = [];

foreach ($columns as $col) {
    if ($col['flex'] ?? false) {
        $flexCols[] = $col['id'];
        $fixedWidth += $col['max_width'];
    } else {
        $fixedWidth += $col['max_width'];
    }
}

$effTableWidth = $tablew - $fixedWidth;


if (!empty($flexCols) && $effTableWidth > 0) {
    $flexTotalInitial = 0;
    foreach ($columns as $col) {
        if ($col['flex'] ?? false) $flexTotalInitial += $col['max_width'];
    }

    foreach ($columns as &$col) {
        if ($col['flex'] ?? false) {
            $col['max_width'] += ($col['max_width'] / $flexTotalInitial) * $effTableWidth;
        }
    }
    unset($col);
}

$pdf->columns = $columns;
$pdf->disciplines = $disciplines;

$pdf->AddPage();

// After AddPage(), the header has been drawn
$margin_top = $pdf->headerBottomY;

// Now adjust your margins if needed
$pdf->SetMargins($marginSide, $margin_top, $marginSide); // +5 mm padding below header
$pdf->SetY($margin_top); // Move cursor below header manually

$pdf->SetFont('', '');
$rIndex = 0;

usort($personen, function($a, $b) use ($ranks) {
    $rankA = $ranks[$a['id']];
    $rankB = $ranks[$b['id']];

    return $rankA <=> $rankB;
});

foreach ($personen as $r) {

	$pdf->SetTextColor(0, 0, 0);

	$rang = $ranks[$r['id']];

	if ($rang == 1) {
		$pdf->SetFillColor(255, 235, 128);
	} elseif($rang == 2) {
		$pdf->SetFillColor(224, 224, 224);
	} elseif($rang == 3) {
		$pdf->SetFillColor(230, 191, 153);
	} elseif ($rIndex % 2 == 0) {
		$pdf->SetFillColor(255, 255, 255); // white
	} else {
		$pdf->SetFillColor(240, 240, 240); // light gray
	}


	// Determine row height based on max runs
	$maxRuns = 1;
	foreach ($columns as $col) {
	    if ($col['type'] === 'note') {
	        $runs = $indexedNotenArray[$r['id']][$col['geraet_id']][$col['note_bezeichnung_id']] ?? [];
	        if (is_array($runs) && count($runs) > $maxRuns) {
	            $maxRuns = count($runs);
	        }
	    }
	}

	// Base height is 8, add extra height for additional runs. Let's say 4mm per additional line.
	// But to keep it centered properly, we will use MultiCell with valign='M' (Middle).
	$rowHeight = max(8, $maxRuns * 5 + 3); // 5mm per line

    if ($pdf->getY() + $rowHeight >= $pdfHeight - $minMarginBottomTable) {
        $pdf->addPage();
        $pdf->setX($marginSide);
    }

	foreach ($columns as $col) {

        $customFontSize = (isset($col['font_size']) && $col['font_size'] != 0);

        if ($customFontSize) {
            $pdf->SetFont('', '', $col['font_size']);
        }

        $startX = $pdf->GetX();
        $startY = $pdf->GetY();

        if ($col['id'] === 'rang') {
            $pdf->SetFont('', 'B');
            $pdf->MultiCell($col['max_width'], $rowHeight, $rang, 'B', 'C', true, 0, $startX, $startY, true, 0, false, true, $rowHeight, 'M');
            $pdf->SetFont('', '');
        } elseif ($col['type'] === 'field') {
            $pdf->MultiCell($col['max_width'], $rowHeight, $r[$col['field']], 'B', $col['align'], true, 0, $startX, $startY, true, 0, false, true, $rowHeight, 'M');
        } elseif ($col['type'] === 'year') {
            $pdf->MultiCell($col['max_width'], $rowHeight, (new DateTime($r[$col['field']]))->format("Y"), 'B', $col['align'], true, 0, $startX, $startY, true, 0, false, true, $rowHeight, 'M');
        } elseif ($col['type'] === 'note') {
            $runs = $indexedNotenArray[$r['id']][$col['geraet_id']][$col['note_bezeichnung_id']] ?? [];
            if (!is_array($runs)) {
                $runs = [$runs];
            } else {
                ksort($runs);
            }

            $numRuns = max(count($runs), 1);
            $lineHeight = $rowHeight / $numRuns;

            $pdf->SetXY($startX, $startY);
            $pdf->Cell($col['max_width'], $rowHeight, '', 0, 0, '', true);

            $runIndex = 0;
            foreach (array_values($runs) as $rnVal) {
                $formatted = number_format((float)$rnVal, $col['nullstellen'] ?? 0);
                $currentY = $startY + ($runIndex * $lineHeight);
                
                $pdf->SetXY($startX, $currentY);
                
                $pdf->Cell($col['max_width'], $lineHeight, $formatted, 0, 0, $col['align'], false);
                $runIndex++;
            }

            // Draw the bottom border for the entire row height
            $pdf->Line($startX, $startY + $rowHeight, $startX + $col['max_width'], $startY + $rowHeight);

            // Reset pointer for the next column
            $pdf->SetXY($startX + $col['max_width'], $startY);

            if ($col['id'] === 'gesamt') {
                // Draw the heavy separator line on the left side of the "Gesamt" column
                $pdf->SetLineWidth(0.6);
                $pdf->Line($startX, $startY, $startX, $startY + $rowHeight);
                $pdf->SetLineWidth(0.2);
            }
        }

        if ($customFontSize) {
            $pdf->SetFont('', '', $normalFontSize);
        }
    }

	$pdf->SetY($startY + $rowHeight); // Move Y manually instead of Ln() to account for rowHeight
	$rIndex++;
}



$pdf->SetFont('', '', 7);
$textanzturnerinnen = (count($personen) > 1) ? 'Turnerinnen': 'Turnerin';
$pdf->Cell(20, 6, count($personen).' '.$textanzturnerinnen, 0, 0, 'L');

if ($buttontype === 'downloadRangliste') {
    $pdf->Output($wkName . "_Ergebnisse_" . $programm . "_" . $current_year . ".pdf", 'I');
    exit;
} elseif ($buttontype === 'updateServerRangliste') {
    $dir = $baseDir . '/files/ranglisten/';
    if (!file_exists($dir)) mkdir($dir, 0755, true);
    $localPath = $dir . str_replace("/", "", str_replace(" ", "_", $wkName)) . "_Ergebnisse_" . str_replace("/", "", $programm) . "_" . $current_year . ".pdf";
    if (file_exists($localPath)) unlink($localPath);
    $pdf->Output($localPath, 'F');
    header('Content-Type: application/json');
    echo json_encode(["success" => true, "message" => "PDF wurde aktualisiert"]);
    exit;
}