<?php
/**
 * Lucas IT Services - IPv4 Subnet Calculator
 * Path: /var/www/html/tools/scalc/index.php
 */

declare(strict_types=1);

session_start();

// Read theme preference directly from cookie for SSR consistency
$current_theme = $_COOKIE['theme'] ?? 'dark';
if (!in_array($current_theme, ['dark', 'light'], true)) {
    $current_theme = 'dark';
}

// Generate CSRF token if not present
if (empty($_SESSION['csrf_token'])) {
    $_SESSION['csrf_token'] = bin2hex(random_bytes(32));
}

$input_cidr = isset($_POST['cidr']) ? trim((string)$_POST['cidr']) : '';
$results = null;
$error_msg = null;

/**
 * Calculates IPv4 Subnet Metrics
 */
function calculate_ipv4_subnet(string $input): array {
    // Length boundary safety check
    if (strlen($input) < 1 || strlen($input) > 64) {
        return ['error' => 'Input string length must be between 1 and 64 characters.'];
    }

    // Handle standard IP/CIDR or IP Subnet Mask notation
    $clean_input = preg_replace('/\s+/', ' ', $input);
    $ip_part = '';
    $mask_part = '';

    if (strpos($clean_input, '/') !== false) {
        $parts = explode('/', $clean_input, 2);
        $ip_part = trim($parts[0]);
        $mask_part = trim($parts[1]);
    } elseif (strpos($clean_input, ' ') !== false) {
        $parts = explode(' ', $clean_input, 2);
        $ip_part = trim($parts[0]);
        $mask_part = trim($parts[1]);
    } else {
        $ip_part = $clean_input;
        $mask_part = '32';
    }

    // Validate IP
    if (!filter_var($ip_part, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4)) {
        return ['error' => 'Invalid IPv4 address provided.'];
    }

    // Parse Subnet Mask / CIDR Prefix
    $cidr = 32;
    if (filter_var($mask_part, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4)) {
        $long_mask = ip2long($mask_part);
        $base2 = sprintf('%032b', $long_mask);
        // Verify contiguous mask bits
        if (preg_match('/^1*0*$/', $base2)) {
            $cidr = substr_count($base2, '1');
        } else {
            return ['error' => 'Invalid dotted-decimal netmask provided.'];
        }
    } elseif (ctype_digit($mask_part)) {
        $cidr = (int)$mask_part;
        if ($cidr < 0 || $cidr > 32) {
            return ['error' => 'CIDR prefix must be an integer between 0 and 32.'];
        }
    } else {
        return ['error' => 'Invalid netmask or CIDR notation.'];
    }

    // Bitwise Network Math
    $ip_long = ip2long($ip_part);
    $mask_long = $cidr === 0 ? 0 : (~0 << (32 - $cidr)) & 0xFFFFFFFF;
    $wildcard_long = ~$mask_long & 0xFFFFFFFF;
    
    $net_long = $ip_long & $mask_long;
    $bcast_long = $ip_long | $wildcard_long;

    // Total & Usable Hosts
    if ($cidr === 32) {
        $total_hosts = 1;
        $usable_hosts = 1;
        $first_ip_long = $ip_long;
        $last_ip_long = $ip_long;
    } elseif ($cidr === 31) {
        // RFC 3021 Point-to-Point Links
        $total_hosts = 2;
        $usable_hosts = 2;
        $first_ip_long = $net_long;
        $last_ip_long = $bcast_long;
    } else {
        $total_hosts = pow(2, (32 - $cidr));
        $usable_hosts = $total_hosts - 2;
        $first_ip_long = $net_long + 1;
        $last_ip_long = $bcast_long - 1;
    }

    // Determine Class
    $first_octet = (int)explode('.', $ip_part)[0];
    $ip_class = 'Unknown';
    if ($first_octet >= 1 && $first_octet <= 126) $ip_class = 'Class A';
    elseif ($first_octet === 127) $ip_class = 'Class A (Loopback)';
    elseif ($first_octet >= 128 && $first_octet <= 191) $ip_class = 'Class B';
    elseif ($first_octet >= 192 && $first_octet <= 223) $ip_class = 'Class C';
    elseif ($first_octet >= 224 && $first_octet <= 239) $ip_class = 'Class D (Multicast)';
    elseif ($first_octet >= 240 && $first_octet <= 255) $ip_class = 'Class E (Experimental)';

    return [
        'ip' => $ip_part,
        'cidr' => $cidr,
        'canonical_cidr' => $ip_part . '/' . $cidr,
        'network' => long2ip($net_long),
        'broadcast' => long2ip($bcast_long),
        'netmask' => long2ip($mask_long),
        'wildcard' => long2ip($wildcard_long),
        'first_ip' => long2ip($first_ip_long),
        'last_ip' => long2ip($last_ip_long),
        'usable_range' => long2ip($first_ip_long) . ' - ' . long2ip($last_ip_long),
        'total_hosts' => number_format($total_hosts),
        'usable_hosts' => number_format($usable_hosts),
        'ip_class' => $ip_class,
        'binary_netmask' => implode('.', str_split(sprintf('%032b', $mask_long), 8))
    ];
}

if ($_SERVER['REQUEST_METHOD'] === 'POST') {
    $posted_token = $_POST['csrf_token'] ?? '';
    if (!hash_equals($_SESSION['csrf_token'], $posted_token)) {
        $error_msg = "Invalid session token. Please refresh the page and try again.";
    } elseif ($input_cidr !== '') {
        $res = calculate_ipv4_subnet($input_cidr);
        if (isset($res['error'])) {
            $error_msg = $res['error'];
        } else {
            $results = $res;
        }
    }
}
?>
<!DOCTYPE html>
<html lang="en" data-theme="<?= htmlspecialchars($current_theme) ?>">
<head>
    <title>Lucas IT Services - Subnet Calculator</title>
    <link rel="icon" type="image/svg+xml" href="data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 50 50'><path fill='%230078d4' fill-rule='evenodd' d='M 25 0 A 24.999868 25.000025 0 0 0 0 25 A 24.999868 25.000025 0 0 0 25 50 A 24.999868 25.000025 0 0 0 50 25 A 24.999868 25.000025 0 0 0 25 0 z M 25 6.9960938 A 18.000229 18.000229 0 0 1 43 24.996094 A 18.000229 18.000229 0 0 1 25 42.996094 A 18.000229 18.000229 0 0 1 7 24.996094 A 18.000229 18.000229 0 0 1 25 6.9960938 z M 21.496094 12 A 0.5 0.5 0 0 0 20.996094 12.5 L 20.996094 25.701172 A 0.5 0.5 0 0 1 20.496094 26.201172 L 11.998047 26.201172 A 0.18766001 0.18766001 0 0 0 11.875 26.529297 L 24.623047 37.669922 A 0.57222682 0.57222682 0 0 0 25.376953 37.669922 L 38.123047 26.529297 A 0.18768046 0.18768046 0 0 0 38 26.201172 L 29.5 26.201172 A 0.5 0.5 0 0 1 29 25.701172 L 29 12.5 A 0.5 0.5 0 0 0 28.5 12 L 21.496094 12 z'/></svg>">
    <link href="../css/style.css" rel="stylesheet" type="text/css">
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <meta name="google" content="notranslate" />
    <meta http-equiv="Content-Language" content="en_US" />
    <style>
        body {
            display: flex;
            flex-direction: column;
            align-items: center;
        }
        .header-bar {
            display: flex;
            justify-content: space-between;
            align-items: flex-start;
            width: 100%;
            max-width: 1050px;
            box-sizing: border-box;
            position: relative;
            z-index: 10;
        }
        .page-header {
            display: flex;
            align-items: center;
            gap: 20px;
            margin-bottom: 20px;
        }
        .page-header svg {
            width: 64px;
            height: 64px;
            flex-shrink: 0;
        }
        .page-title {
            margin: 0;
            font-size: 2.25rem;
            font-weight: 700;
        }
        .content-wrapper {
            width: 100%;
            max-width: 1050px;
            box-sizing: border-box;
            position: relative;
            z-index: 1;
        }
        .intro-text {
            color: var(--text-muted);
            margin-top: 0;
            margin-bottom: 25px;
            line-height: 1.5;
        }
        .control-row {
            display: flex;
            flex-wrap: wrap;
            align-items: center;
            gap: 10px;
            margin-bottom: 25px;
        }
        .section-title {
            margin: 30px 0 12px 0;
            font-size: 1.25rem;
            font-weight: 600;
            color: var(--text-main);
        }
        [data-theme="dark"] .section-title {
            color: #fff;
        }
        
        .results-panel {
            background-color: var(--bg-panel);
            border: 1px solid var(--border-color);
            border-radius: 8px;
            padding: 24px;
            box-shadow: 0 4px 6px rgba(0,0,0,0.2);
            box-sizing: border-box;
            margin-bottom: 25px;
        }
        .kv-grid {
            display: grid;
            grid-template-columns: 200px 1fr auto;
            gap: 12px;
            align-items: center;
            font-size: 14px;
            background: rgba(0,0,0,0.1);
            padding: 15px;
            border-radius: 6px;
            border: 1px solid var(--border-color);
        }
        [data-theme="light"] .kv-grid {
            background: rgba(0,0,0,0.02);
        }
        .kv-grid label {
            font-weight: 600;
            color: var(--text-muted);
        }
        .kv-grid .value-display {
            font-family: monospace;
            color: var(--text-main);
            font-size: 15px;
            overflow: hidden;
            text-overflow: ellipsis;
            white-space: nowrap;
        }
        .kv-grid button {
            padding: 2px 10px;
            font-size: 0.75rem;
            height: 26px;
        }
        
        .footer-links {
            margin-top: 40px;
            font-size: 14px;
            border-top: 1px solid var(--border-color);
            padding-top: 20px;
        }
        .footer-links a {
            color: var(--accent);
            text-decoration: none;
        }
        .footer-links a:hover {
            text-decoration: underline;
        }
        .redboxed {
            background-color: rgba(232, 17, 35, 0.1);
            border: 1px solid #e81123;
            color: #f88;
            padding: 12px 16px;
            border-radius: 6px;
            margin-bottom: 20px;
            font-size: 14px;
        }
        [data-theme="light"] .redboxed {
            color: #a00;
        }

        @media (max-width: 768px) {
            .header-bar {
                flex-direction: column;
                gap: 15px;
            }
            .control-row {
                flex-direction: column;
                align-items: stretch;
            }
            .control-row > * {
                width: 100% !important;
            }
            .kv-grid {
                grid-template-columns: 1fr;
                gap: 6px;
            }
            .kv-grid button {
                width: 100%;
            }
        }
    </style>
</head>
<body>
    <div class="header-bar">
        <div class="page-header">
            <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 50 50" width="100%" height="100%">
                <path fill="var(--accent)" fill-rule="evenodd" d="M 25 0 A 24.999868 25.000025 0 0 0 0 25 A 24.999868 25.000025 0 0 0 25 50 A 24.999868 25.000025 0 0 0 50 25 A 24.999868 25.000025 0 0 0 25 0 z M 25 6.9960938 A 18.000229 18.000229 0 0 1 43 24.996094 A 18.000229 18.000229 0 0 1 25 42.996094 A 18.000229 18.000229 0 0 1 7 24.996094 A 18.000229 18.000229 0 0 1 25 6.9960938 z M 21.496094 12 A 0.5 0.5 0 0 0 20.996094 12.5 L 20.996094 25.701172 A 0.5 0.5 0 0 1 20.496094 26.201172 L 11.998047 26.201172 A 0.18766001 0.18766001 0 0 0 11.875 26.529297 L 24.623047 37.669922 A 0.57222682 0.57222682 0 0 0 25.376953 37.669922 L 38.123047 26.529297 A 0.18768046 0.18768046 0 0 0 38 26.201172 L 29.5 26.201172 A 0.5 0.5 0 0 1 29 25.701172 L 29 12.5 A 0.5 0.5 0 0 0 28.5 12 L 21.496094 12 z" />
            </svg>
            <h1 class="page-title">Subnet Calculator</h1>
        </div>
        <div style="margin-top: 15px;">
            <button class="button" id="theme-toggle" type="button">Toggle Theme</button>
        </div>
    </div>

    <div class="content-wrapper">
        <p class="intro-text">Enter an IPv4 address with CIDR notation (e.g., <code>10.0.0.1/24</code>) or standard netmask (e.g., <code>192.168.1.50 255.255.255.0</code>) to calculate boundaries, wildcard masks, and host capacities.</p>

        <form method="POST" action="index.php">
            <input type="hidden" name="csrf_token" value="<?= htmlspecialchars($_SESSION['csrf_token']) ?>">
            <div class="control-row">
                <span style="font-weight: 600; color: var(--text-main); font-size: 14px;">IP / CIDR Block:</span>
                <input type="text" name="cidr" class="input-field" style="width: 320px;" placeholder="e.g. 192.168.1.100/24" value="<?= htmlspecialchars($input_cidr) ?>" autocomplete="off" autofocus>
                <input type="submit" value="Calculate" class="button">
                <a href="./" class="button">Reset</a>
            </div>
        </form>

        <?php if ($error_msg): ?>
            <div class="redboxed"><?= htmlspecialchars($error_msg) ?></div>
        <?php endif; ?>

        <?php if ($results): ?>
            <div class="results-panel">
                <h3 class="section-title" style="margin-top: 0;">Calculated Subnet Profile:</h3>
                <div class="kv-grid">
                    <label>Network Address</label>
                    <div class="value-display"><?= htmlspecialchars($results['network']) ?>/<?= $results['cidr'] ?></div>
                    <button type="button" class="button" data-copy="<?= htmlspecialchars($results['network']) ?>">Copy</button>

                    <label>Subnet Mask</label>
                    <div class="value-display"><?= htmlspecialchars($results['netmask']) ?></div>
                    <button type="button" class="button" data-copy="<?= htmlspecialchars($results['netmask']) ?>">Copy</button>

                    <label>Wildcard Mask</label>
                    <div class="value-display"><?= htmlspecialchars($results['wildcard']) ?></div>
                    <button type="button" class="button" data-copy="<?= htmlspecialchars($results['wildcard']) ?>">Copy</button>

                    <label>Usable Host Range</label>
                    <div class="value-display" style="font-weight: 600; color: var(--accent);"><?= htmlspecialchars($results['usable_range']) ?></div>
                    <button type="button" class="button" data-copy="<?= htmlspecialchars($results['usable_range']) ?>">Copy</button>

                    <label>Broadcast Address</label>
                    <div class="value-display"><?= htmlspecialchars($results['broadcast']) ?></div>
                    <button type="button" class="button" data-copy="<?= htmlspecialchars($results['broadcast']) ?>">Copy</button>

                    <label>Usable Hosts</label>
                    <div class="value-display"><?= htmlspecialchars($results['usable_hosts']) ?> (Total: <?= htmlspecialchars($results['total_hosts']) ?>)</div>
                    <div></div>

                    <label>Network Class</label>
                    <div class="value-display"><?= htmlspecialchars($results['ip_class']) ?></div>
                    <div></div>

                    <label>Binary Subnet Mask</label>
                    <div class="value-display" style="font-size: 13px;"><?= htmlspecialchars($results['binary_netmask']) ?></div>
                    <div></div>
                </div>
            </div>
        <?php endif; ?>

        <p class="footer-links">
            <a href="index.phps">View Source</a> | <a href="../">Back to Tools</a>
        </p>
    </div>

    <script>
        // Clipboard copy functionality
        document.addEventListener('click', function(e) {
            const btn = e.target.closest('button[data-copy]');
            if (!btn) return;
            const val = btn.getAttribute('data-copy');
            if (!val) return;

            navigator.clipboard.writeText(val.trim()).then(() => {
                const orig = btn.textContent;
                btn.textContent = 'Copied';
                btn.disabled = true;
                setTimeout(() => { btn.textContent = orig; btn.disabled = false; }, 1200);
            });
        });

        // Theme Switcher Engine
        (function () {
            function setCookie(name, value, days = 365) {
                const d = new Date();
                d.setTime(d.getTime() + (days * 24 * 60 * 60 * 1000));
                document.cookie = name + "=" + value + ";path=/;expires=" + d.toUTCString() + ";SameSite=Lax";
            }

            function getCookie(name) {
                const nameEQ = name + "=";
                const ca = document.cookie.split(';');
                for(let i = 0; i < ca.length; i++) {
                    let c = ca[i].trim();
                    if (c.indexOf(nameEQ) === 0) return c.substring(nameEQ.length, c.length);
                }
                return null;
            }

            let savedTheme = getCookie("theme");
            if (!savedTheme) {
                savedTheme = window.matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light";
            }
            document.documentElement.setAttribute("data-theme", savedTheme);

            document.addEventListener("click", function (e) {
                if (e.target && e.target.id === "theme-toggle") {
                    e.preventDefault();
                    const currentTheme = document.documentElement.getAttribute("data-theme");
                    const newTheme = currentTheme === "dark" ? "light" : "dark";

                    document.documentElement.setAttribute("data-theme", newTheme);
                    setCookie("theme", newTheme);
                }
            });
        })();
    </script>
</body>
</html>