// Copyright 2019 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use crate::bitwidth::BitWidth;
use crate::flexbuffer_type::FlexBufferType;
use crate::{Blob, Buffer};
use std::convert::{TryFrom, TryInto};
use std::fmt;
use std::ops::Rem;
use std::str::FromStr;
mod de;
mod iter;
mod map;
mod serialize;
mod vector;
pub use de::DeserializationError;
pub use iter::ReaderIterator;
pub use map::MapReader;
pub use vector::VectorReader;

/// All the possible errors when reading a flexbuffer.
#[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize)]
pub enum Error {
    /// One of the following data errors occured:
    ///
    /// *    The read flexbuffer had an offset that pointed outside the flexbuffer.
    /// *    The 'negative indicies' where length and map keys are stored were out of bounds
    /// *    The buffer was too small to contain a flexbuffer root.
    FlexbufferOutOfBounds,
    /// Failed to parse a valid FlexbufferType and Bitwidth from a type byte.
    InvalidPackedType,
    /// Flexbuffer type of the read data does not match function used.
    UnexpectedFlexbufferType {
        expected: FlexBufferType,
        actual: FlexBufferType,
    },
    /// BitWidth type of the read data does not match function used.
    UnexpectedBitWidth {
        expected: BitWidth,
        actual: BitWidth,
    },
    /// Read a flexbuffer offset or length that overflowed usize.
    ReadUsizeOverflowed,
    /// Tried to index a type that's not one of the Flexbuffer vector types.
    CannotIndexAsVector,
    /// Tried to index a Flexbuffer vector or map out of bounds.
    IndexOutOfBounds,
    /// A Map was indexed with a key that it did not contain.
    KeyNotFound,
    /// Failed to parse a Utf8 string.
    /// The Option will be `None` if and only if this Error was deserialized.
    // NOTE: std::str::Utf8Error does not implement Serialize, Deserialize, nor Default. We tell
    // serde to skip the field and default to None. We prefer to have the boxed error so it can be
    // used with std::error::Error::source, though another (worse) option could be to drop that
    // information.
    Utf8Error(#[serde(skip)] Option<Box<std::str::Utf8Error>>),
    /// get_slice failed because the given data buffer is misaligned.
    AlignmentError,
    InvalidRootWidth,
    InvalidMapKeysVectorWidth,
}
impl std::convert::From<std::str::Utf8Error> for Error {
    fn from(e: std::str::Utf8Error) -> Self {
        Self::Utf8Error(Some(Box::new(e)))
    }
}
impl fmt::Display for Error {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            Self::UnexpectedBitWidth { expected, actual } => write!(
                f,
                "Error reading flexbuffer: Expected bitwidth: {:?}, found bitwidth: {:?}",
                expected, actual
            ),
            Self::UnexpectedFlexbufferType { expected, actual } => write!(
                f,
                "Error reading flexbuffer: Expected type: {:?}, found type: {:?}",
                expected, actual
            ),
            _ => write!(f, "Error reading flexbuffer: {:?}", self),
        }
    }
}
impl std::error::Error for Error {
    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
        if let Self::Utf8Error(Some(e)) = self {
            Some(e)
        } else {
            None
        }
    }
}

pub trait ReadLE: crate::private::Sealed + std::marker::Sized {
    const VECTOR_TYPE: FlexBufferType;
    const WIDTH: BitWidth;
}
macro_rules! rle {
    ($T: ty, $VECTOR_TYPE: ident, $WIDTH: ident) => {
        impl ReadLE for $T {
            const VECTOR_TYPE: FlexBufferType = FlexBufferType::$VECTOR_TYPE;
            const WIDTH: BitWidth = BitWidth::$WIDTH;
        }
    };
}
rle!(u8, VectorUInt, W8);
rle!(u16, VectorUInt, W16);
rle!(u32, VectorUInt, W32);
rle!(u64, VectorUInt, W64);
rle!(i8, VectorInt, W8);
rle!(i16, VectorInt, W16);
rle!(i32, VectorInt, W32);
rle!(i64, VectorInt, W64);
rle!(f32, VectorFloat, W32);
rle!(f64, VectorFloat, W64);

macro_rules! as_default {
    ($as: ident, $get: ident, $T: ty) => {
        pub fn $as(&self) -> $T {
            self.$get().unwrap_or_default()
        }
    };
}

/// `Reader`s allow access to data stored in a Flexbuffer.
///
/// Each reader represents a single address in the buffer so data is read lazily. Start a reader
/// by calling `get_root` on your flexbuffer `&[u8]`.
///
/// - The `get_T` methods return a `Result<T, Error>`. They return an OK value if and only if the
/// flexbuffer type matches `T`. This is analogous to the behavior of Rust's json library, though
/// with Result instead of Option.
/// - The `as_T` methods will try their best to return to a value of type `T`
/// (by casting or even parsing a string if necessary) but ultimately returns `T::default` if it
/// fails. This behavior is analogous to that of flexbuffers C++.
pub struct Reader<B> {
    fxb_type: FlexBufferType,
    width: BitWidth,
    address: usize,
    buffer: B,
}

impl<B: Buffer> Clone for Reader<B> {
    fn clone(&self) -> Self {
        Reader {
            fxb_type: self.fxb_type,
            width: self.width,
            address: self.address,
            buffer: self.buffer.shallow_copy(),
        }
    }
}

impl<B: Buffer> Default for Reader<B> {
    fn default() -> Self {
        Reader {
            fxb_type: FlexBufferType::default(),
            width: BitWidth::default(),
            address: usize::default(),
            buffer: B::empty(),
        }
    }
}

// manual implementation of Debug because buffer slice can't be automatically displayed
impl<B> std::fmt::Debug for Reader<B> {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        // skips buffer field
        f.debug_struct("Reader")
            .field("fxb_type", &self.fxb_type)
            .field("width", &self.width)
            .field("address", &self.address)
            .finish()
    }
}

macro_rules! try_cast_fn {
    ($name: ident, $full_width: ident, $Ty: ident) => {
        pub fn $name(&self) -> $Ty {
            self.$full_width().try_into().unwrap_or_default()
        }
    };
}

fn safe_sub(a: usize, b: usize) -> Result<usize, Error> {
    a.checked_sub(b).ok_or(Error::FlexbufferOutOfBounds)
}

fn deref_offset(buffer: &[u8], address: usize, width: BitWidth) -> Result<usize, Error> {
    let off = read_usize(buffer, address, width);
    safe_sub(address, off)
}

impl<B: Buffer> Reader<B> {
    fn new(
        buffer: B,
        mut address: usize,
        mut fxb_type: FlexBufferType,
        width: BitWidth,
        parent_width: BitWidth,
    ) -> Result<Self, Error> {
        if fxb_type.is_reference() {
            address = deref_offset(&buffer, address, parent_width)?;
            // Indirects were dereferenced.
            if let Some(t) = fxb_type.to_direct() {
                fxb_type = t;
            }
        }
        Ok(Reader { address, fxb_type, width, buffer })
    }

    /// Parses the flexbuffer from the given buffer. Assumes the flexbuffer root is the last byte
    /// of the buffer.
    pub fn get_root(buffer: B) -> Result<Self, Error> {
        let end = buffer.len();
        if end < 3 {
            return Err(Error::FlexbufferOutOfBounds);
        }
        // Last byte is the root width.
        let root_width = BitWidth::from_nbytes(buffer[end - 1]).ok_or(Error::InvalidRootWidth)?;
        // Second last byte is root type.
        let (fxb_type, width) = unpack_type(buffer[end - 2])?;
        // Location of root data. (BitWidth bits before root type)
        let address = safe_sub(end - 2, root_width.n_bytes())?;
        Self::new(buffer, address, fxb_type, width, root_width)
    }

    /// Convenience function to get the underlying buffer. By using `shallow_copy`, this preserves
    /// the lifetime that the underlying buffer has.
    pub fn buffer(&self) -> B {
        self.buffer.shallow_copy()
    }

    /// Returns the FlexBufferType of this Reader.
    pub fn flexbuffer_type(&self) -> FlexBufferType {
        self.fxb_type
    }

    /// Returns the bitwidth of this Reader.
    pub fn bitwidth(&self) -> BitWidth {
        self.width
    }

    /// Returns the length of the Flexbuffer. If the type has no length, or if an error occurs,
    /// 0 is returned.
    pub fn length(&self) -> usize {
        if let Some(len) = self.fxb_type.fixed_length_vector_length() {
            len
        } else if self.fxb_type.has_length_slot() && self.address >= self.width.n_bytes() {
            read_usize(&self.buffer, self.address - self.width.n_bytes(), self.width)
        } else {
            0
        }
    }
    /// Returns true if the flexbuffer is aligned to 8 bytes. This guarantees, for valid
    /// flexbuffers, that the data is correctly aligned in memory and slices can be read directly
    /// e.g. with `get_f64s` or `get_i16s`.
    #[inline]
    pub fn is_aligned(&self) -> bool {
        (self.buffer.as_ptr() as usize).rem(8) == 0
    }

    as_default!(as_vector, get_vector, VectorReader<B>);
    as_default!(as_map, get_map, MapReader<B>);

    fn expect_type(&self, ty: FlexBufferType) -> Result<(), Error> {
        if self.fxb_type == ty {
            Ok(())
        } else {
            Err(Error::UnexpectedFlexbufferType { expected: ty, actual: self.fxb_type })
        }
    }
    fn expect_bw(&self, bw: BitWidth) -> Result<(), Error> {
        if self.width == bw {
            Ok(())
        } else {
            Err(Error::UnexpectedBitWidth { expected: bw, actual: self.width })
        }
    }

    /// Directly reads a slice of type `T` where `T` is one of `u8,u16,u32,u64,i8,i16,i32,i64,f32,f64`.
    /// Returns Err if the type, bitwidth, or memory alignment does not match. Since the bitwidth is
    /// dynamic, its better to use a VectorReader unless you know your data and performance is critical.
    #[cfg(target_endian = "little")]
    #[deprecated(
        since = "0.3.0",
        note = "This function is unsafe - if this functionality is needed use `Reader::buffer::align_to`"
    )]
    pub fn get_slice<T: ReadLE>(&self) -> Result<&[T], Error> {
        if self.flexbuffer_type().typed_vector_type() != T::VECTOR_TYPE.typed_vector_type() {
            self.expect_type(T::VECTOR_TYPE)?;
        }
        if self.bitwidth().n_bytes() != std::mem::size_of::<T>() {
            self.expect_bw(T::WIDTH)?;
        }
        let end = self.address + self.length() * std::mem::size_of::<T>();
        let slice: &[u8] =
            self.buffer.get(self.address..end).ok_or(Error::FlexbufferOutOfBounds)?;

        // `align_to` is required because the point of this function is to directly hand back a
        // slice of scalars. This can fail because Rust's default allocator is not 16byte aligned
        // (though in practice this only happens for small buffers).
        let (pre, mid, suf) = unsafe { slice.align_to::<T>() };
        if pre.is_empty() && suf.is_empty() {
            Ok(mid)
        } else {
            Err(Error::AlignmentError)
        }
    }

    /// Returns the value of the reader if it is a boolean.
    /// Otherwise Returns error.
    pub fn get_bool(&self) -> Result<bool, Error> {
        self.expect_type(FlexBufferType::Bool)?;
        Ok(self.buffer[self.address..self.address + self.width.n_bytes()].iter().any(|&b| b != 0))
    }

    /// Gets the length of the key if this type is a key.
    ///
    /// Otherwise, returns an error.
    #[inline]
    fn get_key_len(&self) -> Result<usize, Error> {
        self.expect_type(FlexBufferType::Key)?;
        let (length, _) = self.buffer[self.address..]
            .iter()
            .enumerate()
            .find(|(_, &b)| b == b'\0')
            .unwrap_or((0, &0));
        Ok(length)
    }

    /// Retrieves the string value up until the first `\0` character.
    pub fn get_key(&self) -> Result<B::BufferString, Error> {
        let bytes = self
            .buffer
            .slice(self.address..self.address + self.get_key_len()?)
            .ok_or(Error::IndexOutOfBounds)?;
        Ok(bytes.buffer_str()?)
    }

    pub fn get_blob(&self) -> Result<Blob<B>, Error> {
        self.expect_type(FlexBufferType::Blob)?;
        Ok(Blob(
            self.buffer
                .slice(self.address..self.address + self.length())
                .ok_or(Error::IndexOutOfBounds)?,
        ))
    }

    pub fn as_blob(&self) -> Blob<B> {
        self.get_blob().unwrap_or_else(|_| Blob(B::empty()))
    }

    /// Retrieves str pointer, errors if invalid UTF-8, or the provided index
    /// is out of bounds.
    pub fn get_str(&self) -> Result<B::BufferString, Error> {
        self.expect_type(FlexBufferType::String)?;
        let bytes = self.buffer.slice(self.address..self.address + self.length());
        Ok(bytes.ok_or(Error::ReadUsizeOverflowed)?.buffer_str()?)
    }

    fn get_map_info(&self) -> Result<(usize, BitWidth), Error> {
        self.expect_type(FlexBufferType::Map)?;
        if 3 * self.width.n_bytes() >= self.address {
            return Err(Error::FlexbufferOutOfBounds);
        }
        let keys_offset_address = self.address - 3 * self.width.n_bytes();
        let keys_width = {
            let kw_addr = self.address - 2 * self.width.n_bytes();
            let kw = read_usize(&self.buffer, kw_addr, self.width);
            BitWidth::from_nbytes(kw).ok_or(Error::InvalidMapKeysVectorWidth)
        }?;
        Ok((keys_offset_address, keys_width))
    }

    pub fn get_map(&self) -> Result<MapReader<B>, Error> {
        let (keys_offset_address, keys_width) = self.get_map_info()?;
        let keys_address = deref_offset(&self.buffer, keys_offset_address, self.width)?;
        // TODO(cneo): Check that vectors length equals keys length.
        Ok(MapReader {
            buffer: self.buffer.shallow_copy(),
            values_address: self.address,
            values_width: self.width,
            keys_address,
            keys_width,
            length: self.length(),
        })
    }

    /// Tries to read a FlexBufferType::UInt. Returns Err if the type is not a UInt or if the
    /// address is out of bounds.
    pub fn get_u64(&self) -> Result<u64, Error> {
        self.expect_type(FlexBufferType::UInt)?;
        let cursor = self.buffer.get(self.address..self.address + self.width.n_bytes());
        match self.width {
            BitWidth::W8 => cursor.map(|s| s[0] as u8).map(Into::into),
            BitWidth::W16 => {
                cursor.and_then(|s| s.try_into().ok()).map(<u16>::from_le_bytes).map(Into::into)
            }
            BitWidth::W32 => {
                cursor.and_then(|s| s.try_into().ok()).map(<u32>::from_le_bytes).map(Into::into)
            }
            BitWidth::W64 => cursor.and_then(|s| s.try_into().ok()).map(<u64>::from_le_bytes),
        }
        .ok_or(Error::FlexbufferOutOfBounds)
    }
    /// Tries to read a FlexBufferType::Int. Returns Err if the type is not a UInt or if the
    /// address is out of bounds.
    pub fn get_i64(&self) -> Result<i64, Error> {
        self.expect_type(FlexBufferType::Int)?;
        let cursor = self.buffer.get(self.address..self.address + self.width.n_bytes());
        match self.width {
            BitWidth::W8 => cursor.map(|s| s[0] as i8).map(Into::into),
            BitWidth::W16 => {
                cursor.and_then(|s| s.try_into().ok()).map(<i16>::from_le_bytes).map(Into::into)
            }
            BitWidth::W32 => {
                cursor.and_then(|s| s.try_into().ok()).map(<i32>::from_le_bytes).map(Into::into)
            }
            BitWidth::W64 => cursor.and_then(|s| s.try_into().ok()).map(<i64>::from_le_bytes),
        }
        .ok_or(Error::FlexbufferOutOfBounds)
    }
    /// Tries to read a FlexBufferType::Float. Returns Err if the type is not a UInt, if the
    /// address is out of bounds, or if its a f16 or f8 (not currently supported).
    pub fn get_f64(&self) -> Result<f64, Error> {
        self.expect_type(FlexBufferType::Float)?;
        let cursor = self.buffer.get(self.address..self.address + self.width.n_bytes());
        match self.width {
            BitWidth::W8 | BitWidth::W16 => return Err(Error::InvalidPackedType),
            BitWidth::W32 => {
                cursor.and_then(|s| s.try_into().ok()).map(f32_from_le_bytes).map(Into::into)
            }
            BitWidth::W64 => cursor.and_then(|s| s.try_into().ok()).map(f64_from_le_bytes),
        }
        .ok_or(Error::FlexbufferOutOfBounds)
    }
    pub fn as_bool(&self) -> bool {
        use FlexBufferType::*;
        match self.fxb_type {
            Bool => self.get_bool().unwrap_or_default(),
            UInt => self.as_u64() != 0,
            Int => self.as_i64() != 0,
            Float => self.as_f64().abs() > std::f64::EPSILON,
            String | Key => !self.as_str().is_empty(),
            Null => false,
            Blob => self.length() != 0,
            ty if ty.is_vector() => self.length() != 0,
            _ => unreachable!(),
        }
    }
    /// Returns a u64, casting if necessary. For Maps and Vectors, their length is
    /// returned. If anything fails, 0 is returned.
    pub fn as_u64(&self) -> u64 {
        match self.fxb_type {
            FlexBufferType::UInt => self.get_u64().unwrap_or_default(),
            FlexBufferType::Int => {
                self.get_i64().unwrap_or_default().try_into().unwrap_or_default()
            }
            FlexBufferType::Float => self.get_f64().unwrap_or_default() as u64,
            FlexBufferType::String => {
                if let Ok(s) = self.get_str() {
                    if let Ok(f) = u64::from_str(&s) {
                        return f;
                    }
                }
                0
            }
            _ if self.fxb_type.is_vector() => self.length() as u64,
            _ => 0,
        }
    }
    try_cast_fn!(as_u32, as_u64, u32);
    try_cast_fn!(as_u16, as_u64, u16);
    try_cast_fn!(as_u8, as_u64, u8);

    /// Returns an i64, casting if necessary. For Maps and Vectors, their length is
    /// returned. If anything fails, 0 is returned.
    pub fn as_i64(&self) -> i64 {
        match self.fxb_type {
            FlexBufferType::Int => self.get_i64().unwrap_or_default(),
            FlexBufferType::UInt => {
                self.get_u64().unwrap_or_default().try_into().unwrap_or_default()
            }
            FlexBufferType::Float => self.get_f64().unwrap_or_default() as i64,
            FlexBufferType::String => {
                if let Ok(s) = self.get_str() {
                    if let Ok(f) = i64::from_str(&s) {
                        return f;
                    }
                }
                0
            }
            _ if self.fxb_type.is_vector() => self.length() as i64,
            _ => 0,
        }
    }
    try_cast_fn!(as_i32, as_i64, i32);
    try_cast_fn!(as_i16, as_i64, i16);
    try_cast_fn!(as_i8, as_i64, i8);

    /// Returns an f64, casting if necessary. For Maps and Vectors, their length is
    /// returned. If anything fails, 0 is returned.
    pub fn as_f64(&self) -> f64 {
        match self.fxb_type {
            FlexBufferType::Int => self.get_i64().unwrap_or_default() as f64,
            FlexBufferType::UInt => self.get_u64().unwrap_or_default() as f64,
            FlexBufferType::Float => self.get_f64().unwrap_or_default(),
            FlexBufferType::String => {
                if let Ok(s) = self.get_str() {
                    if let Ok(f) = f64::from_str(&s) {
                        return f;
                    }
                }
                0.0
            }
            _ if self.fxb_type.is_vector() => self.length() as f64,
            _ => 0.0,
        }
    }
    pub fn as_f32(&self) -> f32 {
        self.as_f64() as f32
    }

    /// Returns empty string if you're not trying to read a string.
    pub fn as_str(&self) -> B::BufferString {
        match self.fxb_type {
            FlexBufferType::String => self.get_str().unwrap_or_else(|_| B::empty_str()),
            FlexBufferType::Key => self.get_key().unwrap_or_else(|_| B::empty_str()),
            _ => B::empty_str(),
        }
    }

    pub fn get_vector(&self) -> Result<VectorReader<B>, Error> {
        if !self.fxb_type.is_vector() {
            self.expect_type(FlexBufferType::Vector)?;
        };
        Ok(VectorReader { reader: self.clone(), length: self.length() })
    }
}

impl<B: Buffer> fmt::Display for Reader<B> {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        use FlexBufferType::*;
        match self.flexbuffer_type() {
            Null => write!(f, "null"),
            UInt => write!(f, "{}", self.as_u64()),
            Int => write!(f, "{}", self.as_i64()),
            Float => write!(f, "{}", self.as_f64()),
            Key | String => write!(f, "{:?}", &self.as_str() as &str),
            Bool => write!(f, "{}", self.as_bool()),
            Blob => write!(f, "blob"),
            Map => {
                write!(f, "{{")?;
                let m = self.as_map();
                let mut pairs = m.iter_keys().zip(m.iter_values());
                if let Some((k, v)) = pairs.next() {
                    write!(f, "{:?}: {}", &k as &str, v)?;
                    for (k, v) in pairs {
                        write!(f, ", {:?}: {}", &k as &str, v)?;
                    }
                }
                write!(f, "}}")
            }
            t if t.is_vector() => {
                write!(f, "[")?;
                let mut elems = self.as_vector().iter();
                if let Some(first) = elems.next() {
                    write!(f, "{}", first)?;
                    for e in elems {
                        write!(f, ", {}", e)?;
                    }
                }
                write!(f, "]")
            }
            _ => unreachable!("Display not implemented for {:?}", self),
        }
    }
}

// TODO(cneo): Use <f..>::from_le_bytes when we move past rustc 1.39.
fn f32_from_le_bytes(bytes: [u8; 4]) -> f32 {
    let bits = <u32>::from_le_bytes(bytes);
    <f32>::from_bits(bits)
}

fn f64_from_le_bytes(bytes: [u8; 8]) -> f64 {
    let bits = <u64>::from_le_bytes(bytes);
    <f64>::from_bits(bits)
}

fn read_usize(buffer: &[u8], address: usize, width: BitWidth) -> usize {
    let cursor = &buffer[address..];
    match width {
        BitWidth::W8 => cursor[0] as usize,
        BitWidth::W16 => cursor
            .get(0..2)
            .and_then(|s| s.try_into().ok())
            .map(<u16>::from_le_bytes)
            .unwrap_or_default() as usize,
        BitWidth::W32 => cursor
            .get(0..4)
            .and_then(|s| s.try_into().ok())
            .map(<u32>::from_le_bytes)
            .unwrap_or_default() as usize,
        BitWidth::W64 => cursor
            .get(0..8)
            .and_then(|s| s.try_into().ok())
            .map(<u64>::from_le_bytes)
            .unwrap_or_default() as usize,
    }
}

fn unpack_type(ty: u8) -> Result<(FlexBufferType, BitWidth), Error> {
    let w = BitWidth::try_from(ty & 3u8).map_err(|_| Error::InvalidPackedType)?;
    let t = FlexBufferType::try_from(ty >> 2).map_err(|_| Error::InvalidPackedType)?;
    Ok((t, w))
}
