#include <luna/Bitmap.h>
#include <luna/CString.h>
#include <luna/Check.h>

Bitmap::Bitmap()
{
}

Bitmap::Bitmap(void* location, usize size_in_bytes) : m_location((u8*)location), m_size_in_bytes(size_in_bytes)
{
}

void Bitmap::initialize(void* location, usize size_in_bytes)
{
    m_location = (u8*)location;
    m_size_in_bytes = size_in_bytes;
}

void* Bitmap::move(void* new_location, usize new_location_size_in_bytes)
{
    expect(initialized(), "Bitmap was never initialized");

    if (new_location_size_in_bytes > m_size_in_bytes) memcpy(new_location, m_location, m_size_in_bytes);
    else
        memcpy(new_location, m_location, new_location_size_in_bytes);

    void* old_location = (void*)m_location;

    m_location = (u8*)new_location;
    m_size_in_bytes = new_location_size_in_bytes;

    return old_location;
}

void Bitmap::set(usize index, bool value)
{
    expect(initialized(), "Bitmap was never initialized");
    expect(index < size(), "Bitmap access out of range");
    u64 byte_index = index / 8;
    u8 bit_mask = (u8)(0b10000000 >> (index % 8));
    m_location[byte_index] &= (u8)(~bit_mask);
    if (value) { m_location[byte_index] |= bit_mask; }
}

bool Bitmap::get(usize index) const
{
    expect(initialized(), "Bitmap was never initialized");
    expect(index < size(), "Bitmap access out of range");
    usize byte_index = index / 8;
    u8 bit_mask = (u8)(0b10000000 >> (index % 8));
    return (m_location[byte_index] & bit_mask) > 0;
}

void Bitmap::clear(bool value)
{
    expect(initialized(), "Bitmap was never initialized");
    memset(m_location, value_byte(value), m_size_in_bytes);
}

void Bitmap::clear_region(usize start, usize bits, bool value)
{
    expect(initialized(), "Bitmap was never initialized");
    expect((start + bits) <= size(), "Bitmap clear out of range");

    if (!bits) return;

    // Set individual bits while not on a byte boundary.
    while ((start % 8) && bits)
    {
        set(start, value);
        start++;
        bits--;
    }

    // Clear out the rest in bytes.
    usize bytes = bits / 8;

    memset(&m_location[start / 8], value_byte(value), bytes);
    start += bytes * 8;
    bits -= bytes * 8;

    // Set the remaining individual bits.
    while (bits--)
    {
        set(start, value);
        start++;
    }
}