use core::ops::{Deref, DerefMut};
use core::ptr;
use core::sync::atomic;

use super::error::Error;

/// A seed, which a key pair can be derived from.
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
pub struct Seed([u8; Seed::BYTES]);

impl From<[u8; 32]> for Seed {
    fn from(seed: [u8; 32]) -> Self {
        Seed(seed)
    }
}

impl Seed {
    /// Number of raw bytes in a seed.
    pub const BYTES: usize = 32;

    /// Creates a seed from raw bytes.
    pub fn new(seed: [u8; Seed::BYTES]) -> Self {
        Seed(seed)
    }

    /// Creates a seed from a slice.
    pub fn from_slice(seed: &[u8]) -> Result<Self, Error> {
        let mut seed_ = [0u8; Seed::BYTES];
        if seed.len() != seed_.len() {
            return Err(Error::InvalidSeed);
        }
        seed_.copy_from_slice(seed);
        Ok(Seed::new(seed_))
    }

    /// Tentatively overwrite the content of the seed with zeros.
    pub fn wipe(self) {
        Mem::wipe(self.0)
    }
}

#[cfg(feature = "random")]
impl Default for Seed {
    /// Generates a random seed.
    fn default() -> Self {
        let mut seed = [0u8; Seed::BYTES];
        getrandom::getrandom(&mut seed).expect("RNG failure");
        Seed(seed)
    }
}

#[cfg(feature = "random")]
impl Seed {
    /// Generates a random seed.
    pub fn generate() -> Self {
        Seed::default()
    }
}

impl Deref for Seed {
    type Target = [u8; Seed::BYTES];

    /// Returns a seed as raw bytes.
    fn deref(&self) -> &Self::Target {
        &self.0
    }
}

impl DerefMut for Seed {
    /// Returns a seed as mutable raw bytes.
    fn deref_mut(&mut self) -> &mut Self::Target {
        &mut self.0
    }
}

pub(crate) struct Mem;

impl Mem {
    #[inline]
    pub fn wipe<T: Default>(mut x: impl AsMut<[T]>) {
        let x = x.as_mut();
        for i in 0..x.len() {
            unsafe {
                ptr::write_volatile(x.as_mut_ptr().add(i), T::default());
            }
        }
        atomic::compiler_fence(atomic::Ordering::SeqCst);
        atomic::fence(atomic::Ordering::SeqCst);
    }
}
