// Copyright Marcus Del Favero 2025
// Licensed under the GNU AGPLv3 with an exception, see `README.md` for details
use std::io::Result;
use std::net::{UdpSocket, SocketAddr};

use socket2::{Socket, Domain, Type};
use tokio::{net::UdpSocket as TokioUdpSocket, task};

/**
 * Creates a UDP socket with IPV6_V6ONLY explicitly disabled. This explicitly
 * ensures that binding to `::` allows connections from both IPv4 and IPv6
 * addresses. On Linux (at least on my machine), this isn't necessary as
 * IPV6_V6ONLY is disabled by default, but on Windows this isn't the case.
 */
pub fn new(addr: SocketAddr) -> Result<UdpSocket> {
	task::block_in_place(|| match addr {
		#[allow(clippy::disallowed_methods)] // Fine as only IPv4
		SocketAddr::V4(addr) => UdpSocket::bind(addr), // If binding to an IPv4 address, create the socket as usual
		SocketAddr::V6(addr) => { // Otherwise uses the `socket2` crate to manually create the socket
			let socket = Socket::new(Domain::IPV6, Type::DGRAM /* UDP */, None)?;
			socket.set_only_v6(false)?;
			socket.bind(&addr.into())?;
			Ok(socket.into())
		},
	})
}

pub fn new_tokio(addr: SocketAddr) -> Result<TokioUdpSocket> {
	let socket = new(addr)?;
	socket.set_nonblocking(true)?;
	TokioUdpSocket::from_std(socket)
}
