use arduino_hal::eeprom::Eeprom;

pub const SECRET_KEY_MAX_LEN: u16 = 32;
pub const SECRET_KEY_NAME_LEN: u16 = 16;
pub const SECRET_KEY_FULL_LEN: u16 = SECRET_KEY_MAX_LEN + SECRET_KEY_NAME_LEN;
pub const ENDL: u8 = 0;

pub struct Tokens<'a> {
    mem: &'a mut Eeprom,
    pub current: Option<u16>,
    capacity: u16,
}

impl<'a> Tokens<'a> {
    pub fn new(mem: &'a mut Eeprom) -> Self {
        let capacity = mem.capacity() / SECRET_KEY_FULL_LEN;

        let mut tokens = Tokens {
            mem,
            capacity,
            current: None,
        };
        tokens.current = tokens.first();

        tokens
    }

    pub fn search_free(&self) -> Option<u16> {
        for n in 0..self.capacity {
            let index = SECRET_KEY_FULL_LEN * n;
            if self.mem.read_byte(index) == ENDL {
                return Some(n);
            }
        }

        None
    }

    fn first(&self) -> Option<u16> {
        for n in 0..self.capacity {
            let index = SECRET_KEY_FULL_LEN * n;
            if self.mem.read_byte(index) != ENDL {
                return Some(n);
            }
        }
        return None;
    }

    pub fn next(&mut self) -> Option<u16> {
        let mut index = self.current.unwrap();
        for _ in 0..self.capacity {
            index = (index + 1) % self.capacity;
            if self.mem.read_byte(index * SECRET_KEY_FULL_LEN) != ENDL {
                self.current = Some(index);
                return Some(index);
            }
        }
        None
    }

    pub fn prev(&mut self) -> Option<u16> {
        let mut index = self.current.unwrap();
        for _ in 0..self.capacity {
            index = (index - 1) % self.capacity;
            if self.mem.read_byte(index * SECRET_KEY_FULL_LEN) != ENDL {
                self.current = Some(index);
                return Some(index);
            }
        }
        None
    }

    pub fn read(&self, index: u16, name: &mut [u8], key: &mut [u8]) -> Result<(usize, usize), ()> {
        name.fill(0);
        key.fill(0);
        let index_name = index * SECRET_KEY_FULL_LEN;
        let index_key = (index * SECRET_KEY_FULL_LEN) + SECRET_KEY_NAME_LEN;

        self.mem.read(index_name, name).map_err(|_| ())?;
        self.mem.read(index_key, key).map_err(|_| ())?;

        let len_name = name
            .iter()
            .position(|&n| n == 0)
            .unwrap_or(SECRET_KEY_NAME_LEN as usize);
        let len_key = key
            .iter()
            .position(|&n| n == 0)
            .unwrap_or(SECRET_KEY_MAX_LEN as usize);
        Ok((len_name, len_key))
    }

    pub fn write(&mut self, index: u16, name: &[u8], key: &[u8]) -> Result<u16, ()> {
        let index_name = index * SECRET_KEY_FULL_LEN;
        let index_key = (index * SECRET_KEY_FULL_LEN) + SECRET_KEY_NAME_LEN;

        self.mem.write(index_name, name).map_err(|_| ())?;
        self.mem.write(index_key, key).map_err(|_| ())?;

        Ok(index)
    }

    pub fn delete(&mut self, index: u16) -> Option<u16> {
        // The Arduino's EEPROM memory has a maximum number of write cycles.
        // To keep writes to a minimum, only the first byte of the token name is set to 0
        // and wipe all the secret key
        let index_token = index * SECRET_KEY_FULL_LEN;
        let index_key_start = index_token + SECRET_KEY_NAME_LEN;
        let index_key_end = index_key_start + SECRET_KEY_MAX_LEN - 1;
        self.mem.write_byte(index_token, ENDL);
        for index in index_key_start..index_key_end {
            self.mem.write_byte(index, ENDL);
        }
        Some(index)
    }

    pub fn soft_wipe_all_tokens(&mut self) -> u8 {
        let mut inc = 0;

        for n in 0..self.capacity {
            let index = SECRET_KEY_FULL_LEN * n;
            if self.mem.read_byte(index) != ENDL {
                self.delete(index);
                inc += 1;
            }
        }
        inc
    }

    pub fn hard_wipe_all_tokens(&mut self) {
        for index in 0..self.mem.capacity() {
            self.mem.write_byte(index, ENDL);
        }
    }
}