<?php
// TBN.php — Temperature Bourne Neck graph (1250x625)
// - 4 in-house sensors from /var/www/html/temp.txt (SSID Verizon_46KJ9W)
// - Optional 5th trace: Buzzards Bay temp from /var/www/html/buzzards_bay.txt
// - Thicker colored lines + dot+X markers
// - Solid +5 C line (freeze warning)
// - Green dotted 50 F (~10 C) "grandparent comfort" line
// - Legend with room labels: OUTSIDE, KITCHEN, LOFT, BATH, BUZZARDS

header('Content-Type: image/png');

// -----------------------------------------------------------------------------
// CONFIG
// -----------------------------------------------------------------------------
$logFile       = '/var/www/html/temp.txt';
$routerSsid    = 'Verizon_46KJ9W';
$hoursBack     = 24;

// Optional Buzzards Bay weather file
// Each line: ISO8601 time, C-temp
//   2025-12-09T17:50:00-05:00,3.2
$buzzardsFile  = '/var/www/html/buzzards_bay.txt';

// Image size (25% bigger than 1000x500)
$width  = 1250;
$height = 625;

// -----------------------------------------------------------------------------
// CREATE IMAGE + COLORS
// -----------------------------------------------------------------------------
$img = imagecreatetruecolor($width, $height);

$bg         = imagecolorallocate($img, 240, 240, 240);
$axisColor  = imagecolorallocate($img, 0,   0,   0);
$gridColor  = imagecolorallocate($img, 210, 210, 210);
$textColor  = imagecolorallocate($img, 0,   0,   0);
$errorRed   = imagecolorallocate($img, 180, 0,   0);
$freezeLine = imagecolorallocate($img, 0,   0,   180); // +5 C
$setLine    = imagecolorallocate($img, 0,  160,  0);   // 50 F (~10 C), dotted
$buzzColor  = imagecolorallocate($img, 0,   0,   0);   // Buzzards Bay: black

// Sensor line colors (fixed order)
$lineColors = [
    imagecolorallocate($img, 0,   0,   255),   // 0: blue   -> OUTSIDE
    imagecolorallocate($img, 0,   150, 0),     // 1: green  -> KITCHEN
    imagecolorallocate($img, 200, 0,   0),     // 2: red    -> LOFT
    imagecolorallocate($img, 255, 140, 0),     // 3: orange -> BATH
];

// Room labels matched by color index
$roomLabels = [
    'OUTSIDE',
    'KITCHEN',
    'LOFT',
    'BATH',
];

imagefilledrectangle($img, 0, 0, $width - 1, $height - 1, $bg);

// Margins
$left   = 110;   // more room for Y labels
$right  = 40;
$top    = 80;
$bottom = 120;

$plotW = $width  - $left - $right;
$plotH = $height - $top  - $bottom;

// -----------------------------------------------------------------------------
// READ HOUSE SENSOR LOG
// -----------------------------------------------------------------------------
if (!file_exists($logFile)) {
    imagestring($img, 5, 10, 10, "No log file: $logFile", $errorRed);
    imagepng($img);
    imagedestroy($img);
    exit;
}

$dataSensors = [];   // mac => [ ['t'=>timestamp, 'c'=>temp], ... ]
$allTimes    = [];
$allTemps    = [];

$fh = fopen($logFile, 'r');
if ($fh === false) {
    imagestring($img, 5, 10, 10, "Cannot open $logFile", $errorRed);
    imagepng($img);
    imagedestroy($img);
    exit;
}

while (($line = fgets($fh)) !== false) {
    $line = trim($line);
    if ($line === '') continue;

    // Example:
    // 1765320602,2025-12-09T17:50:02-05:00,c=17.1&id=24&ssid=Verizon_46KJ9W&ip=...&mac=F4:CF:A2:DA:17:20 173.76.212.141
    $parts = explode(',', $line, 3);
    if (count($parts) < 3) continue;

    $iso  = $parts[1];
    $rest = $parts[2];

    $qsParts     = explode(' ', $rest, 2);
    $queryString = $qsParts[0];

    parse_str($queryString, $fields);

    if (!isset($fields['ssid']) || $fields['ssid'] !== $routerSsid) continue;
    if (!isset($fields['mac'])  || !isset($fields['c'])) continue;

    $mac  = $fields['mac'];
    $temp = (float)$fields['c'];

    $ts = strtotime($iso);
    if ($ts === false) continue;

    $dataSensors[$mac][] = ['t' => $ts, 'c' => $temp];
}
fclose($fh);

// Trim to last N hours
$now    = time();
$cutoff = $now - $hoursBack * 3600;

foreach ($dataSensors as $mac => &$points) {
    usort($points, function($a, $b) {
        return $a['t'] <=> $b['t'];
    });
    $points = array_filter($points, function($p) use ($cutoff) {
        return $p['t'] >= $cutoff;
    });
    if (empty($points)) {
        unset($dataSensors[$mac]);
        continue;
    }
    foreach ($points as $p) {
        $allTimes[] = $p['t'];
        $allTemps[] = $p['c'];
    }
}
unset($points);

// Only keep up to 4 sensors
$dataSensors = array_slice($dataSensors, 0, 4, true);

// -----------------------------------------------------------------------------
// READ OPTIONAL BUZZARDS BAY WEATHER FILE
// -----------------------------------------------------------------------------
$buzzardsPoints = [];  // [ ['t'=>timestamp, 'c'=>temp], ... ]

if (file_exists($buzzardsFile)) {
    $fh2 = fopen($buzzardsFile, 'r');
    if ($fh2 !== false) {
        while (($line = fgets($fh2)) !== false) {
            $line = trim($line);
            if ($line === '') continue;

            // Expect: ISO,C
            $parts = explode(',', $line);
            if (count($parts) < 2) continue;

            $iso = trim($parts[0]);
            $c   = (float)trim($parts[1]);

            $ts = strtotime($iso);
            if ($ts === false) continue;
            if ($ts < $cutoff) continue;

            $buzzardsPoints[] = ['t' => $ts, 'c' => $c];
            $allTimes[] = $ts;
            $allTemps[] = $c;
        }
        fclose($fh2);
    }
}

// -----------------------------------------------------------------------------
// CHECK DATA AVAILABILITY
// -----------------------------------------------------------------------------
if (empty($dataSensors) && empty($buzzardsPoints)) {
    imagestring($img, 5, 10, 10, "No recent data for SSID $routerSsid or Buzzards Bay", $errorRed);
    imagepng($img);
    imagedestroy($img);
    exit;
}

if (empty($allTimes) || empty($allTemps)) {
    imagestring($img, 5, 10, 10, "No datapoints in last {$hoursBack}h", $errorRed);
    imagepng($img);
    imagedestroy($img);
    exit;
}

// -----------------------------------------------------------------------------
// AXIS RANGES
// -----------------------------------------------------------------------------
$minT = min($allTimes);
$maxT = max($allTimes);
if ($minT === $maxT) { $minT -= 60; $maxT += 60; }

$minC = min($allTemps);
$maxC = max($allTemps);
if ($minC === $maxC) { $minC -= 0.5; $maxC += 0.5; }

$tSpan = $maxT - $minT;
$cSpan = $maxC - $minC;
if ($cSpan <= 0) $cSpan = 1;

// Plot rectangle
$plotLeft   = $left;
$plotRight  = $width - $right;
$plotTop    = $top;
$plotBottom = $height - $bottom;

// Border
imagerectangle($img, $plotLeft, $plotTop, $plotRight, $plotBottom, $axisColor);

// -----------------------------------------------------------------------------
// GRID + LABELS
// -----------------------------------------------------------------------------
$yTicks = 6;
for ($i = 0; $i <= $yTicks; $i++) {
    $frac = $i / $yTicks;
    $valC = $minC + $frac * $cSpan;
    $y    = (int)($plotBottom - $frac * $plotH);

    imageline($img, $plotLeft, $y, $plotRight, $y, $gridColor);

    $label = sprintf('%.1f', $valC);
    imagestring($img, 3, 15, $y - 7, $label, $textColor);
}

// X grid + labels (yy/mm/dd HH:MM)
$xTicks = 6;
for ($i = 0; $i <= $xTicks; $i++) {
    $frac = $i / $xTicks;
    $tVal = (int)($minT + $frac * $tSpan);
    $x    = (int)($plotLeft + $frac * $plotW);

    imageline($img, $x, $plotTop, $x, $plotBottom, $gridColor);

    $label = date('y/m/d H:i', $tVal);
    $bboxY = $plotBottom + 8;
    imagestring($img, 3, $x - 60, $bboxY, $label, $textColor);
}

// Helper to map Celsius to Y
function y_for_c($c, $minC, $cSpan, $plotBottom, $plotH) {
    $frac = ($c - $minC) / $cSpan;
    return (int)($plotBottom - $frac * $plotH);
}

// +5 C line (solid)
$freezeC = 5.0;
if ($freezeC >= $minC && $freezeC <= $maxC) {
    $y = y_for_c($freezeC, $minC, $cSpan, $plotBottom, $plotH);
    imagesetthickness($img, 1);
    imageline($img, $plotLeft, $y, $plotRight, $y, $freezeLine);
    imagestring($img, 2, $plotLeft + 5, $y - 12, "+5 C", $freezeLine);
}

// 50 F (~10 C) dotted green line
$setC = 10.0;
if ($setC >= $minC && $setC <= $maxC) {
    $y = y_for_c($setC, $minC, $cSpan, $plotBottom, $plotH);

    $style = [];
    for ($i = 0; $i < 40; $i++) {
        $style[] = $setLine;
        $style[] = $setLine;
        $style[] = $bg;
        $style[] = $bg;
    }
    imagesetstyle($img, $style);
    imageline($img, $plotLeft, $y, $plotRight, $y, IMG_COLOR_STYLED);

    imagestring($img, 2, $plotLeft + 70, $y - 12, "50 F (10 C)", $setLine);
}

// -----------------------------------------------------------------------------
// TITLES & LEGEND
// -----------------------------------------------------------------------------
imagestring($img, 5, $left, 10, "Temperature Bourne Neck (TBN)", $textColor);
imagestring($img, 3, $left, 32, "SSID: $routerSsid  (last {$hoursBack}h)", $textColor);

$legendYTop    = 55;   // boxes row
$legendYOffset = 15;   // room label under box
$legendGapX    = 210;  // horizontal spacing

// Sensor legend entries (color + room + MAC suffix)
$macIndex = 0;
foreach ($dataSensors as $mac => $points) {
    if ($macIndex >= count($lineColors)) break;
    $color = $lineColors[$macIndex];
    $room  = isset($roomLabels[$macIndex]) ? $roomLabels[$macIndex] : ("ROOM".($macIndex+1));

    $legendX = $plotLeft + $macIndex * $legendGapX;
    $shortMac = substr($mac, -8);

    // Color box
    imagefilledrectangle($img, $legendX, $legendYTop, $legendX + 12, $legendYTop + 12, $color);
    imagerectangle($img, $legendX, $legendYTop, $legendX + 12, $legendYTop + 12, $axisColor);

    // Room label (under box)
    imagestring($img, 3, $legendX + 18, $legendYTop - 2, $room, $textColor);
    // MAC suffix under the room label
    imagestring($img, 2, $legendX + 18, $legendYTop + $legendYOffset, $shortMac, $textColor);

    $macIndex++;
}

// Buzzards Bay legend (if present)
if (!empty($buzzardsPoints)) {
    $legendX = $plotLeft + $macIndex * $legendGapX;

    imagefilledrectangle($img, $legendX, $legendYTop, $legendX + 12, $legendYTop + 12, $buzzColor);
    imagerectangle($img, $legendX, $legendYTop, $legendX + 12, $legendYTop + 12, $axisColor);

    imagestring($img, 3, $legendX + 18, $legendYTop - 2, "BUZZARDS", $buzzColor);
    imagestring($img, 2, $legendX + 18, $legendYTop + $legendYOffset, "Bay WX", $buzzColor);
}

// -----------------------------------------------------------------------------
// PLOT TRACES
// -----------------------------------------------------------------------------
imagesetthickness($img, 2);  // thicker lines

// House sensors
$macIndex = 0;
foreach ($dataSensors as $mac => $points) {
    if ($macIndex >= count($lineColors)) break;
    $color = $lineColors[$macIndex];

    $prevX = null;
    $prevY = null;
    $lastPoint = null;

    foreach ($points as $p) {
        $t = $p['t'];
        $c = $p['c'];

        $xFrac = ($t - $minT) / $tSpan;
        $yFrac = ($c - $minC) / $cSpan;

        $x = (int)($plotLeft + $xFrac * $plotW);
        $y = (int)($plotBottom - $yFrac * $plotH);

        if ($prevX !== null) {
            imageline($img, $prevX, $prevY, $x, $y, $color);
        }

        // Dot
        imagefilledellipse($img, $x, $y, 5, 5, $color);
        // X marker
        imageline($img, $x - 3, $y - 3, $x + 3, $y + 3, $axisColor);
        imageline($img, $x - 3, $y + 3, $x + 3, $y - 3, $axisColor);

        $prevX = $x;
        $prevY = $y;
        $lastPoint = ['x' => $x, 'y' => $y, 'c' => $c, 't' => $t];
    }

    if ($lastPoint !== null) {
        $annot = sprintf('%.1f C @ %s', $lastPoint['c'], date('H:i', $lastPoint['t']));
        $ax = $lastPoint['x'] + 5;
        $ay = $lastPoint['y'] - 14;
        imagestring($img, 2, $ax, $ay, $annot, $textColor);
    }

    $macIndex++;
}

// Buzzards Bay trace (black) if present
if (!empty($buzzardsPoints)) {
    usort($buzzardsPoints, function($a, $b) {
        return $a['t'] <=> $b['t'];
    });

    $prevX = null;
    $prevY = null;
    $lastPoint = null;

    foreach ($buzzardsPoints as $p) {
        $t = $p['t'];
        $c = $p['c'];

        $xFrac = ($t - $minT) / $tSpan;
        $yFrac = ($c - $minC) / $cSpan;

        $x = (int)($plotLeft + $xFrac * $plotW);
        $y = (int)($plotBottom - $yFrac * $plotH);

        if ($prevX !== null) {
            imageline($img, $prevX, $prevY, $x, $y, $buzzColor);
        }

        // small dot (no X to keep it distinct)
        imagefilledellipse($img, $x, $y, 4, 4, $buzzColor);

        $prevX = $x;
        $prevY = $y;
        $lastPoint = ['x' => $x, 'y' => $y, 'c' => $c, 't' => $t];
    }

    if ($lastPoint !== null) {
        $annot = sprintf('%.1f C @ %s', $lastPoint['c'], date('H:i', $lastPoint['t']));
        $ax = $lastPoint['x'] + 5;
        $ay = $lastPoint['y'] - 14;
        imagestring($img, 2, $ax, $ay, $annot, $buzzColor);
    }
}

// -----------------------------------------------------------------------------
// OUTPUT
// -----------------------------------------------------------------------------
imagepng($img);
imagedestroy($img);
