//! SM2 Digital Signature Algorithm (SM2DSA) as defined in [draft-shen-sm2-ecdsa § 5].
//!
//! ## Usage
//!
#![cfg_attr(all(feature = "dsa", feature = "getrandom"), doc = "```")]
#![cfg_attr(not(all(feature = "dsa", feature = "getrandom")), doc = "```ignore")]
//! # fn main() -> Result<(), Box<dyn core::error::Error>> {
//! // NOTE: requires the `dsa` and `getrandom` crate features are enabled
//! use sm2::{
//!     dsa::{Signature, SigningKey, signature::Signer},
//!     elliptic_curve::Generate,
//!     SecretKey
//! };
//!
//! // Signing
//! let secret_key = SecretKey::generate(); // serialize with `::to_bytes()`
//! let distid = "example@rustcrypto.org"; // distinguishing identifier
//! let signing_key = SigningKey::new(distid, &secret_key)?;
//! let verifying_key_bytes = signing_key.verifying_key().to_sec1_bytes();
//! let message = b"test message";
//! let signature: Signature = signing_key.sign(message);
//!
//! // Verifying
//! use sm2::dsa::{VerifyingKey, signature::Verifier};
//!
//! let verifying_key = VerifyingKey::from_sec1_bytes(distid, &verifying_key_bytes)?;
//! verifying_key.verify(message, &signature)?;
//! # Ok(())
//! # }
//! ```
//!
//! [draft-shen-sm2-ecdsa § 5]: https://datatracker.ietf.org/doc/html/draft-shen-sm2-ecdsa-02#section-5

#[cfg(feature = "arithmetic")]
mod signing;
#[cfg(feature = "arithmetic")]
mod verifying;

#[cfg(feature = "der")]
mod der;

#[cfg(feature = "der")]
pub use der::Signature as DerSignature;

pub use signature;

#[cfg(feature = "arithmetic")]
pub use self::{signing::SigningKey, verifying::VerifyingKey};

use crate::{Array, FieldBytes, FieldBytesSize, NonZeroScalar, Sm2};
use core::{
    fmt::{self, Debug},
    ops::Add,
};

use signature::{Error, Result, SignatureEncoding};

#[cfg(feature = "alloc")]
use alloc::vec::Vec;

#[cfg(feature = "pkcs8")]
use crate::pkcs8::{
    AlgorithmIdentifierRef, ObjectIdentifier, der::AnyRef, spki::AssociatedAlgorithmIdentifier,
};

#[cfg(all(feature = "alloc", feature = "pkcs8"))]
use crate::pkcs8::{
    der::{self as der_core, asn1::BitString},
    spki::SignatureBitStringEncoding,
};

/// SM2DSA signature size.
pub type SignatureSize = <FieldBytesSize as Add>::Output;

/// SM2DSA signature bytes.
pub type SignatureBytes = Array<u8, SignatureSize>;

/// Primitive scalar type (works without the `arithmetic` feature).
type ScalarValue = elliptic_curve::ScalarValue<Sm2>;

/// SM2DSA signature.
#[derive(Copy, Clone, Eq, PartialEq)]
pub struct Signature {
    r: ScalarValue,
    s: ScalarValue,
}

impl Signature {
    /// Size of an encoded SM2DSA signature in bytes.
    pub const BYTE_SIZE: usize = 64;

    /// Parse an SM2DSA signature from a byte array.
    pub fn from_bytes(bytes: &SignatureBytes) -> Result<Self> {
        let (r_bytes, s_bytes) = bytes.split_at(Self::BYTE_SIZE / 2);
        let r = ScalarValue::from_slice(r_bytes).map_err(|_| Error::new())?;
        let s = ScalarValue::from_slice(s_bytes).map_err(|_| Error::new())?;

        if r.is_zero().into() || s.is_zero().into() {
            return Err(Error::new());
        }

        Ok(Self { r, s })
    }

    /// Parse an SM2DSA signature from a byte slice.
    pub fn from_slice(bytes: &[u8]) -> Result<Self> {
        SignatureBytes::try_from(bytes)
            .map_err(|_| Error::new())?
            .try_into()
    }

    /// Create a [`Signature`] from the serialized `r` and `s` scalar values
    /// which comprise the signature.
    #[inline]
    pub fn from_scalars(r: impl Into<FieldBytes>, s: impl Into<FieldBytes>) -> Result<Self> {
        Self::try_from(r.into().concat(s.into()).as_slice())
    }

    /// Parse a signature from ASN.1 DER.
    #[cfg(feature = "der")]
    pub fn from_der(bytes: &[u8]) -> Result<Self> {
        DerSignature::try_from(bytes).and_then(Self::try_from)
    }

    /// Serialize this signature as bytes.
    pub fn to_bytes(&self) -> SignatureBytes {
        let mut ret = SignatureBytes::default();
        let (r_bytes, s_bytes) = ret.split_at_mut(Self::BYTE_SIZE / 2);
        r_bytes.copy_from_slice(&self.r.to_bytes());
        s_bytes.copy_from_slice(&self.s.to_bytes());
        ret
    }

    /// Bytes for the `R` component of a signature.
    pub fn r_bytes(&self) -> FieldBytes {
        self.r.to_bytes()
    }

    /// Bytes for the `s` component of a signature.
    pub fn s_bytes(&self) -> FieldBytes {
        self.s.to_bytes()
    }

    /// Serialize this signature as ASN.1 DER.
    #[cfg(feature = "der")]
    pub fn to_der(&self) -> DerSignature {
        DerSignature::from_components(&self.r_bytes(), &self.s_bytes()).expect("DER encoding error")
    }

    /// Convert this signature into a byte vector.
    #[cfg(feature = "alloc")]
    pub fn to_vec(&self) -> Vec<u8> {
        self.to_bytes().to_vec()
    }
}

#[cfg(feature = "arithmetic")]
impl Signature {
    /// Get the `r` component of this signature
    pub fn r(&self) -> NonZeroScalar {
        NonZeroScalar::new(self.r.into()).unwrap()
    }

    /// Get the `s` component of this signature
    pub fn s(&self) -> NonZeroScalar {
        NonZeroScalar::new(self.s.into()).unwrap()
    }

    /// Split the signature into its `r` and `s` scalars.
    pub fn split_scalars(&self) -> (NonZeroScalar, NonZeroScalar) {
        (self.r(), self.s())
    }
}

impl Debug for Signature {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "sm2::dsa::Signature(")?;

        for byte in self.to_bytes() {
            write!(f, "{byte:02X}")?;
        }

        write!(f, ")")
    }
}

impl From<Signature> for SignatureBytes {
    fn from(signature: Signature) -> SignatureBytes {
        signature.to_bytes()
    }
}

impl From<&Signature> for SignatureBytes {
    fn from(signature: &Signature) -> SignatureBytes {
        signature.to_bytes()
    }
}

impl SignatureEncoding for Signature {
    type Repr = SignatureBytes;

    fn to_bytes(&self) -> Self::Repr {
        self.into()
    }

    fn encoded_len(&self) -> usize {
        Self::BYTE_SIZE
    }
}

impl TryFrom<SignatureBytes> for Signature {
    type Error = Error;

    fn try_from(signature: SignatureBytes) -> Result<Signature> {
        Signature::from_bytes(&signature)
    }
}

impl TryFrom<&SignatureBytes> for Signature {
    type Error = Error;

    fn try_from(signature: &SignatureBytes) -> Result<Signature> {
        Signature::from_bytes(signature)
    }
}

impl TryFrom<&[u8]> for Signature {
    type Error = Error;

    fn try_from(bytes: &[u8]) -> Result<Signature> {
        Signature::from_slice(bytes)
    }
}

#[cfg(all(feature = "alloc", feature = "pkcs8"))]
impl SignatureBitStringEncoding for Signature {
    fn to_bitstring(&self) -> der_core::Result<BitString> {
        BitString::new(0, self.to_vec())
    }
}

#[cfg(feature = "pkcs8")]
impl AssociatedAlgorithmIdentifier for Signature {
    type Params = AnyRef<'static>;

    const ALGORITHM_IDENTIFIER: AlgorithmIdentifierRef<'static> = AlgorithmIdentifierRef {
        // Reference: https://datatracker.ietf.org/doc/html/draft-sca-cfrg-sm3#section-8.1.3
        // ```
        // "1.2.156.10197.1.501" for "Digital Signature: SM2 and SM3"
        // o  "id-dsa-sm2sm3" "{id-int dsa-sm2sm3(501)}"
        // ```
        //
        // NOTE: Here [`Signature`] represent an SM2 signed object with an SM3 hash (implementation detail
        // of [`sm2::dsa::SigningKey`] and [`sm2::dsa::VerifyingKey`], that might need to change
        // once/if the hash function gets modular.
        oid: ObjectIdentifier::new_unwrap("1.2.156.10197.1.501"),
        parameters: None,
    };
}
