use crate::rom::{self, DATA};

/// Represents a chunk of the ROM's data requested by an object for reading or writing. A requested block should always correspond exactly to an area of strictly contiguous data within an object.
#[derive(Debug)]
pub struct Block {
    _address: usize,
    pointer: usize,
}

impl Block {
    pub fn new(location: usize) -> Self {
        Block {
            _address: location,
            pointer: location,
        }
    }

    /// Decompresses data from the block's current position. Note that this
    /// method first measures the compressed data's size before allocating the
    /// destination array, which incurs a slight additional overhead.
    ///
    /// Returns an array containing the decompressed data.
    pub fn decompress(&mut self) -> Vec<i16> {
        let size = rom::get_compressed_size(self.pointer, DATA);
        assert!(size > 0, "Invalid compressed data: {}", size);
        let mut block_output = vec![0i16; size as usize];
        let mut read = 0;
        block_output = rom::decompress(self.pointer, DATA, block_output, &mut read)
            .expect("Computed and actual decompressed sizes do not match");
        block_output
    }

    /// Reads a 16-bit integer from the block's current position and advances the current position
    /// by 2 bytes.
    ///
    /// Returns the 16-bit value at the current position.
    pub fn read_int16(&mut self) -> i16 {
        let val = DATA[self.pointer];
        self.pointer += 1;
        val.into()
    }

    /// Reads a 32-bit integer from the block's current position and advances the current position
    /// by 4 bytes.
    pub fn read_int32(&mut self) -> u32 {
        self.read_int16() as u32
            + ((self.read_int16() as u32) << 8)
            + ((self.read_int16() as u32) << 16)
            + ((self.read_int16() as u32) << 24)
    }

    pub fn read_double_short(&mut self) -> i16 {
        self.read_int16() + (self.read_int16() << 8)
    }
}
