pub use signature::Verifier;

use generic_array::GenericArray;

use super::EMPTY_CTX;
use super::ParameterSet;

use crate::ffi;
use crate::utils;
use utils::transcoding;
use utils::transcoding::AsBytes;
use utils::typenum::Unsigned;

/// Public key for signature verification.
#[derive(Clone, Debug, PartialEq)]
#[repr(transparent)]
pub struct VerifyingKey<P: ParameterSet> {
    pub(super) pk: GenericArray<u8, <P as crate::VerifyingKeyLen>::LEN>,
}

pub type PublicKey<P> = VerifyingKey<P>;

impl<P: ParameterSet> VerifyingKey<P> {
    /// Use [`Self`] to verify that the provided `signature`
    /// for a given `message` bytestring is authentic
    /// under the associated `context` bytestring.
    ///
    /// # Errors
    ///
    /// Returns [`signature::Error`] if it is inauthentic,
    /// or otherwise returns `()`.
    pub fn verify_with_ctx(
        &self,
        message: &[u8],
        context: &[u8],
        signature: &super::Signature<P>,
    ) -> Result<(), signature::Error> {
        let ret = {
            let pk = self.pk.as_ptr();
            let sig = signature.as_bytes();

            unsafe {
                P::VERIFY_FN(
                    sig.as_ptr(),
                    sig.len(),
                    message.as_ptr(),
                    message.len(),
                    context.as_ptr(),
                    context.len(),
                    pk,
                )
            }
        };
        if ret != ffi::SUCCESS {
            return Err(signature::Error::default());
        }
        Ok(())
    }
}

impl<P: ParameterSet> signature::Verifier<super::Signature<P>> for VerifyingKey<P> {
    fn verify(&self, msg: &[u8], signature: &super::Signature<P>) -> Result<(), signature::Error> {
        self.verify_with_ctx(msg, EMPTY_CTX, signature)
    }
}

impl<P: ParameterSet> From<VerifyingKey<P>>
    for GenericArray<u8, <P as crate::VerifyingKeyLen>::LEN>
{
    fn from(vk: VerifyingKey<P>) -> Self {
        vk.pk
    }
}

impl<P: ParameterSet> TryFrom<&[u8]> for VerifyingKey<P> {
    type Error = transcoding::TranscodingError;

    fn try_from(bytes: &[u8]) -> Result<Self, Self::Error> {
        if bytes.len() != <<P as crate::VerifyingKeyLen>::LEN>::USIZE {
            return Err(transcoding::TranscodingError {});
        }
        let arr = GenericArray::from_slice(bytes).clone();
        Ok(Self { pk: arr })
    }
}

impl<P: ParameterSet> AsRef<[u8]> for VerifyingKey<P> {
    fn as_ref(&self) -> &[u8] {
        self.pk.as_ref()
    }
}
