use no_std_io2::io::{Read, Seek};

use deku::prelude::*;

use super::squashfs::SuperBlock;
use crate::error::BackhandError;
use crate::kinds::Kind;

pub const METADATA_MAXSIZE: usize = 0x2000;

const METDATA_UNCOMPRESSED: u16 = 1 << 15;

pub fn read_block<R: Read + Seek + ?Sized>(
    reader: &mut R,
    superblock: &SuperBlock,
    kind: &Kind,
) -> Result<Vec<u8>, BackhandError> {
    let mut deku_reader = Reader::new(&mut *reader);
    let metadata_len = u16::from_reader_with_ctx(&mut deku_reader, kind.inner.data_endian)?;

    if superblock.check_data() {
        let mut check_byte = [0u8; 1];
        reader.read_exact(&mut check_byte)?;
        tracing::trace!("check_data: skipped check byte 0x{:02x}", check_byte[0]);
    }

    let byte_len = len(metadata_len);
    tracing::trace!("len: 0x{:02x?}", byte_len);
    let mut buf = vec![0u8; byte_len as usize];
    reader.read_exact(&mut buf)?;

    // NOTE: We intentionally ignore superblock.inodes_uncompressed() here.
    // Some v3 images set that flag but still use per-block compression,
    // so we rely solely on the per-block compressed bit.
    let is_block_compressed = is_compressed(metadata_len);
    let bytes = if is_block_compressed {
        let mut out = Vec::with_capacity(8 * 1024);
        kind.inner.compressor.decompress(&buf, &mut out, None)?;
        out
    } else {
        tracing::trace!("uncompressed (superblock flag or block flag)");
        buf
    };

    tracing::trace!("uncompressed size: 0x{:02x?}", bytes.len());
    Ok(bytes)
}

/// Check is_compressed bit within raw `len`
pub fn is_compressed(len: u16) -> bool {
    len & METDATA_UNCOMPRESSED == 0
}

/// Get actual length of `data` following `len` from unedited `len`
pub fn len(len: u16) -> u16 {
    len & !(METDATA_UNCOMPRESSED)
}
