use std::cmp::min;

use crate::common::paddings::{Method2, PaddingModeImpl};
use crate::libs::utils::{bytes_to_dwords, dwords_to_bytes, dwords_add};

use super::constants::{SHA1_BLOCK_SIZE as BLOCK_SIZE, SHA1_OUTPUT_SIZE as OUTPUT_SIZE, SHA1_IV as IV};
use super::CryptographicHashImpl;


/// 安全散列算法1流式计算器（Secure Hash Algorithm 1 Stream Mode Calculator）
///
/// 安全散列算法1是一种密码杂凑算法，美国国家安全局设计，并由美国国家标准技术研究所发布为联邦资料处理标准。\
/// In cryptography, SHA-1 (Secure Hash Algorithm 1) is a hash function
/// which was designed by the United States National Security Agency, and is a U.S. Federal Information Processing Standard.
///
/// SHA-1可以生成一个被称为消息摘要的160位（20字节）杂凑值，杂凑值通常的呈现形式为40个十六进制数。\
/// It takes an input and produces a 160-bit (20-byte) hash value known as a message digest -
/// typically rendered as 40 hexadecimal digits.
///
/// # 示例（Examples）
///
/// ```rust
/// # fn main() -> Result<(), ()> {
/// use neuedu_cryptos::cryptographic_hashs::{CryptographicHashImpl, SHA1};
///
/// const MESSAGE: [u8; 3] = [
///     0x61, 0x62, 0x63,
/// ];
///
/// let mut instance = SHA1::default();
/// instance.update(&MESSAGE)?;
/// let digest = instance.r#final()?;
///
/// assert_eq!(digest, [
///     0xA9, 0x99, 0x3E, 0x36, 0x47, 0x06, 0x81, 0x6A,
///     0xBA, 0x3E, 0x25, 0x71, 0x78, 0x50, 0xC2, 0x6C,
///     0x9C, 0xD0, 0xD8, 0x9D,
/// ]);
/// #
/// #     Ok(())
/// # }
/// ```
pub struct SHA1 {
    buffer: [u8; BLOCK_SIZE as usize],
    total_length: u64,
    h: [u32; 5],
}

impl SHA1 {

    fn new() -> Self {
        Self {
            buffer: [0u8; BLOCK_SIZE as usize],
            total_length: 0,
            h: IV,
        }
    }

    fn padding(&mut self) -> Box<[u8]> {
        let message_length = self.total_length * 8;

        let pad_message = Method2::new(BLOCK_SIZE as usize).pad(&self.buffer[..self.total_length as usize % BLOCK_SIZE as usize]);

        let mut result = Vec::<u8>::with_capacity(BLOCK_SIZE as usize * 2);
        match pad_message.len() {
            1..=8 => {
                result.append(&mut pad_message.into());
                result.resize(BLOCK_SIZE as usize - 8, 0);
            },
            9.. => {
                result.append(&mut pad_message[0..pad_message.len() - 8].into());
            },
            _ => unreachable!(),
        }
        result.append(&mut message_length.to_be_bytes().into());

        result.into()
    }

    fn extend(&self) -> [u32; 80] {
        let words = bytes_to_dwords(&self.buffer);

        let mut w: [u32; 80] = [0; 80];
        w[..16].copy_from_slice(&words[..16]);
        for i in 16..=79 {
            w[i] = (w[i - 3] ^ w[i - 8] ^ w[i - 14] ^ w[i - 16]).rotate_left(1);
        }

        w
    }

    fn compress(&mut self, w: [u32; 80]) {
        let [mut a, mut b, mut c, mut d, mut e] = self.h;
        for (i, item) in w.iter().enumerate() {
            let f = match i {
                0..=19 => (b & c) | ((!b) & d),
                20..=39 => b ^ c ^ d,
                40..=59 => (b & c) | (b & d) | (c & d),
                60..=79 => b ^ c ^ d,
                _ => unreachable!(),
            };
            let k = match i {
                0..=19 => 0x5A827999,
                20..=39 => 0x6ED9EBA1,
                40..=59 => 0x8F1BBCDC,
                60..=79 => 0xCA62C1D6,
                _ => unreachable!(),
            };
            let temp = a.rotate_left(5).wrapping_add(f).wrapping_add(e).wrapping_add(k).wrapping_add(*item);
            e = d;
            d = c;
            c = b.rotate_left(30);
            b = a;
            a = temp;
        }

        let result = dwords_add(&self.h, &[a, b, c, d, e]);
        self.h.copy_from_slice(&result);
    }
}

impl Default for SHA1 {

    fn default() -> Self {
        Self::new()
    }
}

impl CryptographicHashImpl for SHA1 {

    /// 块长度：64字节。\
    /// Block size: 64 Bytes.
    fn block_size(&self) -> usize {
        BLOCK_SIZE as usize
    }

    /// 输出长度：20字节。\
    /// Output size: 20 Bytes.
    fn output_size(&self) -> usize {
        OUTPUT_SIZE as usize
    }

    fn update(&mut self, message: &[u8]) -> Result<(), ()> {
        let mut overflow;
        let mut offset: usize = 0;
        while offset < message.len() {
            let current_length = min(
                message.len() - offset,
                BLOCK_SIZE as usize - self.total_length as usize % BLOCK_SIZE as usize
            ) as u8;
            self.buffer[(self.total_length as usize % BLOCK_SIZE as usize)..(self.total_length as usize % BLOCK_SIZE as usize + current_length as usize)]
                .copy_from_slice(&message[offset..(offset + current_length as usize)]);

            (self.total_length, overflow) = self.total_length.overflowing_add(current_length as u64);
            if overflow {
                return Err(());
            }
            offset += current_length as usize;

            if self.total_length as usize % BLOCK_SIZE as usize == 0 {
                self.compress(self.extend());
            }
        }

        Ok(())
    }

    fn r#final(&mut self) -> Result<Vec<u8>, ()> {
        let pad_message = self.padding();
        self.update(&pad_message).unwrap();

        Ok(dwords_to_bytes(&self.h).into())
    }
}
