//! Syncronous stream `Read` componant.
use super::*;

/// How buffers are used.
pub trait BufferKind : private::Sealed
{
    type InternalBuffer;

    fn create_buffer(cap: usize) -> Self::InternalBuffer;

    fn buffer_len<R: ?Sized>(source: &Source<R, Self>) -> usize;
    fn buffer_cap<R: ?Sized>(source: &Source<R, Self>) -> usize;

    fn buffer_bytes_mut(source: &mut Self::InternalBuffer) -> &'_ mut [u8];
    fn buffer_bytes(source: &Self::InternalBuffer) -> &'_ [u8];

    fn buffer_resize<R: ?Sized>(source: &mut Source<R, Self>, to: usize);
}

/// Use struct-internal buffer for `Read`s
#[derive(Debug)]
pub struct UseBufferInternal;
/// Reuse the output buffer for `Read`s
#[derive(Debug)]
pub struct UseBufferExternal;

impl private::Sealed for UseBufferInternal{}
impl BufferKind for UseBufferInternal
{
    type InternalBuffer = BufferVec;

    #[inline] fn create_buffer(cap: usize) -> Self::InternalBuffer {
	if cap == 0 {
	    BufferVec::new()
	} else {
	    BufferVec::with_capacity(cap)
	}
    }
    
    #[inline(always)] fn buffer_cap<R: ?Sized>(source: &Source<R, Self>) -> usize {
	source.buffer.capacity()
    }
    #[inline(always)] fn buffer_len<R: ?Sized>(source: &Source<R, Self>) -> usize {
	source.buffer.len()
    }
    
    #[inline(always)] fn buffer_bytes_mut(source: &mut Self::InternalBuffer) -> &'_ mut [u8]
    {
	&mut source[..]
    }
    #[inline(always)] fn buffer_bytes(source: &Self::InternalBuffer) -> &'_ [u8]
    {
	&source[..]
    }

    #[inline(always)] fn buffer_resize<R: ?Sized>(source: &mut Source<R, Self>, to: usize)
    {
	source.buffer.resize(to, 0);
    }
}
impl private::Sealed for UseBufferExternal{}
impl BufferKind for UseBufferExternal
{
    type InternalBuffer = ();

    // -- always used --
    
    #[inline(always)] fn create_buffer(_: usize) -> Self::InternalBuffer {
	()
    }
    #[inline(always)] fn buffer_cap<R: ?Sized>(_: &Source<R, Self>) -> usize {
	0
    }

    // -- conditional --
    
    #[cold]
    #[inline(never)] fn buffer_len<R: ?Sized>(_: &Source<R, Self>) -> usize {
	panic!("Phantom buffer length cannot be checked")
    }
    #[cold]
    #[inline(never)] fn buffer_bytes_mut(_: &mut Self::InternalBuffer) -> &'_ mut [u8]
    {
	panic!("Cannot mutref non-existent ibuf.")
    }
    #[cold]
    #[inline(never)] fn buffer_bytes(_: &Self::InternalBuffer) -> &'_ [u8]
    {    
	panic!("Cannot ref non-existent ibuf.")
    }
    #[cold]
    #[inline(never)] fn buffer_resize<R: ?Sized>(_: &mut Source<R, Self>, _: usize)
    {
	panic!("Cannot resize non-existent ibuf.")
    }
}

#[cfg(not(feature="reuse-buffer"))] 
pub type DefaultBuffer = UseBufferInternal;
#[cfg(feature="reuse-buffer")] 
pub type DefaultBuffer = UseBufferExternal;

/// TODO: Document
//#[derive(Debug)]
pub struct Source<R: ?Sized, Buffer: ?Sized + BufferKind = DefaultBuffer>
{
    crypter: Crypter,
    buffer: Buffer::InternalBuffer, // When `reuse-buffer` is enabled, this isn't needed. We re-use the output buffer for the initial read of untransformed data from `stream` and the actual transformation of the read bytes.
    
    stream: R
}

impl<R: ?Sized+ fmt::Debug, K: ?Sized + BufferKind> fmt::Debug for Source<R, K>
{
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result
    {
	use std::any::type_name;
	write!(f, "Source<Wraps: {}, BufferKind: {}>", type_name::<R>(), type_name::<K>())?;
	match K::buffer_cap(self) {
	    0 => write!(f, "({:?}, (unbounded buffer cap))", &self.stream),
	    cap => write!(f, "({:?}, ({} buffer cap))", &self.stream, cap), 
	}
    }
}

impl<R: ?Sized, K: ?Sized + BufferKind> Source<R, K>
where R: Read
{
    /// The crypter of this instance
    #[inline] pub fn crypter(&self) -> &Crypter
    {
	&self.crypter
    }
    
    /// The crypter of this instance
    #[inline] pub fn crypter_mut(&mut self) -> &mut Crypter
    {
	&mut self.crypter
    }

    /// The inner stream
    #[inline] pub fn inner(&self) -> &R
    {
	&self.stream
    }
    
    /// The inner stream
    #[inline] pub fn inner_mut(&mut self) -> &mut R
    {
	&mut self.stream
    }

    /// Grow the inner buffer to fix this size, if needed.
    fn grow_to_fit(&mut self, sz: usize)
    {
	if sz > K::buffer_len(self) {
	    K::buffer_resize(self, sz);
	}
    }
    
    /// Perform the cipher transform on this input to the inner buffer, returning the number of bytes updated.
    fn transform(&mut self, bufsz: usize, output: &mut [u8]) -> Result<usize, ErrorStack>
    {
	//self.grow_to_fix(output.len());
	//let bufsz = self.stream.read(&mut self.buffer[..bufsz])?;
	let n = self.crypter.update(& K::buffer_bytes(&self.buffer)[..bufsz], &mut output[..])?;
	let _f = self.crypter.finalize(&mut output[..n])?;
	debug_assert_eq!(_f, 0);

	Ok(n)
    }

    /// Clear the internal buffer while keeping it allocated for further use.
    ///
    /// This does not affect operations at all, all it does is 0 out the left-over temporary buffer from the last operation(s).
    #[inline] 
    pub fn prune(&mut self)
    {
	#[cfg(feature="explicit_clear")]
	{
	    bytes::explicit_prune(K::buffer_bytes_mut(self));
	    return;
	}
	#[cfg(not(feature="explicit_clear"))] 
	unsafe {
	    std::ptr::write_bytes(K::buffer_bytes_mut(&mut self.buffer).as_mut_ptr(), 0, K::buffer_len(self));
	}
    }

}

impl<R> Source<R, UseBufferExternal>
{
    /// Convert this instance to use external buffer (instead of internal.)
    pub fn with_reused_buffer(self) -> Source<R, UseBufferInternal>
    {
	Source {
	    buffer: UseBufferInternal::create_buffer(UseBufferExternal::buffer_cap(&self)),
	    crypter: self.crypter,
	    stream: self.stream,
	}
    }
}

impl<R> Source<R, UseBufferInternal>
{
    /// Convert this instance to use external buffer (instead of internal.)
    pub fn with_reused_buffer(self) -> Source<R, UseBufferExternal>
    {
	Source {
	    buffer: UseBufferExternal::create_buffer(UseBufferInternal::buffer_cap(&self)),
	    crypter: self.crypter,
	    stream: self.stream,
	}
    }
}

impl<R: ?Sized, K: ?Sized + BufferKind> Read for Source<R, K>
where R: Read
{
    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
	(#[cfg(feature="reuse-buffer")] {
	    todo!()
	},
	 #[cfg(not(feature="reuse-buffer"))] {
	     self.grow_to_fit(buf.len());
	     let read = self.stream.read(&mut self.buffer[..buf.len()])?;
	     Ok(self.transform(read, &mut buf[..read])?)
	 },).0
    }
}