#include "common/BlockStorage.h"

static constexpr Block::Id EMPTY_BLOCK = 65535;

BlockStorage::Map::Map(int length, Block::Id id) : blocks(length, 1) {
    blocks.fill(0);
    map.reserve(2);
    map.add(id);
    map.add(EMPTY_BLOCK);
}

Block::Id BlockStorage::Map::get(int index) const {
    return map[blocks.get(index)];
}

void BlockStorage::Map::set(int index, Block::Id id) {
    for(int i = 0; i < map.getLength(); i++) {
        if(map[i] == id) {
            blocks.set(index, i);
            return;
        } else if(map[i] == EMPTY_BLOCK) {
            map[i] = id;
            blocks.set(index, i);
            return;
        }
    }
    blocks.resize(blocks.getBits() + 1);
    int mapId = map.getLength();
    map.resize(mapId * 2, EMPTY_BLOCK);
    map[mapId] = id;
    blocks.set(index, mapId);
}

BlockStorage::BlockStorage(int sizeBits, int heightBits)
    : size(1 << sizeBits), height(1 << heightBits), sizeMask(size - 1) {
    maps.resize((size * size * height) / SEGMENT);
}

Block::Id BlockStorage::get(int x, int y, int z) const {
    if(y < 0 || y >= height) {
        return y < 0;
    }
    x &= (size - 1);
    z &= (size - 1);

    int index = y * size * size + x * size + z;
    int segmentIndex = index >> SEGMENT_BITS;

    if(maps[segmentIndex] == nullptr) {
        return 0;
    }
    return maps[segmentIndex]->get(index & SEGMENT_MASK);
}

void BlockStorage::set(int x, int y, int z, Block::Id id) {
    if(y < 0 || y >= height) {
        return;
    }
    x &= (size - 1);
    z &= (size - 1);

    int index = y * size * size + x * size + z;
    int segmentIndex = index >> SEGMENT_BITS;

    if(maps[segmentIndex] == nullptr) {
        if(id == 0) {
            return;
        }
        maps[segmentIndex] = new Map(SEGMENT, 0);
    }
    maps[segmentIndex]->set(index & SEGMENT_MASK, id);
}

int BlockStorage::getSize() const {
    return size;
}

int BlockStorage::getHeight() const {
    return height;
}