#[cfg(feature = "alloc")]
use alloc::{vec, vec::Vec};
#[cfg(feature = "std")]
use core::cmp;
use core::mem;

#[cfg(feature = "std")]
use std::io::{self, Read as StdRead};

use crate::error::{Error, ErrorCode, Result};

#[cfg(not(feature = "unsealed_read_write"))]
/// Trait used by the deserializer for iterating over input.
///
/// This trait is sealed by default, enabling the `unsealed_read_write` feature removes this bound
/// to allow objects outside of this crate to implement this trait.
pub trait Read<'de>: private::Sealed {
    #[doc(hidden)]
    /// Read n bytes from the input.
    ///
    /// Implementations that can are asked to return a slice with a Long lifetime that outlives the
    /// decoder, but others (eg. ones that need to allocate the data into a temporary buffer) can
    /// return it with a Short lifetime that just lives for the time of read's mutable borrow of
    /// the reader.
    ///
    /// This may, as a side effect, clear the reader's scratch buffer (as the provided
    /// implementation does).
    ///
    /// A more appropriate lifetime setup for this (that would allow the Deserializer::convert_str
    /// to stay a function) would be something like `fn read<'a, 'r: 'a>(&'a mut 'r immut self, ...) -> ...
    /// EitherLifetime<'r, 'de>>`, which borrows self mutably for the duration of the function and
    /// downgrates that reference to an immutable one that outlives the result (protecting the
    /// scratch buffer from changes), but alas, that can't be expressed (yet?).
    fn read<'a>(&'a mut self, n: usize) -> Result<EitherLifetime<'a, 'de>> {
        self.clear_buffer();
        self.read_to_buffer(n)?;

        Ok(self.take_buffer())
    }

    #[doc(hidden)]
    fn next(&mut self) -> Result<Option<u8>>;

    #[doc(hidden)]
    fn peek(&mut self) -> Result<Option<u8>>;

    #[doc(hidden)]
    fn clear_buffer(&mut self);

    #[doc(hidden)]
    fn read_to_buffer(&mut self, n: usize) -> Result<()>;

    #[doc(hidden)]
    fn take_buffer<'a>(&'a mut self) -> EitherLifetime<'a, 'de>;

    #[doc(hidden)]
    fn read_into(&mut self, buf: &mut [u8]) -> Result<()>;

    #[doc(hidden)]
    fn discard(&mut self);

    #[doc(hidden)]
    fn offset(&self) -> u64;
}

#[cfg(feature = "unsealed_read_write")]
/// Trait used by the deserializer for iterating over input.
pub trait Read<'de> {
    /// Read n bytes from the input.
    ///
    /// Implementations that can are asked to return a slice with a Long lifetime that outlives the
    /// decoder, but others (eg. ones that need to allocate the data into a temporary buffer) can
    /// return it with a Short lifetime that just lives for the time of read's mutable borrow of
    /// the reader.
    ///
    /// This may, as a side effect, clear the reader's scratch buffer (as the provided
    /// implementation does).

    // A more appropriate lifetime setup for this (that would allow the Deserializer::convert_str
    // to stay a function) would be something like `fn read<'a, 'r: 'a>(&'a mut 'r immut self, ...) -> ...
    // EitherLifetime<'r, 'de>>`, which borrows self mutably for the duration of the function and
    // downgrates that reference to an immutable one that outlives the result (protecting the
    // scratch buffer from changes), but alas, that can't be expressed (yet?).
    fn read<'a>(&'a mut self, n: usize) -> Result<EitherLifetime<'a, 'de>> {
        self.clear_buffer();
        self.read_to_buffer(n)?;

        Ok(self.take_buffer())
    }

    /// Read the next byte from the input, if any.
    fn next(&mut self) -> Result<Option<u8>>;

    /// Peek at the next byte of the input, if any. This does not advance the reader, so the result
    /// of this function will remain the same until a read or clear occurs.
    fn peek(&mut self) -> Result<Option<u8>>;

    /// Clear the underlying scratch buffer
    fn clear_buffer(&mut self);

    /// Append n bytes from the reader to the reader's scratch buffer (without clearing it)
    fn read_to_buffer(&mut self, n: usize) -> Result<()>;

    /// Read out everything accumulated in the reader's scratch buffer. This may, as a side effect,
    /// clear it.
    fn take_buffer<'a>(&'a mut self) -> EitherLifetime<'a, 'de>;

    /// Read from the input until `buf` is full or end of input is encountered.
    fn read_into(&mut self, buf: &mut [u8]) -> Result<()>;

    /// Discard any data read by `peek`.
    fn discard(&mut self);

    /// Returns the offset from the start of the reader.
    fn offset(&self) -> u64;
}

/// Represents a reader that can return its current position
pub trait Offset {
    fn byte_offset(&self) -> usize;
}

/// Represents a buffer with one of two lifetimes.
pub enum EitherLifetime<'short, 'long> {
    /// The short lifetime
    Short(&'short [u8]),
    /// The long lifetime
    Long(&'long [u8]),
}

#[cfg(not(feature = "unsealed_read_write"))]
mod private {
    pub trait Sealed {}
}

/// CBOR input source that reads from a std::io input stream.
#[cfg(feature = "std")]
#[derive(Debug)]
pub struct IoRead<R>
where
    R: io::Read,
{
    reader: OffsetReader<R>,
    scratch: Vec<u8>,
    ch: Option<u8>,
}

#[cfg(feature = "std")]
impl<R> IoRead<R>
where
    R: io::Read,
{
    /// Creates a new CBOR input source to read from a std::io input stream.
    pub fn new(reader: R) -> IoRead<R> {
        IoRead {
            reader: OffsetReader { reader, offset: 0 },
            scratch: vec![],
            ch: None,
        }
    }

    #[inline]
    fn next_inner(&mut self) -> Result<Option<u8>> {
        let mut buf = [0; 1];
        loop {
            match self.reader.read(&mut buf) {
                Ok(0) => return Ok(None),
                Ok(_) => return Ok(Some(buf[0])),
                Err(ref e) if e.kind() == io::ErrorKind::Interrupted => {}
                Err(e) => return Err(Error::io(e)),
            }
        }
    }
}

#[cfg(all(feature = "std", not(feature = "unsealed_read_write")))]
impl<R> private::Sealed for IoRead<R> where R: io::Read {}

#[cfg(feature = "std")]
impl<'de, R> Read<'de> for IoRead<R>
where
    R: io::Read,
{
    #[inline]
    fn next(&mut self) -> Result<Option<u8>> {
        match self.ch.take() {
            Some(ch) => Ok(Some(ch)),
            None => self.next_inner(),
        }
    }

    #[inline]
    fn peek(&mut self) -> Result<Option<u8>> {
        match self.ch {
            Some(ch) => Ok(Some(ch)),
            None => {
                self.ch = self.next_inner()?;
                Ok(self.ch)
            }
        }
    }

    fn read_to_buffer(&mut self, mut n: usize) -> Result<()> {
        // defend against malicious input pretending to be huge strings by limiting growth
        self.scratch.reserve(cmp::min(n, 16 * 1024));

        if n == 0 {
            return Ok(());
        }

        if let Some(ch) = self.ch.take() {
            self.scratch.push(ch);
            n -= 1;
        }

        // n == 0 is OK here and needs no further special treatment

        let transfer_result = {
            // Prepare for take() (which consumes its reader) by creating a reference adaptor
            // that'll only live in this block
            let reference = self.reader.by_ref();
            // Append the first n bytes of the reader to the scratch vector (or up to
            // an error or EOF indicated by a shorter read)
            let mut taken = reference.take(n as u64);
            taken.read_to_end(&mut self.scratch)
        };

        match transfer_result {
            Ok(r) if r == n => Ok(()),
            Ok(_) => Err(Error::syntax(
                ErrorCode::EofWhileParsingValue,
                self.offset(),
            )),
            Err(e) => Err(Error::io(e)),
        }
    }

    fn clear_buffer(&mut self) {
        self.scratch.clear();
    }

    fn take_buffer<'a>(&'a mut self) -> EitherLifetime<'a, 'de> {
        EitherLifetime::Short(&self.scratch)
    }

    fn read_into(&mut self, buf: &mut [u8]) -> Result<()> {
        self.reader.read_exact(buf).map_err(|e| {
            if e.kind() == io::ErrorKind::UnexpectedEof {
                Error::syntax(ErrorCode::EofWhileParsingValue, self.offset())
            } else {
                Error::io(e)
            }
        })
    }

    #[inline]
    fn discard(&mut self) {
        self.ch = None;
    }

    fn offset(&self) -> u64 {
        self.reader.offset
    }
}

#[cfg(feature = "std")]
impl<R> Offset for IoRead<R>
where
    R: std::io::Read,
{
    fn byte_offset(&self) -> usize {
        self.offset() as usize
    }
}

#[cfg(feature = "std")]
#[derive(Debug)]
struct OffsetReader<R> {
    reader: R,
    offset: u64,
}

#[cfg(feature = "std")]
impl<R> io::Read for OffsetReader<R>
where
    R: io::Read,
{
    #[inline]
    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
        let r = self.reader.read(buf);
        if let Ok(count) = r {
            self.offset += count as u64;
        }
        r
    }
}

/// A CBOR input source that reads from a slice of bytes.
#[cfg(any(feature = "std", feature = "alloc"))]
#[derive(Debug)]
pub struct SliceRead<'a> {
    slice: &'a [u8],
    scratch: Vec<u8>,
    index: usize,
}

#[cfg(any(feature = "std", feature = "alloc"))]
impl<'a> SliceRead<'a> {
    /// Creates a CBOR input source to read from a slice of bytes.
    pub fn new(slice: &'a [u8]) -> SliceRead<'a> {
        SliceRead {
            slice,
            scratch: vec![],
            index: 0,
        }
    }

    fn end(&self, n: usize) -> Result<usize> {
        match self.index.checked_add(n) {
            Some(end) if end <= self.slice.len() => Ok(end),
            _ => Err(Error::syntax(
                ErrorCode::EofWhileParsingValue,
                self.slice.len() as u64,
            )),
        }
    }
}

#[cfg(any(feature = "std", feature = "alloc"))]
impl Offset for SliceRead<'_> {
    #[inline]
    fn byte_offset(&self) -> usize {
        self.index
    }
}

#[cfg(all(
    any(feature = "std", feature = "alloc"),
    not(feature = "unsealed_read_write")
))]
impl private::Sealed for SliceRead<'_> {}

#[cfg(any(feature = "std", feature = "alloc"))]
impl<'a> Read<'a> for SliceRead<'a> {
    #[inline]
    fn next(&mut self) -> Result<Option<u8>> {
        Ok(if self.index < self.slice.len() {
            let ch = self.slice[self.index];
            self.index += 1;
            Some(ch)
        } else {
            None
        })
    }

    #[inline]
    fn peek(&mut self) -> Result<Option<u8>> {
        Ok(if self.index < self.slice.len() {
            Some(self.slice[self.index])
        } else {
            None
        })
    }

    fn clear_buffer(&mut self) {
        self.scratch.clear();
    }

    fn read_to_buffer(&mut self, n: usize) -> Result<()> {
        let end = self.end(n)?;
        let slice = &self.slice[self.index..end];
        self.scratch.extend_from_slice(slice);
        self.index = end;

        Ok(())
    }

    #[inline]
    fn read<'b>(&'b mut self, n: usize) -> Result<EitherLifetime<'b, 'a>> {
        let end = self.end(n)?;
        let slice = &self.slice[self.index..end];
        self.index = end;
        Ok(EitherLifetime::Long(slice))
    }

    fn take_buffer<'b>(&'b mut self) -> EitherLifetime<'b, 'a> {
        EitherLifetime::Short(&self.scratch)
    }

    #[inline]
    fn read_into(&mut self, buf: &mut [u8]) -> Result<()> {
        let end = self.end(buf.len())?;
        buf.copy_from_slice(&self.slice[self.index..end]);
        self.index = end;
        Ok(())
    }

    #[inline]
    fn discard(&mut self) {
        self.index += 1;
    }

    fn offset(&self) -> u64 {
        self.index as u64
    }
}

/// A CBOR input source that reads from a slice of bytes using a fixed size scratch buffer.
///
/// [`SliceRead`](struct.SliceRead.html) and [`MutSliceRead`](struct.MutSliceRead.html) are usually
/// preferred over this, as they can handle indefinite length items.
#[derive(Debug)]
pub struct SliceReadFixed<'a, 'b> {
    slice: &'a [u8],
    scratch: &'b mut [u8],
    index: usize,
    scratch_index: usize,
}

impl<'a, 'b> SliceReadFixed<'a, 'b> {
    /// Creates a CBOR input source to read from a slice of bytes, backed by a scratch buffer.
    pub fn new(slice: &'a [u8], scratch: &'b mut [u8]) -> SliceReadFixed<'a, 'b> {
        SliceReadFixed {
            slice,
            scratch,
            index: 0,
            scratch_index: 0,
        }
    }

    fn end(&self, n: usize) -> Result<usize> {
        match self.index.checked_add(n) {
            Some(end) if end <= self.slice.len() => Ok(end),
            _ => Err(Error::syntax(
                ErrorCode::EofWhileParsingValue,
                self.slice.len() as u64,
            )),
        }
    }

    fn scratch_end(&self, n: usize) -> Result<usize> {
        match self.scratch_index.checked_add(n) {
            Some(end) if end <= self.scratch.len() => Ok(end),
            _ => Err(Error::scratch_too_small(self.index as u64)),
        }
    }
}

#[cfg(not(feature = "unsealed_read_write"))]
impl private::Sealed for SliceReadFixed<'_, '_> {}

impl<'a> Read<'a> for SliceReadFixed<'a, '_> {
    #[inline]
    fn next(&mut self) -> Result<Option<u8>> {
        Ok(if self.index < self.slice.len() {
            let ch = self.slice[self.index];
            self.index += 1;
            Some(ch)
        } else {
            None
        })
    }

    #[inline]
    fn peek(&mut self) -> Result<Option<u8>> {
        Ok(if self.index < self.slice.len() {
            Some(self.slice[self.index])
        } else {
            None
        })
    }

    fn clear_buffer(&mut self) {
        self.scratch_index = 0;
    }

    fn read_to_buffer(&mut self, n: usize) -> Result<()> {
        let end = self.end(n)?;
        let scratch_end = self.scratch_end(n)?;
        let slice = &self.slice[self.index..end];
        self.scratch[self.scratch_index..scratch_end].copy_from_slice(slice);
        self.index = end;
        self.scratch_index = scratch_end;

        Ok(())
    }

    fn read<'c>(&'c mut self, n: usize) -> Result<EitherLifetime<'c, 'a>> {
        let end = self.end(n)?;
        let slice = &self.slice[self.index..end];
        self.index = end;
        Ok(EitherLifetime::Long(slice))
    }

    fn take_buffer<'c>(&'c mut self) -> EitherLifetime<'c, 'a> {
        EitherLifetime::Short(&self.scratch[0..self.scratch_index])
    }

    #[inline]
    fn read_into(&mut self, buf: &mut [u8]) -> Result<()> {
        let end = self.end(buf.len())?;
        buf.copy_from_slice(&self.slice[self.index..end]);
        self.index = end;
        Ok(())
    }

    #[inline]
    fn discard(&mut self) {
        self.index += 1;
    }

    fn offset(&self) -> u64 {
        self.index as u64
    }
}

#[cfg(any(feature = "std", feature = "alloc"))]
impl Offset for SliceReadFixed<'_, '_> {
    #[inline]
    fn byte_offset(&self) -> usize {
        self.index
    }
}

/// A CBOR input source that reads from a slice of bytes, and can move data around internally to
/// reassemble indefinite strings without the need of an allocated scratch buffer.
#[derive(Debug)]
pub struct MutSliceRead<'a> {
    /// A complete view of the reader's data. It is promised that bytes before buffer_end are not
    /// mutated any more.
    slice: &'a mut [u8],
    /// Read cursor position in slice
    index: usize,
    /// Number of bytes already discarded from the slice
    before: usize,
    /// End of the buffer area that contains all bytes read_into_buffer. This is always <= index.
    buffer_end: usize,
}

impl<'a> MutSliceRead<'a> {
    /// Creates a CBOR input source to read from a slice of bytes.
    pub fn new(slice: &'a mut [u8]) -> MutSliceRead<'a> {
        MutSliceRead {
            slice,
            index: 0,
            before: 0,
            buffer_end: 0,
        }
    }

    fn end(&self, n: usize) -> Result<usize> {
        match self.index.checked_add(n) {
            Some(end) if end <= self.slice.len() => Ok(end),
            _ => Err(Error::syntax(
                ErrorCode::EofWhileParsingValue,
                self.slice.len() as u64,
            )),
        }
    }
}

#[cfg(not(feature = "unsealed_read_write"))]
impl private::Sealed for MutSliceRead<'_> {}

impl<'a> Read<'a> for MutSliceRead<'a> {
    #[inline]
    fn next(&mut self) -> Result<Option<u8>> {
        // This is duplicated from SliceRead, can that be eased?
        Ok(if self.index < self.slice.len() {
            let ch = self.slice[self.index];
            self.index += 1;
            Some(ch)
        } else {
            None
        })
    }

    #[inline]
    fn peek(&mut self) -> Result<Option<u8>> {
        // This is duplicated from SliceRead, can that be eased?
        Ok(if self.index < self.slice.len() {
            Some(self.slice[self.index])
        } else {
            None
        })
    }

    fn clear_buffer(&mut self) {
        self.slice = &mut mem::take(&mut self.slice)[self.index..];
        self.before += self.index;
        self.index = 0;
        self.buffer_end = 0;
    }

    fn read_to_buffer(&mut self, n: usize) -> Result<()> {
        let end = self.end(n)?;
        debug_assert!(
            self.buffer_end <= self.index,
            "MutSliceRead invariant violated: scratch buffer exceeds index"
        );
        self.slice[self.buffer_end..end].rotate_left(self.index - self.buffer_end);
        self.buffer_end += n;
        self.index = end;

        Ok(())
    }

    fn take_buffer<'b>(&'b mut self) -> EitherLifetime<'b, 'a> {
        let (left, right) = mem::take(&mut self.slice).split_at_mut(self.index);
        self.slice = right;
        self.before += self.index;
        self.index = 0;

        let left = &left[..self.buffer_end];
        self.buffer_end = 0;

        EitherLifetime::Long(left)
    }

    #[inline]
    fn read_into(&mut self, buf: &mut [u8]) -> Result<()> {
        // This is duplicated from SliceRead, can that be eased?
        let end = self.end(buf.len())?;
        buf.copy_from_slice(&self.slice[self.index..end]);
        self.index = end;
        Ok(())
    }

    #[inline]
    fn discard(&mut self) {
        self.index += 1;
    }

    fn offset(&self) -> u64 {
        (self.before + self.index) as u64
    }
}
