pragma Singleton

import QtQuick
import Quickshell
import Quickshell.Io
import ZShell.Internal
import qs.Config

Singleton {
	id: root

	// Private properties
	property real _downloadSpeed: 0
	property real _downloadTotal: 0

	// Initial readings for calculating totals
	property real _initialRxBytes: 0
	property real _initialTxBytes: 0
	property bool _initialized: false

	// Previous readings for calculating speed
	property real _prevRxBytes: 0
	property real _prevTimestamp: 0
	property real _prevTxBytes: 0
	property real _uploadSpeed: 0
	property real _uploadTotal: 0

	// History buffers for sparkline
	readonly property CircularBuffer downloadBuffer: _downloadBuffer

	// Current speeds in bytes per second
	readonly property real downloadSpeed: _downloadSpeed

	// Total bytes transferred since tracking started
	readonly property real downloadTotal: _downloadTotal
	readonly property int historyLength: 30
	property int refCount: 0
	readonly property CircularBuffer uploadBuffer: _uploadBuffer
	readonly property real uploadSpeed: _uploadSpeed
	readonly property real uploadTotal: _uploadTotal

function resetSampling(): void {
	netDevFile.reload();

	const content = netDevFile.text();
	if (!content)
		return;

	const data = root.parseNetDev(content);
	const now = Date.now();

	root._prevRxBytes = data.rx;
	root._prevTxBytes = data.tx;
	root._prevTimestamp = now;
}

	function formatBytes(bytes: real): var {
		// Handle negative or invalid values
		if (bytes < 0 || isNaN(bytes) || !isFinite(bytes)) {
			return {
				value: 0,
				unit: "B/s"
			};
		}

		if (bytes < 1024) {
			return {
				value: bytes,
				unit: "B/s"
			};
		} else if (bytes < 1024 * 1024) {
			return {
				value: bytes / 1024,
				unit: "KB/s"
			};
		} else if (bytes < 1024 * 1024 * 1024) {
			return {
				value: bytes / (1024 * 1024),
				unit: "MB/s"
			};
		} else {
			return {
				value: bytes / (1024 * 1024 * 1024),
				unit: "GB/s"
			};
		}
	}

	function formatBytesTotal(bytes: real): var {
		// Handle negative or invalid values
		if (bytes < 0 || isNaN(bytes) || !isFinite(bytes)) {
			return {
				value: 0,
				unit: "B"
			};
		}

		if (bytes < 1024) {
			return {
				value: bytes,
				unit: "B"
			};
		} else if (bytes < 1024 * 1024) {
			return {
				value: bytes / 1024,
				unit: "KB"
			};
		} else if (bytes < 1024 * 1024 * 1024) {
			return {
				value: bytes / (1024 * 1024),
				unit: "MB"
			};
		} else {
			return {
				value: bytes / (1024 * 1024 * 1024),
				unit: "GB"
			};
		}
	}

	function parseNetDev(content: string): var {
		const lines = content.split("\n");
		let totalRx = 0;
		let totalTx = 0;

		for (let i = 2; i < lines.length; i++) {
			const line = lines[i].trim();
			if (!line)
				continue;

			const parts = line.split(/\s+/);
			if (parts.length < 10)
				continue;

			const iface = parts[0].replace(":", "");
			// Skip loopback interface
			if (iface === "lo")
				continue;

			const rxBytes = parseFloat(parts[1]) || 0;
			const txBytes = parseFloat(parts[9]) || 0;

			totalRx += rxBytes;
			totalTx += txBytes;
		}

		return {
			rx: totalRx,
			tx: totalTx
		};
	}

	CircularBuffer {
		id: _downloadBuffer

		capacity: root.historyLength + 1
	}

	CircularBuffer {
		id: _uploadBuffer

		capacity: root.historyLength + 1
	}

	FileView {
		id: netDevFile

		path: "/proc/net/dev"
	}

	Timer {
		interval: Config.dashboard.resourceUpdateInterval
		repeat: true
		running: true
		triggeredOnStart: true

		onTriggered: {
			if (root.refCount <= 0) {
				root.resetSampling();
				return;
			}

			netDevFile.reload();
			const content = netDevFile.text();
			if (!content)
				return;

			const data = root.parseNetDev(content);
			const now = Date.now();

			if (!root._initialized) {
				root._initialRxBytes = data.rx;
				root._initialTxBytes = data.tx;
				root._prevRxBytes = data.rx;
				root._prevTxBytes = data.tx;
				root._prevTimestamp = now;
				root._initialized = true;
				return;
			}

			const timeDelta = (now - root._prevTimestamp) / 1000; // seconds
			if (timeDelta > 0) {
				// Calculate byte deltas
				let rxDelta = data.rx - root._prevRxBytes;
				let txDelta = data.tx - root._prevTxBytes;

				// Handle counter overflow (when counters wrap around from max to 0)
				// This happens when counters exceed 32-bit or 64-bit limits
				if (rxDelta < 0) {
					// Counter wrapped around - assume 64-bit counter
					rxDelta += Math.pow(2, 64);
				}
				if (txDelta < 0) {
					txDelta += Math.pow(2, 64);
				}

				// Calculate speeds
				root._downloadSpeed = rxDelta / timeDelta;
				root._uploadSpeed = txDelta / timeDelta;

				if (root._downloadSpeed >= 0 && isFinite(root._downloadSpeed))
					_downloadBuffer.push(root._downloadSpeed);

				if (root._uploadSpeed >= 0 && isFinite(root._uploadSpeed))
					_uploadBuffer.push(root._uploadSpeed);
			}

			// Calculate totals with overflow handling
			let downTotal = data.rx - root._initialRxBytes;
			let upTotal = data.tx - root._initialTxBytes;

			// Handle counter overflow for totals
			if (downTotal < 0) {
				downTotal += Math.pow(2, 64);
			}
			if (upTotal < 0) {
				upTotal += Math.pow(2, 64);
			}

			root._downloadTotal = downTotal;
			root._uploadTotal = upTotal;

			root._prevRxBytes = data.rx;
			root._prevTxBytes = data.tx;
			root._prevTimestamp = now;
		}
	}
}
