// Copyright Marcus Del Favero 2025
// Licensed under the GNU AGPLv3 with an exception, see `README.md` for details
pub mod networked;
#[cfg(feature = "client")] pub mod local;

use std::fmt::{Display, Formatter, Result as FmtResult};
#[cfg(feature = "client")] use std::time::Duration;

use super::Message;

use crate::net::serp::Error as SerpError;
#[cfg(feature = "client")] use crate::net::utils::security::Security;

/**
 * A dynamically dispatched message stream that can be sent across threads.
 */
pub type DynMs = dyn MessageStream + Send;

/**
 * Categorises the possible errors of an action on a MessageStream.
 */
#[derive(Clone)]
pub enum MsError {
	/**
	 * The other network node has disconnected normally, with possibly a string
	 * for the reason why they disconnected.
	 */
	Disconnected(Option<String>),

	/**
	 * Something else erroneous has happened during an action, and as a result
	 * the node is already disconnected or should be disconnected. A String is
	 * provided for information about this error.
	 */
	Other(String),
}

impl Display for MsError {
	fn fmt(&self, fmt: &mut Formatter<'_>) -> FmtResult {
		match self {
			MsError::Disconnected(Some(reason)) => write!(fmt, "disconnected: {reason}"),
			MsError::Disconnected(None) => write!(fmt, "disconnected"),
			MsError::Other(err) => write!(fmt, "error: {err}"),
		}
	}
}

impl From<SerpError> for MsError {
	fn from(err: SerpError) -> MsError {
		match err {
			SerpError::Disconnected(err) => MsError::Disconnected(err),
			_ => MsError::Other(err.to_string()),
		}
	}
}

/**
 * A bidirectional stream of messages.
 *
 * This trait represents anything that can maintain a connection between this
 * network node and another network node. Here, "network node" is a general term
 * referring to either the client or the server.
 *
 * Implementations of this trait ideally will ensure that all messages will
 * arrive at the destination in-order, with no loss or duplicates. However,
 * implementations are allowed to violate these guarantees as long as they don't
 * affect the application-layer code. This can refer to the order in which world
 * and scoring messages arrive at the client be independent, as will be the case
 * soon.
 *
 * Security of messages sent is an ideal property and implementations should try
 * to achieve this, but no guarantee of security is required.
 *
 * This trait provides methods to send and receive messages, as well as to flush
 * all messages that have been sent. All of these methods must be non-blocking,
 * or all blocks should be very short (like mutex locks).
 *
 * These methods return a Result with Err variant MsError. This can represent
 * the action being successfully performed (Ok), a "normal" disconnection
 * happening (MsError::Disconnected), or something erroneous happening
 * (MsError::Other) which should result in a disconnection. Once a Disconnected
 * or Other variant is reached, the code shouldn't execute any further methods.
 */
pub trait MessageStream {
	/**
	 * Sends a message to the other node.
	 *
	 * No guarantees are made about whether this message will be received. If the
	 * message is never received then there will be a disconnection. It is
	 * possible that the Ok variant is returned and the message is never
	 * received.
	 *
	 * It is possible that the implementation doesn't send the message as soon as
	 * possible due to buffering. To hint for any potential messages queued up to
	 * be sent, see `flush`.
	 */
	fn send(&mut self, msg: Message) -> Result<(), MsError>;

	/**
	 * Receives a message from the other node if it exists.
	 *
	 * On success, this returns the Ok variant which is an Option<Message>. If
	 * there is a message to be received, the Some variant will be returned,
	 * otherwise None. If there's an error, an appropriate error variant will be
	 * returned returned.
	 */
	fn receive(&mut self) -> Result<Option<Message>, MsError>;

	/**
	 * Ensures that all messages sent since the last `flush` call are sent out as
	 * soon as possible.
	 *
	 * Calling this method is highly recommended after sending one or a few
	 * messages quickly in a row, such as sending messages on the same frame or
	 * update.
	 *
	 * The exact behaviour of this method depends on the implementation. Some
	 * implementations might buffer messages before sending them, so messages
	 * would never actually be sent without calling this. Other implementations
	 * would have this method do nothing.
	 *
	 * Despite the range of possible implementations of this method, one
	 * guarantee is that if no messages have been sent since the last flush (or
	 * creation of the connection), then calling `flush` will do nothing. In
	 * other words, calling `flush` twice in a row does nothing, it doesn't
	 * "fully flush" or anything.
	 */
	fn flush(&mut self) -> Result<(), MsError>;

	/**
	 * Returns whether the message stream is over the network or if messages are
	 * passed locally.
	 *
	 * This can be used to determine whether to perform certain actions if the
	 * connection with the other end might not be reliable, such as sending
	 * redundant player update datagrams.
	 *
	 * This method is expected to always return the same result.
	 */
	#[cfg(feature = "client")]
	fn is_networked(&self) -> bool;

	/**
	 * Closes the message stream and provides a specific message that it's being
	 * closed down. Right now this method is just used by the QUIC server message
	 * streams when a server is closed to customise the close reason.
	 */
	fn close(self: Box<Self>) {}

	/**
	 * Returns a String representation of the source "address" (if any) of the
	 * message stream.
	 */
	fn source(&self) -> String { String::new() }

	/**
	 * Returns an estimation of the ping between the two nodes.
	 */
	#[cfg(feature = "client")]
	fn ping(&self) -> Duration;

	/**
	 * Gets the security of this connection.
	 */
	#[cfg(feature = "client")]
	fn security(&self) -> Security;
}
