use super::*;
use crate::{
	os::windows::{
		named_pipe::PipeMode, security_descriptor::create_security_attributes, winprelude::*,
	},
	AsPtr, HandleOrErrno,
};
use std::num::NonZeroU8;
use windows_sys::Win32::{
	Storage::FileSystem::{
		FILE_FLAG_FIRST_PIPE_INSTANCE, FILE_FLAG_OVERLAPPED, FILE_FLAG_WRITE_THROUGH,
	},
	System::Pipes::{CreateNamedPipeW, PIPE_NOWAIT, PIPE_REJECT_REMOTE_CLIENTS},
};

impl PipeListenerOptions<'_> {
	pub(super) fn _create(
		&self,
		role: PipeStreamRole,
		recv_mode: Option<PipeMode>,
	) -> io::Result<(PipeListenerOptions<'static>, FileHandle)> {
		let owned_config = self.to_owned()?;

		let instance = self
			.create_instance(true, self.nonblocking, false, role, recv_mode)
			.map(FileHandle::from)?;
		Ok((owned_config, instance))
	}

	/// Creates an instance of a pipe for a listener with the specified stream type and with the
	/// first-instance flag set to the specified value.
	pub(crate) fn create_instance(
		&self,
		first: bool,
		nonblocking: bool,
		overlapped: bool,
		role: PipeStreamRole,
		recv_mode: Option<PipeMode>,
	) -> io::Result<OwnedHandle> {
		if recv_mode == Some(PipeMode::Messages) && self.mode == PipeMode::Bytes {
			return Err(io::Error::new(
				io::ErrorKind::InvalidInput,
				"\
cannot create pipe server that has byte type but receives messages – have you forgotten to set the \
`mode` field in `PipeListenerOptions`?",
			));
		}

		let open_mode = self.open_mode(first, role, overlapped);
		let pipe_mode = self.pipe_mode(recv_mode, nonblocking);

		let sa = create_security_attributes(
			self.security_descriptor.as_ref().map(|sd| sd.borrow()),
			self.inheritable,
		);

		let max_instances = match self.instance_limit.map(NonZeroU8::get) {
			Some(255) => return Err(io::Error::new(
				io::ErrorKind::InvalidInput,
				"cannot set 255 as the named pipe instance limit due to 255 being a reserved value",
			)),
			Some(x) => x.into(),
			None => 255,
		};

		unsafe {
			CreateNamedPipeW(
				(*self.path).as_ptr(),
				open_mode,
				pipe_mode,
				max_instances,
				self.output_buffer_size_hint,
				self.input_buffer_size_hint,
				self.wait_timeout.to_raw(),
				sa.as_ptr().cast_mut().cast(),
			)
			.handle_or_errno()
			.map(|h|
				// SAFETY: we just made it and received ownership
				OwnedHandle::from_raw_handle(h.to_std()))
		}
	}

	fn open_mode(&self, first: bool, role: PipeStreamRole, overlapped: bool) -> u32 {
		let mut open_mode = 0_u32;
		open_mode |= u32::from(role.direction_as_server());
		if first {
			open_mode |= FILE_FLAG_FIRST_PIPE_INSTANCE;
		}
		if self.write_through {
			open_mode |= FILE_FLAG_WRITE_THROUGH;
		}
		if overlapped {
			open_mode |= FILE_FLAG_OVERLAPPED;
		}
		open_mode
	}
	fn pipe_mode(&self, recv_mode: Option<PipeMode>, nonblocking: bool) -> u32 {
		let mut pipe_mode = 0_u32;
		pipe_mode |= self.mode.to_pipe_type();
		pipe_mode |= recv_mode.map_or(0, PipeMode::to_readmode);
		if nonblocking {
			pipe_mode |= PIPE_NOWAIT;
		}
		if !self.accept_remote {
			pipe_mode |= PIPE_REJECT_REMOTE_CLIENTS;
		}
		pipe_mode
	}
}
