<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>NIO Swap Data – Daily and Weekly Battery Swap Counts in Real Time</title>
	<script src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/4.0.0/crypto-js.min.js"></script>
    <meta name="description" content="Access real-time NIO swap data with daily and weekly battery swap counts. Track NIO battery swaps, latest updates, and trends with easy-to-read data.">
    <meta name="keywords" content="NIO swap data, battery swaps, NIO daily swap count, NIO swap trends">
    <style>
	
        body {
            font-family: Arial, sans-serif;
            background-color: #f4f4f4;
            color: #333;
            padding: 20px;
        }
        h1 {
            color: #444;
        }
		
		/* Code Block */
		
			pre {
				counter-reset: listing;
				background-color: #fff;
				padding: 10px;
				border-radius: 8px;
				box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
			}
			pre code span::before {
				content: counter(listing) ". ";
				counter-increment: listing;
				display: inline-block;
				width: 3em;
				margin-right: 5px;
				text-align: right;
				color: #888;
			}
			pre code span {
				display: block;
			}
			#data-container {
				display: none; /* Initially hide the data */
			}
			
		/* Author / Last Updated */
		
			#last-updated {
				font-style: italic;
				color: #999; /* Light gray for the last updated text */
				margin-bottom: 0px; /* Add space below the last updated text */
			}
			
		/* Containers in a Flex Box --> */
		
			.grid-container {
				display: grid;
				margin-bottom: 0px;
				grid-template-columns: repeat(auto-fit, minmax(335px, 1fr));
				gap: 20px; /* Space between the containers */
				padding: 0px;
			}
			
			/* Main Data At Top */
			
    			#data-indicators {
    				margin-bottom: 10px;
    				padding: 10px;
    				border-radius: 8px;
    				background-color: #fff;
    				box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
    			}
    			#data-indicators p {
    				font-size: 1.2em;
    				margin-top: 10px;
    				margin-bottom: 10px;
    				color: #555;
    			}
    			#data-indicators ul {
    				list-style-type: none;
    				padding: 0px;
    				margin-left: 15px;
    			}
    			#data-indicators li {
    				font-size: 1.1em;
    				color: #666;
    				margin-bottom: 0px;
    			}
			
			#swaps-container {
				margin-bottom: 0px;
				max-height: 200px; /* Adjust height as needed */
				overflow-y: auto; /* Enable vertical scrolling */
				padding-right: 0px; /* Optional: add padding to avoid scrollbar overlap */
				border: 1px solid #ddd; /* Optional: add a border for better visibility */
				border-radius: 5px;
				background-color: #fff; /* Match the background of the data indicators */
				box-shadow: 0 0 5px rgba(0, 0, 0, 0.1); /* Optional shadow */
				font-family: "Courier New", Courier, monospace; /* Monospaced font for uniform character width */
				font-size: 12.5px; /* Set font size to 8 pixels */
			}
			
			#mill-container {
				margin-bottom: 0px;
				max-height: 200px; /* Adjust height as needed */
				overflow-y: auto; /* Enable vertical scrolling */
				padding-right: 0px; /* Optional: add padding to avoid scrollbar overlap */
				border: 1px solid #ddd; /* Optional: add a border for better visibility */
				border-radius: 5px;
				background-color: #fff; /* Match the background of the data indicators */
				box-shadow: 0 0 5px rgba(0, 0, 0, 0.1); /* Optional shadow */
				font-family: "Courier New", Courier, monospace; /* Monospaced font for uniform character width */
				font-size: 12.5px; /* Set font size to 8 pixels */
			}
			
		/* Donation Section */
		
			#donation-section {
				margin: 20px 0;
				padding: 10px;
				padding-top: 5px;
				border-radius: 8px;
				background-color: #fff;
				box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
				text-align: center;
			}
			#donation-section p {
				font-size: 1.2em;
				color: #555;
			}
			#paypal-logo {
				width: 100px; /* Adjust width as needed */
			}
		
		/* Download Raw Data */
		
			#toggle-data-btn, #download-btn, #donation-button {
				padding: 10px 20px;
				margin: 5px;
				border: none;
				border-radius: 5px;
				background-color: #007bff;
				color: white;
				cursor: pointer;
				font-size: 1em;
			}
			#toggle-data-btn:hover, #download-btn:hover, #donation-button:hover {
				background-color: #0056b3;
			}
    </style>
	
</head>
<body>
    <h1>NIO Swap Data Insights – Real-Time and Weekly Battery Swaps</h1>
    <div id="last-updated">
        Last updated: <span id="mins-ago">Loading...</span>
        <br>
        China - Local Time: <span id="china-local-time">Loading...</span>
        <span id="full-timestamp">Loading...</span><br>
		Creator: @<a href="https://stocktwits.com/ItsMeeeeee" target="_blank" rel="noopener noreferrer" style="color: inherit; text-decoration: underline;">ItsMeeeeee : Stocktwits.com</a><br><br>
    </div>
	
	<!-- Containers : Flex Grid -->
	<div class="grid-container">
	
		<!-- Main Data at Top -->
		<div class="container" id="data-indicators">
			<p>Total: <b><span id="total-swaps">Loading...</span></b></p>
			<p>Swaps Today: <b><span id="swaps-today">Loading...</span></b></p>
			<p>Current Rate: <b><span id="hourly-rate">Loading...</span> / hour</b></p>
		</div>
		<!-- Daily NIO Battery Swaps -->
		<div class="container" id="data-indicators">
			<p>Daily NIO Battery Swaps:</p>
			<div id="swaps-container">
				<ul id="swaps-daily">
					<li>Loading...</li>
				</ul>
			</div>
			<br>
			<button id="compare-day-btn">Compare Day</button>
		</div>
		<!-- Time to Each Million Swaps -->
		<div class="container" id="data-indicators">
			<p>Time to Each Million Swaps:</p>
			<div id="mill-container">
				<ul id="mill-swaps">
					<li>Loading...</li>
				</ul>
			</div>
		</div>
	</div>
    
    <!-- Popup menu for day selection -->
    
        <div id="day-selector-popup" style="display: none; position: fixed; top: 50%; left: 50%; transform: translate(-50%, -50%); z-index: 1000; background-color: white; padding: 20px; box-shadow: 0 5px 15px rgba(0, 0, 0, 0.3); border-radius: 10px; text-align: left;">
            <button id="close-day-selector" style="position: absolute; top: 5px; right: 5px; background-color: transparent; border: none; font-size: 16px; font-weight: bold; color: #999; cursor: pointer;">&times;</button>
        <p>Select a day to compare:</p>
        <form id="day-selector-form">
            <label><input type="radio" name="day" value="Mon"> Monday</label><br>
            <label><input type="radio" name="day" value="Tue"> Tuesday</label><br>
            <label><input type="radio" name="day" value="Wed"> Wednesday</label><br>
            <label><input type="radio" name="day" value="Thu"> Thursday</label><br>
            <label><input type="radio" name="day" value="Fri"> Friday</label><br>
            <label><input type="radio" name="day" value="Sat"> Saturday</label><br>
            <label><input type="radio" name="day" value="Sun"> Sunday</label>
        </form>
        </div>
    <!-- Tooltip bar -->
        <div id="tooltip-bar" style="display: none; position: fixed; bottom: 10px; left: 50%; transform: translateX(-50%); background-color: #007bff; color: white; padding: 10px 20px; border-radius: 5px; box-shadow: 0 3px 10px rgba(0, 0, 0, 0.2); z-index: 1000; font-size: 1em; text-align: center;">
            Copied to clipboard!
        </div>
    <p>Stay updated with the latest NIO swap data, including battery swap counts for each day. This data provides insights into NIO's battery swap technology, allowing users to access charged batteries seamlessly.</p>
    <!-- <button id="toggle-data-btn">Show Raw Data</button> -->
    <!-- <button id="download-btn">Download NIO Swap Data</button><br> -->
    	
    <div id="donation-section">
        <p>If you appreciate this data and would like to support my work, consider making a donation!</p>
        <button id="donation-button" onclick="window.open('https://ko-fi.com/itsmeeeeee', '_blank');">
            <img id="paypal-logo" src="https://www.paypalobjects.com/en_US/i/btn/btn_donate_LG.gif" alt="PayPal Donation">
        </button>
    </div>
    	
    <pre id="data-container">      Date ; Time ; Swaps ; Added<br><br><code></code></pre>
    <script>
        async function fetchData() {
            try {
				// Fetch count.txt file & Decrypt
				
					const response = await fetch('/serve_count.php');
					if (!response.ok) {
						throw new Error(`HTTP error! status: ${response.status}`);
					}

					const encryptedData = await response.text();
					//console.log("Encrypted Data: ", encryptedData);

					// Base64 decode the received data
					const decodedData = CryptoJS.enc.Base64.parse(encryptedData);

					// Extract the IV (first 16 bytes) and the ciphertext (remaining bytes)
					const iv = CryptoJS.lib.WordArray.create(decodedData.words.slice(0, 4), 16);
					const ciphertext = CryptoJS.lib.WordArray.create(decodedData.words.slice(4));

					//console.log("IV (Hex): ", iv.toString(CryptoJS.enc.Hex));
					//console.log("Ciphertext (Hex): ", ciphertext.toString(CryptoJS.enc.Hex));

					// Decrypt the data using CryptoJS
					const key = CryptoJS.enc.Base64.parse(btoa('a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6')); // Ensure the key matches server-side key

					const decryptedData = CryptoJS.AES.decrypt({
						ciphertext: ciphertext
					}, key, {
						iv: iv,
						mode: CryptoJS.mode.CBC,
						padding: CryptoJS.pad.Pkcs7
					});

					const data = decryptedData.toString(CryptoJS.enc.Utf8);
					if (!data) {
						//console.error("Decryption failed: Empty result.");
					} else {
						//console.log("Data: " + data);
					}
					
				// Assign "data" to other parts
				
					const milldata = data;
					const lines = data.split('\n');
					
					const swapsToday = calculateSwapsToday(lines);
					const swapsDay = calculateSwapsDay(lines);
				
				// Save data for Mill Containers
					
				    // Find the section starting from "Millions:"
					const millionsIndex = milldata.indexOf("Millions:");
					if (millionsIndex === -1) {
						console.error("No 'Millions:' section found in count.txt");
						return;
					}
					
					// Extract the lines after "Millions:"
					const millionsData = milldata.substring(millionsIndex + "Millions:".length).trim();
					const millionsLines = millionsData.split('\n').filter(line => line.trim() !== '');
					// Populate the <ul id="mill-swaps"> list
					const millSwapsList = document.getElementById('mill-swaps');
					if (!millSwapsList) {
						console.error("Element with id 'mill-swaps' not found in the document.");
						return;
					}
					// Clear existing list items
					millSwapsList.innerHTML = '';
					
					// Add each line as a list item
					millionsLines.forEach(line => {
						const listItem = document.createElement('li');
						listItem.textContent = line.trim();
						millSwapsList.appendChild(listItem);
					});
					
				//
                
                // Compare Day
                
                    const compareDayBtn = document.getElementById('compare-day-btn');
                    const popup = document.getElementById('day-selector-popup');
                    const form = document.getElementById('day-selector-form');
                    const tooltipBar = document.getElementById('tooltip-bar');
                
                    // Show the popup when "Compare Day" button is clicked
                    compareDayBtn.addEventListener('click', () => {
                        popup.style.display = 'block';
                    });
                
                    // Handle Compare Day selection
                    form.addEventListener('change', (event) => {
                        const selectedDay = event.target.value; // Get the selected day (e.g., "Mon")
                        popup.style.display = 'none'; // Close the popup
                
                        const swapsDailyList = document.querySelectorAll('#swaps-daily li');
                        const matchingEntries = [];
                
                        swapsDailyList.forEach((listItem) => {
                            if (listItem.textContent.includes(selectedDay)) {
                                matchingEntries.push(listItem.textContent.trim());
                            }
                        });
                
                        if (matchingEntries.length > 0) {
                            const clipboardText = matchingEntries.join('\n') + "\n\nFrom:\nhttps://tinyurl.com/22lbqehp";
                            navigator.clipboard.writeText(clipboardText).then(() => {
                                // Show tooltip bar
                                showTooltip('Copied to clipboard!');
                            }).catch((err) => {
                                console.error('Error copying to clipboard:', err);
                                showTooltip('Failed to copy to clipboard!');
                            });
                        } else {
                            showTooltip(`No entries found for ${selectedDay}.`);
                        }
                    });
                    // Close button functionality
                    const closeDaySelectorBtn = document.getElementById('close-day-selector');
                    closeDaySelectorBtn.addEventListener('click', () => {
                        document.getElementById('day-selector-popup').style.display = 'none';
                    });
                    
                    // Close the popup when clicking outside it
                    window.addEventListener('click', (event) => {
                        const popup = document.getElementById('day-selector-popup');
                        if (event.target === popup) {
                            popup.style.display = 'none';
                        }
                    });
                
                    // Function to show tooltip
                    function showTooltip(message) {
                        tooltipBar.textContent = message;
                        tooltipBar.style.display = 'block';
                
                        setTimeout(() => {
                            tooltipBar.style.opacity = '1'; // Show the bar
                            setTimeout(() => {
                                tooltipBar.style.opacity = '0'; // Fade out
                                setTimeout(() => {
                                    tooltipBar.style.display = 'none'; // Hide completely
                                }, 300); // Wait for fade-out to complete
                            }, 1000); // Display duration
                        }, 0);
                    }
                document.getElementById('swaps-today').textContent = swapsToday.toLocaleString();
                // Get the latest entry time
                const latestEntry = lines[0].split(';').map(item => item.trim());
                const latestDateTime = `${latestEntry[0]} ${latestEntry[1]}`;
                const chinaTime = new Date(`${latestDateTime} UTC+08:00`);
                // Display full timestamp
                document.getElementById('full-timestamp').textContent = ""; //`${latestDateTime} (UTC+8)`;
                // Calculate "mins ago" from latest entry to now
                updateLastUpdated(chinaTime);
                // Calculate and display the estimated hourly rate
                const swapCount15Min = parseInt(latestEntry[3], 10);
                const hourlyRate = swapCount15Min * 4;
                document.getElementById('hourly-rate').textContent = hourlyRate.toLocaleString();
                
                // Display Total Count
                document.getElementById('total-swaps').textContent = latestEntry[2];
                // Update daily swaps
                const swapsDayElement = document.getElementById('swaps-daily');
                swapsDayElement.innerHTML = swapsDay.map(item => `<li>${item}</li>`).join('');
                // Display raw data, if necessary
                const codeElement = document.querySelector('#data-container code');
                codeElement.innerHTML = lines.map(line => `<span>${line}</span>`).join('');
            } catch (error) {
                console.error('Error fetching data:', error);
            }
        }
        function updateLastUpdated(chinaTime) {
            const now = new Date();
            const minutesAgo = Math.floor((now - chinaTime) / 60000); // Minutes difference
            document.getElementById('mins-ago').textContent = `${minutesAgo} mins ago`;
        
            // Display the current time in China at page load
            const chinaLocalTime = new Date().toLocaleTimeString('en-GB', {
                hour: '2-digit',
                minute: '2-digit',
                timeZone: 'Asia/Shanghai'
            });
            document.getElementById('china-local-time').textContent = chinaLocalTime + " (UTC+8)";
        }
		// Swaps Today Caculate
		
			function calculateSwapsToday(lines) {
				let latestStartTotal = 0;
				for (let line of lines) {
					const [date, time, total] = line.split(';').map((item) => item.trim());
					if (time === '00:00') {
						latestStartTotal = parseInt(total.replace(/,/g, ''));
						break;
					}
				}
				if (latestStartTotal === 0) {
					console.warn('No "00:00" entry found. Using the first entry as a fallback.');
					latestStartTotal = parseInt(lines[lines.length - 1].split(';')[2].replace(/,/g, ''));
				}
				const latestTotal = parseInt(lines[0].split(';')[2].replace(/,/g, '').trim());
				return latestTotal - latestStartTotal;
			}
			
		// Daily Swaps
        
			function calculateSwapsDay(lines) {
				const midnightEntries = [];
				
				// Collect all midnight entries
				for (let line of lines) {
					const [date, time, total] = line.split(';').map((item) => item.trim());
					if (time === '00:00') {
						midnightEntries.push({ date, total: parseInt(total.replace(/,/g, '')) });
					}
				}
			
				// Ensure we have at least 7 days of data
				if (midnightEntries.length < 2) {
					console.error('Not enough data to calculate daily swaps.');
					return [];
				}
			
				const swapsPerDay = [];
				let weeklyTotal = 0; // To accumulate swaps for each 7-day period
				let dayCount = 0; // Use 'let' so it can be incremented
				
				for (let i = 1; i < midnightEntries.length; i++) {
					const daySwaps = midnightEntries[i - 1].total - midnightEntries[i].total;
					const dayHourly = Math.ceil(daySwaps / 24);
					const date = midnightEntries[i].date;
					const dayName = new Date(date).toLocaleDateString('en-GB', { weekday: 'short' });
				
					// Add the day's swaps to the weekly total
					weeklyTotal += daySwaps;
				
					// Increment dayCount for each day processed
					dayCount++;
				
					swapsPerDay.push(`${date}, ${dayName}: ${daySwaps.toLocaleString()} (${dayHourly.toLocaleString()}/hr)`);
				
					// Check if the day is Monday to mark the end of the week
					if (dayName === 'Mon') {
						// Calculate the weekly hourly rate based on total swaps and day count
						const weekHourly = Math.ceil(weeklyTotal / (dayCount * 24)); 
						swapsPerDay.push(`Week Total: ${weeklyTotal.toLocaleString()} (${weekHourly.toLocaleString()}/hr)`);
						swapsPerDay.push(`&nbsp`); // Separator line for a new week
				
						// Reset weekly total and day count after each Monday
						weeklyTotal = 0;
						dayCount = 0;
					}
				}
				return swapsPerDay;
			}
		
        fetchData();
		
		const toggleButton = document.getElementById('toggle-data-btn');
		toggleButton.addEventListener('click', () => {
			const dataContainer = document.getElementById('data-container');
			if (dataContainer.style.display === 'none' || !dataContainer.style.display) {
				dataContainer.style.display = 'block';
				toggleButton.textContent = 'Hide Raw Data';
				// Scroll to make the button visible at the top of the screen
				toggleButton.scrollIntoView({ behavior: 'smooth', block: 'start' });
			} else {
				dataContainer.style.display = 'none';
				toggleButton.textContent = 'Show Raw Data';
			}
		});
        document.getElementById('download-btn').addEventListener('click', () => {
            const a = document.createElement('a');
            a.href = 'https://lightyellow-reindeer-769516.hostingersite.com/count.txt';
            a.download = 'count.txt';
            document.body.appendChild(a);
            a.click();
            document.body.removeChild(a);
        });
		
		// Force page reload
		window.onload = function () {
			location.reload(true); // Reload from server, not cache
		};
    </script>
</body>
</html>