Documentation
//! Static Record Builder

use crate::error::BuilderError;
use crate::RecordBuffer;
use crate::WrappedRecordBuffer;

use ytls_traits::ClientHandshakeFinishedBuilder;
use ytls_traits::ClientHelloBuilder;
use ytls_traits::EncryptedExtensionsBuilder;
use ytls_traits::HandshakeBuilder;
use ytls_traits::ServerCertificateVerifyBuilder;
use ytls_traits::ServerCertificatesBuilder;
use ytls_traits::ServerHandshakeFinishedBuilder;
use ytls_traits::ServerHelloBuilder;
use ytls_traits::WrappedApplicationBuilder;
use ytls_traits::WrappedHandshakeBuilder;

/// Provides statically allocated Record Builder based on worst case
/// estimation of the maximum size of record.
/// Typically record sizes need to be also limited based on the record
/// limit extension but this has to be done in runtime.
#[derive(Debug, PartialEq)]
pub struct StaticRecordBuilder<const N: usize> {
    rec_buf: RecordBuffer<N>,
}

use crate::builder::b_wrap_application_data::BufStaticAppData;
use crate::WrappedAppRecordBuffer;

/// Same as [`StaticRecordBuilder`] but in the Application context instead of handshake
#[derive(Debug, PartialEq)]
pub struct WrappedAppStaticRecordBuilder<const N: usize> {
    rec_buf: WrappedAppRecordBuffer<N>,
}

impl<const N: usize> WrappedApplicationBuilder for WrappedAppStaticRecordBuilder<N> {
    type Error = BuilderError;
    /// Construct handshake server finished
    fn application_data(s: &[u8]) -> Result<Self, Self::Error> {
        Ok(Self {
            rec_buf: WrappedAppRecordBuffer::<N>::AppData(
                BufStaticAppData::<N>::static_from_untyped(s)?,
            ),
        })
    }
    #[inline]
    fn as_disjoint_mut_for_aead(&mut self) -> Result<[&mut [u8]; 2], Self::Error> {
        match self.rec_buf {
            WrappedAppRecordBuffer::AppData(ref mut s) => s.as_disjoint_mut_for_aead(),
        }
    }
    #[inline]
    fn set_auth_tag(&mut self, new_tag: &[u8; 16]) {
        match self.rec_buf {
            WrappedAppRecordBuffer::AppData(ref mut s) => s.set_auth_tag(new_tag),
        }
    }
    #[inline]
    fn as_ciphertext_mut(&mut self) -> &mut [u8] {
        match self.rec_buf {
            WrappedAppRecordBuffer::AppData(ref mut s) => s.as_ciphertext_mut(),
        }
    }
    #[inline]
    fn wrapped_hash_header_ref(&self) -> [u8; 5] {
        match self.rec_buf {
            WrappedAppRecordBuffer::AppData(ref s) => s.wrapped_hash_header_ref(),
        }
    }
    #[inline]
    fn as_hashing_context_ref(&self) -> &[u8] {
        match self.rec_buf {
            WrappedAppRecordBuffer::AppData(ref s) => &s.as_hashing_context_ref(),
        }
    }
    #[inline]
    fn as_encoded_bytes(&self) -> &[u8] {
        match self.rec_buf {
            WrappedAppRecordBuffer::AppData(ref s) => &s.as_encoded_bytes(),
        }
    }
}

/// Same as [`StaticRecordBuilder`] but provides wrapping into TLS1.2 AppData
/// which is typically used when the records are AEAD'd to preserve compatibility
/// with the middleboxes.
#[derive(Debug, PartialEq)]
pub struct WrappedStaticRecordBuilder<const N: usize> {
    rec_buf: WrappedRecordBuffer<N>,
}

use super::b_dhs_client_handshake_finished::BufStaticClientHandshakeFinished;
use super::b_dhs_encrypted_extensions::BufStaticEncryptedExtensions;
use super::b_dhs_server_certificate::BufStaticServerCertificates;
use super::b_dhs_server_certificate_verify::BufStaticServerCertificateVerify;
use super::b_dhs_server_handshake_finished::BufStaticServerHandshakeFinished;

impl<const N: usize> WrappedHandshakeBuilder for WrappedStaticRecordBuilder<N> {
    type Error = BuilderError;
    /// Construct handshake client finished
    #[inline]
    fn client_handshake_finished<S: ClientHandshakeFinishedBuilder>(
        s: &S,
    ) -> Result<Self, Self::Error> {
        Ok(Self {
            rec_buf: WrappedRecordBuffer::<N>::ClientHandshakeFinished(
                BufStaticClientHandshakeFinished::<N>::static_from_untyped(s)?,
            ),
        })
    }
    /// Construct handshake server finished
    #[inline]
    fn server_handshake_finished<S: ServerHandshakeFinishedBuilder>(
        s: &S,
    ) -> Result<Self, Self::Error> {
        Ok(Self {
            rec_buf: WrappedRecordBuffer::<N>::ServerHandshakeFinished(
                BufStaticServerHandshakeFinished::<N>::static_from_untyped(s)?,
            ),
        })
    }
    /// Construct handshake server certificate/s.
    #[inline]
    fn server_certificates<S: ServerCertificatesBuilder>(s: &S) -> Result<Self, Self::Error> {
        Ok(Self {
            rec_buf: WrappedRecordBuffer::<N>::ServerCertificates(
                BufStaticServerCertificates::<N>::static_from_untyped(s)?,
            ),
        })
    }
    /// Construct handshake server certificate verify.
    #[inline]
    fn server_certificate_verify<S: ServerCertificateVerifyBuilder>(
        s: &S,
    ) -> Result<Self, Self::Error> {
        Ok(Self {
            rec_buf: WrappedRecordBuffer::<N>::ServerCertificateVerify(
                BufStaticServerCertificateVerify::<N>::static_from_untyped(s)?,
            ),
        })
    }
    /// Construct handshake encrypted extensions.
    #[inline]
    fn encrypted_extensions<S: EncryptedExtensionsBuilder>(s: &S) -> Result<Self, Self::Error> {
        Ok(Self {
            rec_buf: WrappedRecordBuffer::<N>::EncryptedExtensions(BufStaticEncryptedExtensions::<
                N,
            >::static_from_untyped(
                s
            )?),
        })
    }
    #[inline]
    fn as_disjoint_mut_for_aead(&mut self) -> Result<[&mut [u8]; 2], Self::Error> {
        match self.rec_buf {
            WrappedRecordBuffer::ServerCertificates(ref mut s) => s.as_disjoint_mut_for_aead(),
            WrappedRecordBuffer::ServerCertificateVerify(ref mut s) => s.as_disjoint_mut_for_aead(),
            WrappedRecordBuffer::EncryptedExtensions(ref mut s) => s.as_disjoint_mut_for_aead(),
            WrappedRecordBuffer::ServerHandshakeFinished(ref mut s) => s.as_disjoint_mut_for_aead(),
            WrappedRecordBuffer::ClientHandshakeFinished(ref mut s) => s.as_disjoint_mut_for_aead(),
        }
    }
    #[inline]
    fn set_auth_tag(&mut self, new_tag: &[u8; 16]) {
        match self.rec_buf {
            WrappedRecordBuffer::ServerCertificates(ref mut s) => s.set_auth_tag(new_tag),
            WrappedRecordBuffer::ServerCertificateVerify(ref mut s) => s.set_auth_tag(new_tag),
            WrappedRecordBuffer::EncryptedExtensions(ref mut s) => s.set_auth_tag(new_tag),
            WrappedRecordBuffer::ServerHandshakeFinished(ref mut s) => s.set_auth_tag(new_tag),
            WrappedRecordBuffer::ClientHandshakeFinished(ref mut s) => s.set_auth_tag(new_tag),
        }
    }
    #[inline]
    fn as_ciphertext_mut(&mut self) -> &mut [u8] {
        match self.rec_buf {
            WrappedRecordBuffer::ServerCertificates(ref mut s) => s.as_ciphertext_mut(),
            WrappedRecordBuffer::ServerCertificateVerify(ref mut s) => s.as_ciphertext_mut(),
            WrappedRecordBuffer::EncryptedExtensions(ref mut s) => s.as_ciphertext_mut(),
            WrappedRecordBuffer::ServerHandshakeFinished(ref mut s) => s.as_ciphertext_mut(),
            WrappedRecordBuffer::ClientHandshakeFinished(ref mut s) => s.as_ciphertext_mut(),
        }
    }
    #[inline]
    fn wrapped_hash_header_ref(&self) -> [u8; 5] {
        match self.rec_buf {
            WrappedRecordBuffer::ServerCertificates(ref s) => s.wrapped_hash_header_ref(),
            WrappedRecordBuffer::ServerCertificateVerify(ref s) => s.wrapped_hash_header_ref(),
            WrappedRecordBuffer::EncryptedExtensions(ref s) => s.wrapped_hash_header_ref(),
            WrappedRecordBuffer::ServerHandshakeFinished(ref s) => s.wrapped_hash_header_ref(),
            WrappedRecordBuffer::ClientHandshakeFinished(ref s) => s.wrapped_hash_header_ref(),
        }
    }
    #[inline]
    fn as_hashing_context_ref(&self) -> &[u8] {
        match self.rec_buf {
            WrappedRecordBuffer::ServerCertificates(ref s) => &s.as_hashing_context_ref(),
            WrappedRecordBuffer::ServerCertificateVerify(ref s) => &s.as_hashing_context_ref(),
            WrappedRecordBuffer::EncryptedExtensions(ref s) => &s.as_hashing_context_ref(),
            WrappedRecordBuffer::ServerHandshakeFinished(ref s) => &s.as_hashing_context_ref(),
            WrappedRecordBuffer::ClientHandshakeFinished(ref s) => &s.as_hashing_context_ref(),
        }
    }
    #[inline]
    fn as_encoded_bytes(&self) -> &[u8] {
        match self.rec_buf {
            WrappedRecordBuffer::ServerCertificates(ref s) => &s.as_encoded_bytes(),
            WrappedRecordBuffer::ServerCertificateVerify(ref s) => &s.as_encoded_bytes(),
            WrappedRecordBuffer::EncryptedExtensions(ref s) => &s.as_encoded_bytes(),
            WrappedRecordBuffer::ServerHandshakeFinished(ref s) => &s.as_encoded_bytes(),
            WrappedRecordBuffer::ClientHandshakeFinished(ref s) => &s.as_encoded_bytes(),
        }
    }
}

impl<const N: usize> HandshakeBuilder for StaticRecordBuilder<N> {
    type Error = BuilderError;
    /// Construct a handshake record featuring a Client Hello from untyped raw data.
    #[inline]
    fn client_hello_untyped<S: ClientHelloBuilder>(s: &S) -> Result<Self, Self::Error> {
        Ok(Self {
            rec_buf: RecordBuffer::<N>::ClientHello(super::b_client_hello::BufStaticClientHello::<
                N,
            >::static_from_untyped(s)?),
        })
    }
    /// Construct a Handshake record featuring a Server Hello from untyped raw data.
    #[inline]
    fn server_hello_untyped<S: ServerHelloBuilder>(s: &S) -> Result<Self, Self::Error> {
        Ok(Self {
            rec_buf: RecordBuffer::<N>::ServerHello(super::b_server_hello::BufStaticServerHello::<
                N,
            >::static_from_untyped(s)?),
        })
    }
    #[inline]
    fn as_hashing_context(&self) -> &[u8] {
        match self.rec_buf {
            RecordBuffer::ClientHello(ref h) => &h.as_hashing_context(),
            RecordBuffer::ServerHello(ref h) => &h.as_hashing_context(),
        }
    }
    #[inline]
    fn as_encoded_bytes(&self) -> &[u8] {
        match self.rec_buf {
            RecordBuffer::ClientHello(ref h) => &h.as_encoded_bytes(),
            RecordBuffer::ServerHello(ref h) => &h.as_encoded_bytes(),
        }
    }
}