use super::super::config::Base32DecodeConfig;
use super::super::error::Base32Error;

#[inline(always)]
pub fn decode_tail_into(
    config: &Base32DecodeConfig,
    dst: &mut [u8],
    src: &[u8],
    src_offset: usize,
    padding_count: usize,
) -> Result<usize, Base32Error> {
    let decode_table = config.decode_table;
    let mut indices: [i8; 8] = [0; 8];

    for (j, &byte) in src.iter().enumerate() {
        let pos = src_offset + j;

        if let Some(padding) = config.padding {
            if byte == padding {
                let expected_min_pos = 8 - padding_count;
                if j < expected_min_pos {
                    return Err(Base32Error::InvalidPadding);
                }
                indices[j] = 0;
            } else {
                if byte >= 128 {
                    return Err(Base32Error::InvalidCharacter(byte, pos));
                }
                let val = decode_table[byte as usize];
                if val < 0 {
                    return Err(Base32Error::InvalidCharacter(byte, pos));
                }
                indices[j] = val;
            }
        } else {
            if byte >= 128 {
                return Err(Base32Error::InvalidCharacter(byte, pos));
            }
            let val = decode_table[byte as usize];
            if val < 0 {
                return Err(Base32Error::InvalidCharacter(byte, pos));
            }
            indices[j] = val;
        }
    }

    let bytes_written = match padding_count {
        6 => 1,
        4 => 2,
        3 => 3,
        1 => 4,
        0 => 5,
        _ => return Err(Base32Error::InvalidPadding),
    };

    let i0 = indices[0] as u32;
    let i1 = indices[1] as u32;
    let i2 = indices[2] as u32;
    let i3 = indices[3] as u32;
    let i4 = indices[4] as u32;
    let i5 = indices[5] as u32;
    let i6 = indices[6] as u32;
    let i7 = indices[7] as u32;

    let b0 = (i0 << 3) | (i1 >> 2);
    let b1 = (i1 << 6) | (i2 << 1) | (i3 >> 4);
    let b2 = (i3 << 4) | (i4 >> 1);
    let b3 = (i4 << 7) | (i5 << 2) | (i6 >> 3);
    let b4 = (i6 << 5) | i7;

    match padding_count {
        6 => {
            dst[0] = b0 as u8;
        }
        4 => {
            dst[0] = b0 as u8;
            dst[1] = b1 as u8;
        }
        3 => {
            dst[0] = b0 as u8;
            dst[1] = b1 as u8;
            dst[2] = b2 as u8;
        }
        1 => {
            dst[0] = b0 as u8;
            dst[1] = b1 as u8;
            dst[2] = b2 as u8;
            dst[3] = b3 as u8;
        }
        0 => {
            dst[0] = b0 as u8;
            dst[1] = b1 as u8;
            dst[2] = b2 as u8;
            dst[3] = b3 as u8;
            dst[4] = b4 as u8;
        }
        _ => unreachable!(),
    }

    Ok(bytes_written)
}
