use crate::{
    error::EResult,
    io::{managed::FILE, Read},
};

pub trait GetChar {
    fn getc(&mut self) -> EResult<Option<u8>>;
}

pub struct ScanReader<'a, G: GetChar> {
    getter: &'a mut G,
    buffer: Option<u8>,
    consumed_count: usize,
}

impl GetChar for FILE {
    fn getc(&mut self) -> EResult<Option<u8>> {
        let mut buf = [0];
        match self.read(&mut buf) {
            EResult::Ok(1) => EResult::Ok(Some(buf[0])),
            EResult::Ok(_) => EResult::Ok(None),
            EResult::Err(err) => EResult::Err(err),
        }
    }
}

impl<'a, I: Iterator<Item = &'a u8>> GetChar for I {
    fn getc(&mut self) -> EResult<Option<u8>> {
        EResult::Ok(self.next().copied())
    }
}

impl<'a, G: GetChar> ScanReader<'a, G> {
    pub fn new(getter: &'a mut G) -> Self {
        Self {
            getter,
            buffer: None,
            consumed_count: 0,
        }
    }

    fn getc(&mut self) -> EResult<Option<u8>> {
        match self.getter.getc() {
            EResult::Ok(Some(n)) => {
                self.consumed_count += 1;
                EResult::Ok(Some(n))
            }
            e => e,
        }
    }

    pub fn consumed_count(&self) -> usize {
        self.consumed_count
    }

    pub fn peek(&mut self) -> EResult<Option<u8>> {
        if self.buffer.is_none() {
            self.buffer = self.getc()?;
        }
        EResult::Ok(self.buffer)
    }

    pub fn next(&mut self) -> EResult<Option<u8>> {
        match self.buffer.take() {
            Some(v) => EResult::Ok(Some(v)),
            None => self.getc(),
        }
    }
}
