
#include "bitmap.h"
#include <limits.h>

static constexpr struct bitmap B = {0}; /* <-- just used on the line below */
static constexpr size_t bits_per_index = sizeof (B.data[0]) * CHAR_BIT;

#define mask(n) ((1<<(n))-1)
_Static_assert(mask(0) == 0b0000);
_Static_assert(mask(1) == 0b0001);
_Static_assert(mask(2) == 0b0011);
_Static_assert(mask(3) == 0b0111);

static inline int trailing_zeroes(int n) 
{
    return __builtin_ctz(n);
}

int bitmap_find(const struct bitmap* bitmap, int val)
{
    for (size_t i = 0; i < bitmap->bit_count / bits_per_index; i++) {
        auto x = val == 0
            ? ~bitmap->data[i]
            : bitmap->data[i];
        if (x != 0) {
            return i * bits_per_index + trailing_zeroes(x);
        }
    }
    return -1;
}

int bitmap_set_range(struct bitmap* bitmap, size_t begin, size_t end)
{
    if (end < begin) {
        return bitmap_set_range(bitmap, end, begin);
    }

    if (unlikely(begin > bitmap->bit_count)) {
        return -1;
    }

    if (unlikely(end > bitmap->bit_count)) {
        return -2;
    }
 
    /* begin and end are within the same index: */
    if ((begin / bits_per_index) == (end / bits_per_index)) {
        #define expr(begin, end) (~mask((begin) % bits_per_index) & mask((end) % bits_per_index))
        _Static_assert(expr(1,6) == 0b0000111110, "bitmap range logic invalid!");
        bitmap->data[begin / bits_per_index] |= expr(begin, end);
        #undef expr
    } else {
        bitmap->data[begin / bits_per_index] |= ~mask(begin % bits_per_index);
        for (size_t i = 1 + begin/bits_per_index;
             i < end/bits_per_index;
             i++)
        {
            bitmap->data[i] |= ~0;
        }
        bitmap->data[end / bits_per_index] |= mask(end % bits_per_index);
    }

    return 0;
}

int bitmap_clear_range(struct bitmap* bitmap, size_t begin, size_t end)
{
    if (end < begin) {
        return bitmap_set_range(bitmap, end, begin);
    }

    if (unlikely(begin > bitmap->bit_count)) {
        return -1;
    }

    if (unlikely(end > bitmap->bit_count)) {
        return -2;
    }
 
    /* begin and end are within the same index: */
    if ((begin / bits_per_index) == (end / bits_per_index)) {
        #define expr(begin, end) (~mask((begin) % bits_per_index) & mask((end) % bits_per_index))
        _Static_assert(expr(1,6) == 0b0000111110, "bitmap range logic invalid!");
        bitmap->data[begin / bits_per_index] &= ~expr(begin, end);
        #undef expr
    } else {
        bitmap->data[begin / bits_per_index] &= mask(begin % bits_per_index);
        for (size_t i = 1 + begin/bits_per_index;
             i < end/bits_per_index;
             i++)
        {
            bitmap->data[i] = 0;
        }
        bitmap->data[end / bits_per_index] &= ~mask(end % bits_per_index);
    }

    return 0;
}

/* This 4 am code is almost certainly wrong. No tests have been written yet.
 *
 * Returns 0 if range is empty. 
 * If range is not emtpy, it returns the offset of the first non-zero bit
 * from `begin`
 *
 * TODO: write tests */
int bitmap_range_empty(struct bitmap* bitmap, size_t begin, size_t end)
{
    if (end < begin) {
        return bitmap_set_range(bitmap, end, begin);
    }

    if (unlikely(begin > bitmap->bit_count)) {
        return -1;
    }

    if (unlikely(end > bitmap->bit_count)) {
        return -2;
    }
 
    /* begin and end are within the same index: */
    if ((begin / bits_per_index) == (end / bits_per_index)) {
        #define expr(begin, end) (~mask((begin) % bits_per_index) & mask((end) % bits_per_index))
        _Static_assert(expr(1,6) == 0b0000111110, "bitmap range logic invalid!");
        const typeof(bitmap->data[0]) n = bitmap->data[begin / bits_per_index];
        if (n & expr(begin, end)) {
            return trailing_zeroes(n) - (begin % bits_per_index) ;
        };
        #undef expr
    } else {
        {
            const typeof(bitmap->data[0]) n = bitmap->data[begin / bits_per_index];
            if (n & ~mask(begin % bits_per_index)) {
                return trailing_zeroes(n) - (begin % bits_per_index);
            }
        }

        for (size_t i = 1 + begin/bits_per_index;
             i < end/bits_per_index;
             i++)
        {
            const typeof(bitmap->data[0]) n = bitmap->data[i];
            if (n != 0) {
                return i*bits_per_index + trailing_zeroes(n);
            }
        }

        {
            const typeof(bitmap->data[0]) n = bitmap->data[end / bits_per_index];
            if (n & mask(end % bits_per_index)) {
                return (end % bits_per_index) - trailing_zeroes(n);
            }
            bitmap->data[end / bits_per_index] &= ~mask(end % bits_per_index);
        }
    }

    return 0;
}
