/*
 * Description: Vectored I/O traits and structs.
 *
 * Copyright (C) 2025 d@nny mc² <dmc2@amass.energy>
 * SPDX-License-Identifier: AGPL-3.0-or-later
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published
 * by the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
 */

#![cfg(all(unix, feature = "std-io"))]

//! Vectored I/O traits and structs.

use std::{marker::PhantomData, mem, slice};

use libc;

use super::BufError;


pub struct IoSlice<'data> {
  vec: libc::iovec,
  _ph: PhantomData<&'data [u8]>,
}

impl<'data> IoSlice<'data> {
  #[inline(always)]
  pub const fn new(buf: &'data [u8]) -> Self {
    Self {
      vec: libc::iovec {
        iov_base: buf.as_ptr() as *mut u8 as *mut libc::c_void,
        iov_len: buf.len(),
      },
      _ph: PhantomData,
    }
  }

  #[inline]
  pub fn advance(&mut self, n: usize) {
    if self.vec.iov_len < n {
      panic!("advancing IoSlice beyond its length");
    }
    unsafe {
      self.vec.iov_len -= n;
      self.vec.iov_base = self.vec.iov_base.add(n);
    }
  }

  #[inline(always)]
  pub const fn as_slice(&self) -> &'data [u8] {
    unsafe { slice::from_raw_parts(self.vec.iov_base as *mut u8, self.vec.iov_len) }
  }
}

pub trait WriteVecData {
  type E: BufError;
  fn write_vectored_data<'data>(
    &mut self,
    data: &'data [IoSlice<'data>],
  ) -> (usize, Option<Self::E>);
}

pub struct IoSliceMut<'buf> {
  vec: libc::iovec,
  _ph: PhantomData<&'buf mut [mem::MaybeUninit<u8>]>,
}

impl<'buf> IoSliceMut<'buf> {
  #[inline(always)]
  pub const fn new(buf: &'buf mut [u8]) -> Self {
    Self {
      vec: libc::iovec {
        iov_base: buf.as_mut_ptr() as *mut libc::c_void,
        iov_len: buf.len(),
      },
      _ph: PhantomData,
    }
  }

  #[inline]
  pub fn advance(&mut self, n: usize) {
    if self.vec.iov_len < n {
      panic!("advancing IoSliceMut beyond its length");
    }
    unsafe {
      self.vec.iov_len -= n;
      self.vec.iov_base = self.vec.iov_base.add(n);
    }
  }

  #[inline(always)]
  pub const fn as_slice(&self) -> &[mem::MaybeUninit<u8>] {
    unsafe {
      slice::from_raw_parts(
        self.vec.iov_base as *mut mem::MaybeUninit<u8>,
        self.vec.iov_len,
      )
    }
  }

  #[inline(always)]
  pub const fn into_slice(self) -> &'buf mut [mem::MaybeUninit<u8>] {
    unsafe {
      slice::from_raw_parts_mut(
        self.vec.iov_base as *mut mem::MaybeUninit<u8>,
        self.vec.iov_len,
      )
    }
  }

  #[inline(always)]
  pub const fn as_mut_slice(&mut self) -> &mut [mem::MaybeUninit<u8>] {
    unsafe {
      slice::from_raw_parts_mut(
        self.vec.iov_base as *mut mem::MaybeUninit<u8>,
        self.vec.iov_len,
      )
    }
  }
}

pub trait ReadVecBuf {
  type E: BufError;
  fn read_vectored_buf<'buf>(
    &mut self,
    buf: &'buf mut [IoSliceMut<'buf>],
  ) -> (usize, Option<Self::E>);
}
