// Copyright 2022 Google LLC
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

use crate::util::{byte_to_ascii_hex_lower, parse_byte_from_ascii_str_at};
use crate::GuidFromStrError;
use core::fmt::{self, Display, Formatter};
use core::str::{self, FromStr};

#[cfg(feature = "serde")]
use {
    serde::de::{self, Visitor},
    serde::{Deserialize, Deserializer, Serialize, Serializer},
};

#[cfg(feature = "bytemuck")]
use bytemuck::{Pod, Zeroable};

/// Globally-unique identifier.
///
/// The format is defined in [RFC 4122]. However, unlike "normal" UUIDs
/// (such as those provided by the [`uuid`] crate), the first three
/// fields are little-endian. See also [Appendix A] of the UEFI
/// Specification.
///
/// This type is 4-byte aligned. The UEFI Specification says the GUID
/// type should be 8-byte aligned, but most C implementations have
/// 4-byte alignment, so we do the same here for compatibility.
///
/// [Appendix A]: https://uefi.org/specs/UEFI/2.10/Apx_A_GUID_and_Time_Formats.html
/// [RFC 4122]: https://datatracker.ietf.org/doc/html/rfc4122
/// [`uuid`]: https://docs.rs/uuid/latest/uuid
#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash, Ord, PartialOrd)]
#[cfg_attr(feature = "bytemuck", derive(Pod, Zeroable))]
#[repr(C)]
pub struct Guid {
    // Use `u32` rather than `[u8; 4]` here so that the natural
    // alignment of the struct is four bytes. This is better for the end
    // user than setting `repr(align(4))` because it doesn't prevent use
    // of the type in a `repr(packed)` struct. For more discussion, see
    // https://github.com/rust-lang/rfcs/pull/1358#issuecomment-217582887
    time_low: u32,
    time_mid: [u8; 2],
    time_high_and_version: [u8; 2],
    clock_seq_high_and_reserved: u8,
    clock_seq_low: u8,
    node: [u8; 6],
}

impl Guid {
    /// GUID with all fields set to zero.
    pub const ZERO: Self = Self {
        time_low: 0,
        time_mid: [0, 0],
        time_high_and_version: [0, 0],
        clock_seq_high_and_reserved: 0,
        clock_seq_low: 0,
        node: [0; 6],
    };

    /// Create a new GUID.
    #[must_use]
    pub const fn new(
        time_low: [u8; 4],
        time_mid: [u8; 2],
        time_high_and_version: [u8; 2],
        clock_seq_high_and_reserved: u8,
        clock_seq_low: u8,
        node: [u8; 6],
    ) -> Self {
        Self {
            time_low: u32::from_ne_bytes([
                time_low[0],
                time_low[1],
                time_low[2],
                time_low[3],
            ]),
            time_mid: [time_mid[0], time_mid[1]],
            time_high_and_version: [
                time_high_and_version[0],
                time_high_and_version[1],
            ],
            clock_seq_high_and_reserved,
            clock_seq_low,
            node,
        }
    }

    /// Create a version 4 GUID from provided random bytes.
    ///
    /// See [RFC 4122 section 4.4][rfc] for the definition of a version
    /// 4 GUID.
    ///
    /// This constructor does not itself generate random bytes, but
    /// instead expects the caller to provide suitably random bytes.
    ///
    /// # Example
    ///
    /// ```
    /// use uguid::{Guid, Variant};
    ///
    /// let guid = Guid::from_random_bytes([
    ///     104, 192, 95, 215, 120, 33, 249, 1, 102, 21, 171, 84, 233, 204, 68, 176,
    /// ]);
    /// assert_eq!(guid.variant(), Variant::Rfc4122);
    /// assert_eq!(guid.version(), 4);
    /// ```
    ///
    /// [rfc]: https://datatracker.ietf.org/doc/html/rfc4122#section-4.4
    #[must_use]
    pub const fn from_random_bytes(mut random_bytes: [u8; 16]) -> Self {
        // Set the variant in byte 8: set bit 7, clear bit 6.
        random_bytes[8] &= 0b1011_1111;
        random_bytes[8] |= 0b1000_0000;
        // Set the version in byte 7: set the most-significant-nibble to 4.
        random_bytes[7] &= 0b0000_1111;
        random_bytes[7] |= 0b0100_1111;

        Self::from_bytes(random_bytes)
    }

    /// True if all bits are zero, false otherwise.
    ///
    /// # Example
    ///
    /// ```
    /// use uguid::guid;
    ///
    /// assert!(guid!("00000000-0000-0000-0000-000000000000").is_zero());
    /// assert!(!guid!("308bbc16-a308-47e8-8977-5e5646c5291f").is_zero());
    /// ```
    #[must_use]
    pub const fn is_zero(self) -> bool {
        let b = self.to_bytes();
        b[0] == 0
            && b[1] == 0
            && b[2] == 0
            && b[3] == 0
            && b[4] == 0
            && b[5] == 0
            && b[6] == 0
            && b[7] == 0
            && b[8] == 0
            && b[9] == 0
            && b[10] == 0
            && b[11] == 0
            && b[12] == 0
            && b[13] == 0
            && b[14] == 0
            && b[15] == 0
    }

    /// The little-endian low field of the timestamp.
    #[must_use]
    pub const fn time_low(self) -> [u8; 4] {
        self.time_low.to_ne_bytes()
    }

    /// The little-endian middle field of the timestamp.
    #[must_use]
    pub const fn time_mid(self) -> [u8; 2] {
        self.time_mid
    }

    /// The little-endian high field of the timestamp multiplexed with
    /// the version number.
    #[must_use]
    pub const fn time_high_and_version(self) -> [u8; 2] {
        self.time_high_and_version
    }

    /// The high field of the clock sequence multiplexed with the
    /// variant.
    #[must_use]
    pub const fn clock_seq_high_and_reserved(self) -> u8 {
        self.clock_seq_high_and_reserved
    }

    /// The low field of the clock sequence.
    #[must_use]
    pub const fn clock_seq_low(self) -> u8 {
        self.clock_seq_low
    }

    /// The spatially unique node identifier.
    #[must_use]
    pub const fn node(self) -> [u8; 6] {
        self.node
    }

    /// Get the GUID variant.
    ///
    /// # Example
    ///
    /// ```
    /// use uguid::{guid, Variant};
    ///
    /// assert_eq!(
    ///     guid!("308bbc16-a308-47e8-8977-5e5646c5291f").variant(),
    ///     Variant::Rfc4122
    /// );
    /// ```
    #[must_use]
    pub const fn variant(self) -> Variant {
        // Get the 3 most significant bits of `clock_seq_high_and_reserved`.
        let bits = (self.clock_seq_high_and_reserved & 0b1110_0000) >> 5;

        if (bits & 0b100) == 0 {
            Variant::ReservedNcs
        } else if (bits & 0b010) == 0 {
            Variant::Rfc4122
        } else if (bits & 0b001) == 0 {
            Variant::ReservedMicrosoft
        } else {
            Variant::ReservedFuture
        }
    }

    /// Get the GUID version. This is a sub-type of the variant as
    /// defined in [RFC4122].
    ///
    /// [RFC4122]: https://datatracker.ietf.org/doc/html/rfc4122#section-4.1.3
    ///
    /// # Example
    ///
    /// ```
    /// use uguid::guid;
    ///
    /// assert_eq!(guid!("308bbc16-a308-47e8-8977-5e5646c5291f").version(), 4);
    /// ```
    #[must_use]
    pub const fn version(self) -> u8 {
        (self.time_high_and_version[1] & 0b1111_0000) >> 4
    }

    /// Parse a GUID from a string.
    ///
    /// This is functionally the same as [`Self::from_str`], but is
    /// exposed separately to provide a `const` method for parsing.
    pub const fn try_parse(s: &str) -> Result<Self, GuidFromStrError> {
        // Treat input as ASCII.
        let s = s.as_bytes();

        if s.len() != 36 {
            return Err(GuidFromStrError::Length);
        }

        let sep = b'-';
        if s[8] != sep {
            return Err(GuidFromStrError::Separator(8));
        }
        if s[13] != sep {
            return Err(GuidFromStrError::Separator(13));
        }
        if s[18] != sep {
            return Err(GuidFromStrError::Separator(18));
        }
        if s[23] != sep {
            return Err(GuidFromStrError::Separator(23));
        }

        Ok(Self::from_bytes([
            mtry!(parse_byte_from_ascii_str_at(s, 6)),
            mtry!(parse_byte_from_ascii_str_at(s, 4)),
            mtry!(parse_byte_from_ascii_str_at(s, 2)),
            mtry!(parse_byte_from_ascii_str_at(s, 0)),
            mtry!(parse_byte_from_ascii_str_at(s, 11)),
            mtry!(parse_byte_from_ascii_str_at(s, 9)),
            mtry!(parse_byte_from_ascii_str_at(s, 16)),
            mtry!(parse_byte_from_ascii_str_at(s, 14)),
            mtry!(parse_byte_from_ascii_str_at(s, 19)),
            mtry!(parse_byte_from_ascii_str_at(s, 21)),
            mtry!(parse_byte_from_ascii_str_at(s, 24)),
            mtry!(parse_byte_from_ascii_str_at(s, 26)),
            mtry!(parse_byte_from_ascii_str_at(s, 28)),
            mtry!(parse_byte_from_ascii_str_at(s, 30)),
            mtry!(parse_byte_from_ascii_str_at(s, 32)),
            mtry!(parse_byte_from_ascii_str_at(s, 34)),
        ]))
    }

    /// Parse a GUID from a string, panicking on failure.
    ///
    /// The input must be in "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
    /// format, where each `x` is a hex digit (any of `0-9`, `a-f`, or
    /// `A-F`).
    ///
    /// This function is marked `track_caller` so that error messages
    /// point directly to the invalid GUID string.
    ///
    /// # Panics
    ///
    /// This function will panic if the input is not in the format shown
    /// above. In particular, it will panic if the input is not exactly
    /// 36 bytes long, or if the input does not have separators at the
    /// expected positions, or if any of the remaining characters are
    /// not valid hex digits.
    #[must_use]
    #[track_caller]
    pub const fn parse_or_panic(s: &str) -> Self {
        match Self::try_parse(s) {
            Ok(g) => g,
            Err(GuidFromStrError::Length) => {
                panic!("GUID string has wrong length (expected 36 bytes)");
            }
            Err(GuidFromStrError::Separator(_)) => {
                panic!("GUID string is missing one or more separators (`-`)");
            }
            Err(GuidFromStrError::Hex(_)) => {
                panic!("GUID string contains one or more invalid characters");
            }
        }
    }

    /// Create a GUID from a 16-byte array. No changes to byte order are made.
    #[must_use]
    pub const fn from_bytes(bytes: [u8; 16]) -> Self {
        Self::new(
            [bytes[0], bytes[1], bytes[2], bytes[3]],
            [bytes[4], bytes[5]],
            [bytes[6], bytes[7]],
            bytes[8],
            bytes[9],
            [
                bytes[10], bytes[11], bytes[12], bytes[13], bytes[14],
                bytes[15],
            ],
        )
    }

    /// Convert to a 16-byte array.
    #[must_use]
    pub const fn to_bytes(self) -> [u8; 16] {
        let time_low = self.time_low();

        [
            time_low[0],
            time_low[1],
            time_low[2],
            time_low[3],
            self.time_mid[0],
            self.time_mid[1],
            self.time_high_and_version[0],
            self.time_high_and_version[1],
            self.clock_seq_high_and_reserved,
            self.clock_seq_low,
            self.node[0],
            self.node[1],
            self.node[2],
            self.node[3],
            self.node[4],
            self.node[5],
        ]
    }

    /// Convert to a lower-case hex ASCII string.
    ///
    /// The output is in "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" format.
    #[must_use]
    pub const fn to_ascii_hex_lower(self) -> [u8; 36] {
        let bytes = self.to_bytes();

        let mut buf = [0; 36];
        (buf[0], buf[1]) = byte_to_ascii_hex_lower(bytes[3]);
        (buf[2], buf[3]) = byte_to_ascii_hex_lower(bytes[2]);
        (buf[4], buf[5]) = byte_to_ascii_hex_lower(bytes[1]);
        (buf[6], buf[7]) = byte_to_ascii_hex_lower(bytes[0]);
        buf[8] = b'-';
        (buf[9], buf[10]) = byte_to_ascii_hex_lower(bytes[5]);
        (buf[11], buf[12]) = byte_to_ascii_hex_lower(bytes[4]);
        buf[13] = b'-';
        (buf[14], buf[15]) = byte_to_ascii_hex_lower(bytes[7]);
        (buf[16], buf[17]) = byte_to_ascii_hex_lower(bytes[6]);
        buf[18] = b'-';
        (buf[19], buf[20]) = byte_to_ascii_hex_lower(bytes[8]);
        (buf[21], buf[22]) = byte_to_ascii_hex_lower(bytes[9]);
        buf[23] = b'-';
        (buf[24], buf[25]) = byte_to_ascii_hex_lower(bytes[10]);
        (buf[26], buf[27]) = byte_to_ascii_hex_lower(bytes[11]);
        (buf[28], buf[29]) = byte_to_ascii_hex_lower(bytes[12]);
        (buf[30], buf[31]) = byte_to_ascii_hex_lower(bytes[13]);
        (buf[32], buf[33]) = byte_to_ascii_hex_lower(bytes[14]);
        (buf[34], buf[35]) = byte_to_ascii_hex_lower(bytes[15]);
        buf
    }
}

impl Default for Guid {
    fn default() -> Self {
        Self::ZERO
    }
}

impl Display for Guid {
    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
        let ascii = self.to_ascii_hex_lower();
        // OK to unwrap since the ascii output is valid utf-8.
        let s = str::from_utf8(&ascii).unwrap();
        f.write_str(s)
    }
}

impl FromStr for Guid {
    type Err = GuidFromStrError;

    /// Parse a GUID from a string, panicking on failure.
    ///
    /// The input must be in "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
    /// format, where each `x` is a hex digit (any of `0-9`, `a-f`, or
    /// `A-F`).
    fn from_str(s: &str) -> Result<Self, Self::Err> {
        Self::try_parse(s)
    }
}

#[cfg(feature = "serde")]
impl Serialize for Guid {
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
    where
        S: Serializer,
    {
        let ascii = self.to_ascii_hex_lower();
        // OK to unwrap since the ascii output is valid utf-8.
        let s = str::from_utf8(&ascii).unwrap();
        serializer.serialize_str(s)
    }
}

#[cfg(feature = "serde")]
struct DeserializerVisitor;

#[cfg(feature = "serde")]
impl Visitor<'_> for DeserializerVisitor {
    type Value = Guid;

    fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
        formatter.write_str(
            "a string in the format \"xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx\"",
        )
    }

    fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
    where
        E: de::Error,
    {
        Guid::try_parse(value).map_err(E::custom)
    }
}

#[cfg(feature = "serde")]
impl<'de> Deserialize<'de> for Guid {
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    where
        D: Deserializer<'de>,
    {
        deserializer.deserialize_str(DeserializerVisitor)
    }
}

/// Variant or type of GUID, as defined in [RFC4122].
///
/// [RFC4122]: https://datatracker.ietf.org/doc/html/rfc4122#section-4.1.3
#[derive(Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd)]
pub enum Variant {
    /// Reserved, NCS backward compatibility.
    ReservedNcs,

    /// The GUID variant described by RFC4122.
    Rfc4122,

    /// Reserved, Microsoft Corporation backward compatibility.
    ReservedMicrosoft,

    /// Reserved for future use.
    ReservedFuture,
}
