// ==========================================
// 3. IDENTIFY STATUS (Live vs Reached vs Cancelled vs Diverted vs Not Started)
// ==========================================
$isReached = (isset($data['status_code']) && $data['status_code'] == 'reached') || (isset($data['isArrDSTN']) && $data['isArrDSTN'] == true);
$isCancelled = !$isReached && (!empty($data['ISCANCEL']) && (int)$data['ISCANCEL'] === 1);
// $isDiverted can be true even when $isReached is true (diverted train that arrived at new terminus)
$isDiverted = !$isCancelled && (!empty($data['ISDIVERT']) && (int)$data['ISDIVERT'] === 1);
// Diverted trains: API never sets status_code='reached'. Detect completion via last-station arrival flag or stale journey (>30h old).
if (!$isReached && $isDiverted) {
if (!empty($data['STNS'])) {
$lastStn = end($data['STNS']);
if (!empty($lastStn['ISA'])) $isReached = true;
}
if (!$isReached) {
$_journeyDt = DateTime::createFromFormat('d-m-Y', $apiDate);
if ($_journeyDt && (time() - $_journeyDt->getTimestamp()) > 30 * 3600) $isReached = true;
}
}
$isNotStarted = !$isReached && !$isCancelled && !$isDiverted && ((isset($data['status_code']) && $data['status_code'] == 'not_started') || (isset($data['TRUNST']) && (int)$data['TRUNST'] === 0));
// ==========================================
// 4. HELPER FUNCTIONS
// ==========================================
// Converts minutes to readable text
function formatMinsToDelay($mins) {
$m = (int)$mins;
if ($m <= 0) return "RT";
$h = floor($m / 60);
$rem = $m % 60;
$res = [];
if ($h > 0) $res[] = $h . ($h > 1 ? " hrs" : " hr");
if ($rem > 0) $res[] = $rem . " mins";
return implode(" ", $res);
}
// Handles string delays like "02:11"
function formatDelayStr($timeStr) {
if (empty($timeStr) || strtolower($timeStr) == "on time") return "RT";
$parts = explode(':', $timeStr);
if (count($parts) == 2) {
return formatMinsToDelay(((int)$parts[0] * 60) + (int)$parts[1]);
}
return $timeStr;
}
// Handles both H:i (Live) and H:i:s (History) -> AM/PM
function formatTimeAMPM($timeStr) {
if (empty($timeStr) || strtolower($timeStr) == 'source' || strtolower($timeStr) == 'destination' || $timeStr == '00:00:00' || $timeStr == '--') return '--';
$timeObj = DateTime::createFromFormat('H:i:s', $timeStr);
if (!$timeObj && preg_match('/([0-9]{2}):([0-9]{2})/', $timeStr, $m)) {
$timeObj = DateTime::createFromFormat('H:i', $m[1].':'.$m[2]);
}
if ($timeObj) {
$formatted = $timeObj->format('h:i A');
return preg_replace('/[0-9]{2}:[0-9]{2}(:[0-9]{2})?/', $formatted, $timeStr, 1);
}
return $timeStr;
}
// Intelligent Unix Timestamp Tracker for Speed Math
function getUnixSequential($timeStr, &$lastTimeRaw, &$globalDayOffset) {
if (empty($timeStr) || strtolower($timeStr) == 'source' || strtolower($timeStr) == 'destination' || $timeStr == '--' || $timeStr == '00:00:00') return null;
preg_match('/(\d{1,2}:\d{2})(:\d{2})?/', $timeStr, $m);
if (!$m) return null;
$t = strtotime($m[1]);
if ($lastTimeRaw !== -1 && $t < ($lastTimeRaw - 21600)) {
$globalDayOffset += 86400;
}
$lastTimeRaw = $t;
return $t + $globalDayOffset;
}
// ==========================================
// 5. EXTRACT SAFE VARIABLES
// ==========================================
$trainName = isset($data['train_name']) ? $data['train_name'] : (isset($data['trainName']) ? $data['trainName'] : 'N/A');
$stoppages = $data['STNS'];
$sourceName = isset($data['SRCN']) ? $data['SRCN'] : $stoppages[0]['SN'];
$destName = isset($data['DSTNN']) ? $data['DSTNN'] : end($stoppages)['SN'];
$journeyDate = isset($data['STD']) ? $data['STD'] : $apiDate;
$alertMsg = isset($data['AlertMsg']) ? $data['AlertMsg'] : '';
$totalDistance = isset($data['TTLDIST']) ? floatval($data['TTLDIST']) : floatval(end($stoppages)['DIST']);
// Distance Covered Logic
$coveredDistance = 0;
$lastActiveIndex = -1;
foreach ($stoppages as $i => $s) {
if (isset($s['ISD']) && ($s['ISD'] || $s['ISA'])) {
$lastActiveIndex = $i;
$coveredDistance = isset($s['DIST']) ? floatval($s['DIST']) : $coveredDistance;
}
}
$progressPercent = ($totalDistance > 0) ? min(100, round(($coveredDistance / $totalDistance) * 100)) : 0;
// Extract diversion info whenever ISDIVERT=1 (runs even if train is also reached)
$divertFrom = '';
$divertTo = '';
$divertMsg = '';
if ($isDiverted) {
$excpRaw = isset($data['EXCP']) ? str_replace(['[', ']'], ['', ' |'], $data['EXCP']) : '';
$excpRaw = trim(rtrim(trim($excpRaw), '|'));
$divertMsg = !empty($excpRaw) ? $excpRaw : trim($data['AlertMsg'] ?? '');
$divertFrom = isset($data['DFROM']) ? trim($data['DFROM']) : '';
$divertTo = isset($data['DTO']) ? trim($data['DTO']) : '';
}
if ($isReached) {
$lastUpdateText = !empty($data['CPOS']) ? $data['CPOS'] : (!empty($data['LUPDFULL']) ? $data['LUPDFULL'] : "Journey Completed");
$finalLateMins = isset($data['totalLateMins']) ? (int)$data['totalLateMins'] : (isset($data['LDEL']) ? (int)$data['LDEL'] : 0);
$delayFormatted = formatMinsToDelay($finalLateMins);
$statusColor = ($finalLateMins > 0) ? "danger" : "success";
} elseif ($isCancelled) {
$cancelMsg = trim($data['CPOS'] ?? ($data['EXCP'] ? trim($data['EXCP'], '[]') : ''));
$lastUpdateText = $cancelMsg ?: 'This train has been cancelled';
$delayFormatted = 'Cancelled';
$statusColor = 'danger';
} elseif ($isDiverted) {
$lastUpdateText = $divertMsg ?: 'This train has been diverted from its original route';
$delayMins = isset($data['LDEL']) ? (int)$data['LDEL'] : 0;
$delayFormatted = formatMinsToDelay($delayMins);
$statusColor = ($delayMins > 0) ? "danger" : "success";
} elseif ($isNotStarted) {
$schDep0 = isset($stoppages[0]['STD']) ? $stoppages[0]['STD'] : '';
$lastUpdateText = $schDep0 ? "Scheduled to depart at " . $schDep0 : (isset($data['LUPDFULL']) ? $data['LUPDFULL'] : "Yet to start from source");
$delayFormatted = 'Scheduled';
$statusColor = 'secondary';
} else {
$lastUpdateText = isset($data['LUPDFULL']) ? $data['LUPDFULL'] : "Live Status Available";
$delayMins = isset($data['LDEL']) ? (int)$data['LDEL'] : 0;
$delayFormatted = formatMinsToDelay($delayMins);
$statusColor = ($delayMins > 0) ? "danger" : "success";
}
$coachString = isset($data['STNS'][0]['departureCoachPosition']) ? $data['STNS'][0]['departureCoachPosition'] : '';
$coaches = $coachString ? explode('-', $coachString) : [];
// Date navigation — Yesterday / Today / Tomorrow
$_dateObj = DateTime::createFromFormat('d-m-Y', $apiDate) ?: new DateTime();
$_dateYest = (clone $_dateObj)->modify('-1 day')->format('Ymd');
$_dateCurr = $_dateObj->format('Ymd');
$_dateTomo = (clone $_dateObj)->modify('+1 day')->format('Ymd');
$_todayYmd = date('Ymd');
$_dateLabel = ($_dateCurr === $_todayYmd) ? 'Today' : $_dateObj->format('d M Y');
// Big delay — current delay in minutes
$_currentDelayMins = 0;
if (!$isReached && !$isCancelled && !$isNotStarted) {
$_currentDelayMins = isset($data['LDEL']) ? (int)$data['LDEL'] : 0;
} elseif ($isReached) {
$_currentDelayMins = isset($data['totalLateMins']) ? (int)$data['totalLateMins'] : (isset($data['LDEL']) ? (int)$data['LDEL'] : 0);
}
// WhatsApp share URL
$_shareUrl = 'https://runningstatus.in/status/' . urlencode($trainNo) . '-on-' . $_dateCurr;
$_shareText = $trainName . ' (' . $trainNo . ') - ' . $delayFormatted . ' - ' . $_dateLabel . ' | ' . $_shareUrl;
$_waUrl = 'https://wa.me/?text=' . rawurlencode($_shareText);
?>
12054 -
Running
Refreshing in 180s
Late