#include <hilbert/kernel/storage.hpp>

namespace hilbert::kernel::storage {

  bd_result block_device::load_cache_block(uint64_t i) {

    if (block_cache_i == i)
      return bd_result::success;

    bd_result result = read_blocks_no_cache(i, 1, block_cache);

    if (result != bd_result::success) {
      block_cache_i = block_count;
      return result;
    }

    block_cache_i = i;
    return bd_result::success;

  }

  bd_result block_device::read_bytes(
    uint64_t start, uint64_t count, void *into
  ) {

    if (start + count > block_size * block_count)
      return bd_result::out_of_bounds;

    uint8_t *into_u8 = (uint8_t *)into;

    if (start % block_size != 0) {
      uint64_t prefix_len = block_size - start % block_size;
      bd_result result = load_cache_block(start / block_size);
      if (result != bd_result::success)
        return result;
      for (uint64_t i = 0; i < prefix_len; ++i)
        into_u8[i] = block_cache[start % block_size + i];
      into_u8 += prefix_len;
      start += prefix_len;
      count -= prefix_len;
    }

    uint64_t postfix_start = ((start + count) / block_size) * block_size;

    if (postfix_start != start) {
      bd_result result = read_blocks_no_cache(
        start / block_size, (postfix_start - start) / block_size, into_u8);
      if (result != bd_result::success)
        return result;
      count -= postfix_start - start;
      into_u8 += postfix_start - start;
      start = postfix_start;
    }

    if (count != 0) {
      bd_result result = load_cache_block(start / block_size);
      if (result != bd_result::success)
        return result;
      for (uint64_t i = 0; i < count; ++i)
        into_u8[i] = block_cache[i];
    }

    return bd_result::success;

  }

}
