import { world } from '@minecraft/server';
import getBlockTransformation from './blockMap';
import { Chunk3Utils } from '../util/chunk';
import WBVector3 from '../util/wbVector3';
import { gaussianRandom, mulberryPRNG } from '../util/random';
import LRUCache from '../util/lruCache';
import { BLOCKS_PER_CHUNK, CHUNK_SIZE, ChunkDataMap } from './chunk';
const PROGRESS_PASSTHROUGH_THRESHOLD = Math.floor(BLOCKS_PER_CHUNK * 0.5);
const PROGRESS_PASSTHROUGH_CHANCE = 0.5;
const CHUNK_PASSTHROUGH_LIMIT = 5;
const CHUNK_INFECTED_THRESHOLD = Math.floor(BLOCKS_PER_CHUNK * 0.5);
const NUM_CONVERTED_PER_ITERATION = 10;
const DEFAULT_ARRAY = new Uint16Array(BLOCKS_PER_CHUNK);
for (let i = 0; i < BLOCKS_PER_CHUNK; i++) {
    DEFAULT_ARRAY[i] = i;
}
const chunkUtils = new Chunk3Utils(CHUNK_SIZE);
const overworld = world.getDimension('overworld');
const arrayCache = new LRUCache(100);
export function transformBlock(block) {
    const transformation = getBlockTransformation(block.typeId);
    transformation?.transform(block);
}
function getShuffledArray(chunkData) {
    const chunk = chunkData.getChunk();
    const propertyId = chunkData.getPropertyId();
    const cached = arrayCache.get(chunkData.getPropertyId());
    if (cached)
        return cached;
    const seed = 2394 * chunk.x + 923333 * chunk.y + 86753 * chunk.z;
    const rand = mulberryPRNG(seed);
    const arr = new Uint16Array(BLOCKS_PER_CHUNK);
    for (let i = 0; i < BLOCKS_PER_CHUNK; i++) {
        const j = Math.floor(rand() * i);
        // arr[i] = DEFAULT_ARRAY[i];
        arr[i] = arr[j];
        arr[j] = DEFAULT_ARRAY[i];
    }
    arrayCache.set(propertyId, arr);
    return arr;
}
function getNextLocation(chunkData, progress) {
    const arr = getShuffledArray(chunkData);
    const idx = arr[progress];
    const y = Math.floor(idx / (CHUNK_SIZE * CHUNK_SIZE));
    const xz = idx % (CHUNK_SIZE * CHUNK_SIZE);
    const x = Math.floor(xz / CHUNK_SIZE);
    const z = idx % CHUNK_SIZE;
    const offset = new WBVector3(x, y, z);
    return chunkUtils.fromChunkSpace(chunkData.getChunk()).add(offset);
}
export function infectNearbyBlock(location) {
    const direction = new WBVector3(gaussianRandom(), gaussianRandom(0, 0.5), gaussianRandom()).normalize();
    let chunk = chunkUtils.toChunkSpace(location).add({
        x: Math.random() - 0.5,
        y: Math.random() - 0.5,
        z: Math.random() - 0.5,
    });
    const chunkData = new ChunkDataMap(chunk.floorAll());
    let progress;
    let i = 0;
    // Find chunk that is not (nearly) full
    // eslint-disable-next-line no-constant-condition
    while (true) {
        progress = chunkData.get();
        if (progress < BLOCKS_PER_CHUNK) {
            if (progress < PROGRESS_PASSTHROUGH_THRESHOLD
                || Math.random() > PROGRESS_PASSTHROUGH_CHANCE) {
                break;
            }
        }
        else if (++i >= CHUNK_PASSTHROUGH_LIMIT) {
            return undefined;
        }
        chunk = chunk.add(direction);
        chunkData.selectChunk(chunk.floorAll());
    }
    const maxTransformations = Math.min(NUM_CONVERTED_PER_ITERATION, BLOCKS_PER_CHUNK - progress);
    for (i = 0; i < maxTransformations; i++) {
        // Find next location in this chunk
        const nextLoc = getNextLocation(chunkData, progress);
        let block;
        try {
            block = overworld.getBlock(nextLoc);
        }
        catch (e) {
            return undefined;
        }
        if (block) {
            const biome = chunkData.getBiome();
            const newBiome = getBlockTransformation(block.typeId, biome)?.transform(block);
            chunkData.set(++progress);
            if (newBiome && !biome)
                chunkData.setBiome(newBiome);
        }
    }
    return undefined;
}
export function isLocationInfected(location) {
    const chunk = chunkUtils.toChunkSpace(location).floorAll();
    const chunkData = new ChunkDataMap(chunk);
    return (chunkData.get() > CHUNK_INFECTED_THRESHOLD);
}
