//! The `B512` type supports the usage of 64-byte values in Sway which are needed when working with public keys and signatures.
library;

use ::ops::*;
use ::primitives::*;
use ::convert::{From, Into, TryFrom};
use ::bytes::Bytes;
use ::option::Option::{self, *};
use ::raw_slice::*;
use ::codec::*;
use ::debug::*;
use ::hash::*;

/// Stores two `b256`s in contiguous memory.
/// Guaranteed to be contiguous for use with ec-recover: `std::ecr::ec_recover`.
pub struct B512 {
    /// The two `b256`s that make up the `B512`.
    bits: [b256; 2],
}

impl PartialEq for B512 {
    fn eq(self, other: Self) -> bool {
        (self.bits)[0] == (other.bits)[0] && (self.bits)[1] == (other.bits)[1]
    }
}
impl Eq for B512 {}

impl From<(b256, b256)> for B512 {
    /// Converts from a `b256` tuple to a `B512`.
    ///
    /// # Arguments
    ///
    /// * `components`: [(b256, b256)] - The `(b256, b256)` tuple to convert to a `B512`.
    ///
    /// # Returns
    ///
    /// * [B512] - The newly created `B512`.
    ///
    /// # Examples
    ///
    /// ```sway
    /// use std::b512::B512;
    ///
    /// fn foo() {
    ///     let tuple: (b256, b256) = (b256::zero(), b256::zero());
    ///     let b512 = B512::from(tuple);
    /// }
    /// ```
    fn from(components: (b256, b256)) -> Self {
        Self {
            bits: [components.0, components.1],
        }
    }
}

impl From<B512> for (b256, b256) {
    /// Converts from a `B512` to a `b256` tuple.
    ///
    /// # Additional Information
    ///
    /// **NOTE:** To import, use the glob operator i.e. `use std::b512::*;`
    ///
    /// # Arguments
    ///
    /// * `val`: [B512] - The `B512` to convert to a tuple.
    ///
    /// # Returns
    ///
    /// * [(b256, b256)] - The newly created tuple.
    ///
    /// # Examples
    ///
    /// ```sway
    /// use std::b512::*;
    ///
    /// fn foo() {
    ///     let b512 = B512::zero();
    ///     let tuple: (b256, b256) = (b256, b256)::from(b512);
    /// }
    /// ```
    fn from(val: B512) -> (b256, b256) {
        ((val.bits)[0], (val.bits)[1])
    }
}

/// Methods on the `B512` type.
impl B512 {
    /// Initializes a new, zeroed `B512`.
    ///
    /// # Returns
    ///
    /// * [B512] - A zero value B512.
    ///
    /// # Examples
    ///
    /// ```sway
    /// use std::b512::B512;
    ///
    /// fn foo() {
    ///     let zero = B512::new();
    /// }
    /// ```
    pub fn new() -> Self {
        Self {
            bits: [b256::zero(), b256::zero()],
        }
    }

    /// Returns the underlying bits for the B512 type.
    ///
    /// # Returns
    ///
    /// * [[b256; 2]] - The two `b256`s that make up the `B512`.
    ///
    /// # Examples
    ///
    /// ```sway
    /// use std::b512::B512;
    ///
    /// fn foo() {
    ///     let zero = B512::new();
    ///     assert(zero.bits() == [b256::zero(), b256::zero()]);
    /// }
    /// ```
    pub fn bits(self) -> [b256; 2] {
        self.bits
    }

    /// Returns the zero value for the `B512` type.
    ///
    /// # Returns
    ///
    /// * [B512] -> The zero value for the `B512` type.
    ///
    /// # Examples
    ///
    /// ```sway
    /// use std::b512::B512;
    ///
    /// fn foo() {
    ///     let zero_b512 = B512::zero();
    ///     assert(zero_b512 == B512::from((b256::zero(), b256::zero())));
    /// }
    /// ```
    pub fn zero() -> Self {
        Self {
            bits: [b256::zero(), b256::zero()],
        }
    }

    /// Returns whether a `B512` is set to zero.
    ///
    /// # Returns
    ///
    /// * [bool] -> True if the `B512` is zero, otherwise false.
    ///
    /// # Examples
    ///
    /// ```sway
    /// use std::b512::B512;
    ///
    /// fn foo() {
    ///     let zero_b512 = B512::zero();
    ///     assert(zero_b512.is_zero());
    /// }
    /// ```
    pub fn is_zero(self) -> bool {
        (self.bits)[0] == b256::zero() && (self.bits)[1] == b256::zero()
    }
}

impl TryFrom<Bytes> for B512 {
    /// Casts raw `Bytes` data to an `B512`.
    ///
    /// # Arguments
    ///
    /// * `bytes`: [Bytes] - The raw `Bytes` data to be casted.
    ///
    /// # Returns
    ///
    /// * [B512] - The newly created `B512` from the raw `Bytes`.
    ///
    /// # Examples
    ///
    /// ```sway
    /// use std::bytes::Bytes;
    ///
    /// fn foo(bytes: Bytes) {
    ///    let result = B512::try_from(bytes);
    ///    assert(result.is_some());
    ///    let b512 = result.unwrap();
    /// }
    /// ```
    fn try_from(bytes: Bytes) -> Option<Self> {
        if bytes.len() != 64 {
            return None;
        }

        Some(Self {
            bits: asm(ptr: bytes.ptr()) {
                ptr: [b256; 2]
            },
        })
    }
}

impl Into<Bytes> for B512 {
    /// Casts an `B512` to raw `Bytes` data.
    ///
    /// # Returns
    ///
    /// * [Bytes] - The underlying raw `Bytes` data of the `B512`.
    ///
    /// # Examples
    ///
    /// ```sway
    /// fn foo() {
    ///     let b512 = B512::from((b256::zero(), b256::zero()));
    ///     let bytes_data: Bytes = b512.into()
    ///     assert(bytes_data.len() == 64);
    /// }
    /// ```
    fn into(self) -> Bytes {
        Bytes::from(raw_slice::from_parts::<u8>(__addr_of(self.bits), 64))
    }
}

impl Hash for B512 {
    fn is_hash_trivial() -> bool {
        // `B512` is just two contiguous `b256`s (64 bytes), **hashed as raw bytes**,
        // so its in-memory representation is identical to its hash bytes.
        true
    }

    fn hash(self, ref mut state: Hasher) {
        // We want to hash just the raw bytes of the b512,
        // and not the `self.bits` array itself.
        // The fact that the signature bits are stored in a fixed-size array
        // is just an implementation detail.
        // The hash is computed only over the raw bytes of the b512.
        state.write_raw_slice(raw_slice::from_parts::<u8>(__addr_of(self.bits), 64));
    }
}