//! Defines the `*RecordBox` APIs and associated types

use crate::error::Result;


/// An en-/decodable record
pub trait Record where Self: Sized {
    /// The associated error
    type Error: std::error::Error;

    /// Encodes `self` into a byte representation
    fn encode(&self) -> std::result::Result<Vec<u8>, Self::Error>;
    /// Decodes some data as `Self`
    fn decode<D>(encoded: D) -> std::result::Result<Self, Self::Error> where D: AsRef<[u8]>;
}


/// A record ID (e.g. an associated name etc.)
pub trait RecordId {
    /// The associated error
    type Error: std::error::Error;

    /// Encodes `self` into a byte representation
    fn encode(&self) -> std::result::Result<Vec<u8>, Self::Error>;
}
/// A unique record ID
/// 
/// # Warning
/// If we say "unique" we __really mean it__. If it ever happens that two different records have the same ID, it is usually
/// trivial to break the underlying encryption completely (see also "nonce reuse").
pub trait UniqueRecordId: RecordId {
    /* No additional members */
}
/// A unique record ID that can be directly converted into a nonce without invoking a one-way compression hashing etc. The
/// idea behind this trait is to implement a cheap and fast mapping from an ID like e.g. a counter into a nonce.
/// 
/// # Warning
/// If we say "unique" we __really mean it__. If it ever happens that two different records have the same ID, it is usually
/// trivial to break the underlying encryption completely (see also "nonce reuse").
/// 
/// # Warning
/// Usually nonces are pretty short (8 to 12 bytes). Therefore, to avoid collisions, you _MUST_ ensure that you have an
/// injective mapping from each record ID to the nonce. In other words: it _MUST_ be impossible that two different
/// objects result in the same nonce.
pub trait FastUniqueRecordId: UniqueRecordId {
    /// The associated error
    type Error: std::error::Error;

    /// Encodes `self` directly into a byte representation without invoking a one-way compression
    fn encode_fast<const N: usize>(&self) -> std::result::Result<[u8; N], <Self as FastUniqueRecordId>::Error>;
}


/// A deterministic SIV-based record box
/// 
/// # Note
/// This box seals the records deterministically; this means that the same `record:address` will always result in the same
/// ciphertext. While this is not a problem for the underlying cryptopgraphy, the simple knowledge that two records are the
/// same might be enough to gain secret information or even break a protocol.
pub trait Recordbox {
    /// The name of the implementation
    fn name(&self) -> &'static str;
    
    /// Seals the record deterministically (i.e. the same `record:address` will result in the same ciphertext)
    /// 
    /// # Note
    /// This box seals the records deterministically; this means that the same `record:address` will always result in the
    /// same ciphertext. While this is not a problem for the underlying cryptopgraphy, the simple knowledge that two
    /// records are the same might be enough to gain secret information or even break a protocol.
    fn seal<R, I>(&self, record: R, id: I) -> Result<Vec<u8>> where R: Record, I: RecordId;
    /// Opens the sealed record
    fn open<C, I, R>(&self, ciphertext: C, address: I) -> Result<R> where C: AsRef<[u8]>, I: RecordId, R: Record;
}
/// A randomized record box derives an intermediate key from the unique ID to allow arbitrarily long IDs
/// 
/// # Warning
/// This box requires a unique ID per record. If it ever happens that two different records have the same ID, it is usually
/// trivial to break the underlying encryption completely (see also "nonce reuse").
pub trait UniqueRecordbox {
    /// The name of the implementation
    fn name(&self) -> &'static str;

    /// Seals the record
    /// 
    /// # Warning
    /// This box requires a unique ID per record. If it ever happens that two different records have the same ID, it is
    /// usually trivial to break the underlying encryption completely (see also "nonce reuse").
    fn seal<R, I>(&self, record: R, id: I) -> Result<Vec<u8>> where R: Record, I: UniqueRecordId;
    /// Opens the sealed record
    fn open<C, I, R>(&self, ciphertext: C, id: I) -> Result<R> where C: AsRef<[u8]>, I: UniqueRecordId, R: Record;
}
/// A randomized fast record box which converts the ID into a directly usable nonce (without the intermediate key
/// derivation step).
/// 
/// # Warning
/// This box requires a unique ID per record. If it ever happens that two different records have the same ID, it is usually
/// trivial to break the underlying encryption completely (see also "nonce reuse").
pub trait FastRecordbox {
    /// The name of the implementation
    fn name(&self) -> &'static str;
    /// The nonce size used by the underlying implementation (i.e. the size `N` that will be passed to the `id`'s
    /// `encode_direct<N>` implementation)
    fn nonce_len(&self) -> usize;

    /// Seals the record
    /// 
    /// # Warning
    /// This box requires a unique ID per record. If it ever happens that two different records have the same ID, it is
    /// usually trivial to break the underlying encryption completely (see also "nonce reuse").
    fn seal<R, I>(&self, record: R, id: I) -> Result<Vec<u8>> where R: Record, I: FastUniqueRecordId;
    /// Opens the sealed record
    fn open<C, I, R>(&self, ciphertext: C, id: I) -> Result<R> where C: AsRef<[u8]>, I: FastUniqueRecordId, R: Record;
}
