use core::{cell::RefCell, fmt::Formatter};

use alloc::rc::Rc;
use embedded_storage::{ReadStorage, Storage};
use log::*;
use rmp::decode::{RmpRead, RmpReadErr};

#[derive(Debug)]
pub struct SharedFlash<S> {
    storage: Rc<RefCell<S>>
}

impl<S> Clone for SharedFlash<S> {
    fn clone(&self) -> Self {
        Self {
            storage: Rc::clone(&self.storage)
        }
    }
}

impl<S> SharedFlash<S> {
    pub fn new(storage: S) -> Self {
        Self {
            storage: Rc::new(RefCell::new(storage))
        }
    }
}

impl<S: Storage> Storage for SharedFlash<S> {
    fn write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Self::Error> {
        self.storage.borrow_mut().write(offset, bytes)
    }
}

impl<S: ReadStorage> ReadStorage for SharedFlash<S> {
    type Error = S::Error;

    fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Self::Error> {
        self.storage.borrow_mut().read(offset, bytes)
    }

    fn capacity(&self) -> usize {
        self.storage.borrow().capacity()
    }
}

#[derive(Debug)]
pub enum RangeReadError<E> {
    OutOfData,
    Storage(E)
}
impl<E: core::fmt::Debug + 'static> RmpReadErr for RangeReadError<E> {}

impl<E> core::fmt::Display for RangeReadError<E> {
    fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
        f.write_str("RmpErr")
    }
}

#[derive(Debug)]
pub struct RangeReader<S> {
    storage: S,
    start: usize,
    end: usize,
    offset: usize
}

impl<S: ReadStorage> RangeReader<S> {
    pub const fn new(storage: S, start: usize, end: usize) -> Self {
        assert!(start <= end);
        // TODO: Should add bounds checking since we will know the size of the chunk already
        Self {
            storage,
            start,
            end,
            offset: 0
        }
    }

    pub fn seek(&mut self, offset: usize) -> Result<(), RangeReadError<S::Error>> {
        self.offset += offset;
        if self.offset > self.end {
            Err(RangeReadError::OutOfData)
        } else {
            Ok(())
        }
    }

    pub fn subset(&self, size: usize) -> Result<Self, RangeReadError<S::Error>> where S: Clone + core::fmt::Debug {
        trace!("subset {:#02x}:{:#02x} -> {:#02x}:{:#02x}", self.start, self.end, self.start + self.offset, self.start + self.offset + size);
        if self.start + self.offset + size > self.end {
            Err(RangeReadError::OutOfData)
        } else {
            Ok(Self {
                storage: self.storage.clone(),
                start: self.offset + self.start,
                end: self.start + self.offset + size,
                offset: 0
            })
        }
    }
}

impl<S: ReadStorage> RmpRead for RangeReader<S> where S::Error: core::fmt::Debug + 'static {
    type Error = RangeReadError<S::Error>;

    fn read_exact_buf(&mut self, buf: &mut [u8]) -> Result<(), RangeReadError<S::Error>> {
        let pos = self.start + self.offset;
        if pos > self.end {
            Err(RangeReadError::OutOfData)
        } else {
            assert!(pos + buf.len() <= self.end);
            match self.storage.read(pos as u32, buf) {
                Ok(_) => {
                    self.offset += buf.len();
                    Ok(())
                },
                Err(err) => Err(RangeReadError::Storage(err))
            }
        }
    }
}
