//! Implement the Field structure.

/// Structure representing the location of a register.
/// This structure does not provide any kind of safety, it
/// is only descriptif.
///
/// Fields represent a continuous set of bits in an alligned
/// `u32`. This means that the address is a multiple of 4, 
/// the offset is < 32, and offset + size <= 32.
///
/// The constructor of the is class ensure that the field is 
/// always inside the `u32` at `address`, and that `address`
/// is alligned.
pub struct Field {
    /// The address of the discribed field.
    address: usize,
    /// The offset of the first bit of the field relative to the
    /// address.
    offset: usize,
    /// The size of the field in bit.
    size: usize,
    /// The mask for the field in the `u32` at `address`.
    mask: u32
}

// TODO: test
impl Field {
    /// Constructor for a Field.
    /// The field is defined by the `size` bits at `offset` bits after
    /// `address`. (**`address` is in bytes, not bits**)
    pub const fn new(address: usize, offset: usize, size: usize) -> Self {
        // align the address 
        let offset = offset + 8 * (address % 4);
        let address = address - (address % 4);
        // make sure the field is in the u32 at address
        let address = address + 4 * (offset / 32);
        let offset = offset % 32;
        // make sure the field does not overlap with the next u32
        if offset + size > 32 {
            panic!("A field can not overlap two aligned `u32`");
        }

        let mask = Self::compute_mask(offset, size);

        Self { address, offset, size, mask }
    }

    /// Compute mask for the field.
    const fn compute_mask(offset: usize, size: usize) -> u32 {
        let mut mask = 0u32;
        // Const functions don't allow for loops.
        let mut i = 0;
        while i < size {
            mask <<= 1;
            mask |= 1;
            i += 1;
        }
        mask <<= offset;
        mask
    }

    /// Return the address of the `u32` containing the field.
    pub const fn get_address(&self) -> usize {
        self.address
    }

    /// Return the offset of the field in relation to the address, in bits.
    #[allow(dead_code)]
    pub const fn get_offset(&self) -> usize {
        self.offset
    }

    /// Return the size of the field in bit.
    #[allow(dead_code)]
    pub const fn get_size(&self) -> usize {
        self.size
    }

    /// Return the mask of the field in the `u32` at `address`.
    pub const fn get_mask(&self) -> u32 {
        self.mask
    }
}
