import { world, BlockVolume, Entity, ItemStack, Player, system, ListBlockVolume, Dimension } from "@minecraft/server";
import V3 from './utilities/V3';

const interactionCooldown = new Map()

const fishSpawnDelay = new Map([
    ["cyd_dino:bary", 4.9],
    ["cyd_dino:icty", 1.9],
    ["cyd_dino:suchomimus", 2.8]
])

const stoneBreakDelay = new Map([
    ["cyd_dino:anky", 1.36],
    ["cyd_dino:majungosaurus", 1.3667],
    ["cyd_dino:carno", 0.63]
])

const stoneBreakRemainder = new Map([
    ["cyd_dino:anky", 1.64],
    ['cyd_dino:majungosaurus', 2.2333 - 1.3667],
    ['cyd_dino:carno', 1.04 - 0.63]
])

const stompBreakDelay = new Map([
    ["cyd_dino:stego", 2.24],
    ["cyd_dino:trex", 1.34]
])

/**
 * @param {Entity} entity 
 * @param {Player} player 
 * @param {ItemStack} itemStack 
 * @returns 
 */
export async function handleRiderInteract(entity, player, itemStack) {
    if (!interactionCooldown.has(player.id)) interactionCooldown.set(player.id, 0);

    if (system.currentTick - interactionCooldown.get(player.id) < 20) return;

    const { dimension } = player
    const utilty = entity.getProperty('cyd_dino:utility')
    if (!utilty) return

    if (utilty != 'stomp' && utilty != 'stone_breaker') return;

    if (utilty == 'stone_breaker') { handleStoneBreaker(entity, player, dimension); return }

    if (utilty == 'stomp' && entity.getProperty('cyd_dino:is_stomping')) return;

    const onBlock = dimension.getBlock(entity.location).below()
    await system.waitTicks(1);

    entity.triggerEvent('cyd_dino:' + utilty)

    const cooldown = (utilty == 'stomp' ? stompBreakDelay.get(entity.typeId) : stoneBreakDelay.get(entity.typeId)) ?? 0
    if (cooldown > 0) await system.waitTicks(cooldown * 20)

    const box = utilty == 'stomp' ? new BlockVolume(onBlock.offset({ x: 2, y: 0, z: 2 }), onBlock.offset({ x: -2, y: 0, z: -2 })) : getCube(entity.location, entity.getViewDirection())
    Array.from(dimension.getBlocks(box, { includeTags: ['minecraft:is_pickaxe_item_destructible', 'minecraft:is_shovel_item_destructible'] }, false).getBlockLocationIterator()).forEach(location => {
        dimension.runCommand(`setblock ${location.x} ${location.y} ${location.z} air destroy`)
    })

    interactionCooldown.set(player.id, system.currentTick)

    await system.waitTicks(1);
    entity.triggerEvent('cyd_dino:end_' + utilty)
}

/**
 * @param {Dimension} dimension 
 */
async function handleStoneBreaker(entity, player, dimension) {

    if (entity.getProperty('cyd_dino:is_breaking_stone')) return;

    await system.waitTicks(1);

    entity.triggerEvent('cyd_dino:stone_breaker')

    const cooldown = stoneBreakDelay.get(entity.typeId) ?? 0
    if (cooldown > 0) await system.waitTicks(cooldown * 20)

    const snappedDir = snapToCardinal(entity.getViewDirection());
    const block = dimension.getBlockFromRay(entity.location, snappedDir, { maxDistance: 2 })?.block
    Array.from(dimension.getBlocks(getCube(block === undefined ? entity.location : block.offset(new V3(snappedDir).scale(-2)), entity.getViewDirection()), { includeTags: ['minecraft:is_pickaxe_item_destructible', 'minecraft:is_shovel_item_destructible'] }, false).getBlockLocationIterator()).forEach(location => {
        dimension.runCommand(`setblock ${location.x} ${location.y} ${location.z} air destroy`)
    })

    await system.waitTicks(stoneBreakRemainder.get(entity.typeId) * 20)

    interactionCooldown.set(player.id, system.currentTick)

    await system.waitTicks(1);
    entity.triggerEvent('cyd_dino:end_stone_breaker')

}

world.afterEvents.dataDrivenEntityTrigger.subscribe(event => {
    const { entity } = event;
    const { dimension, location } = entity;
    const { x, y, z } = location;
    try {
        if (!dimension.containsBlock(new BlockVolume({ x: x + 3, y: y + 2, z: z + 3 }, { x: x - 3, y: y - 2, z: z - 3 }), { includeTypes: ['minecraft:water', 'minecraft:flowing_water'] }, false)) return;
    } catch (error) { return; }
    entity.triggerEvent('cyd_dino:fisher')
}, { eventTypes: ['cyd_dino:check_fisher'] })

world.afterEvents.dataDrivenEntityTrigger.subscribe(async event => {
    const { entity } = event;
    const { dimension, location } = entity;
    const cooldown = fishSpawnDelay.get(entity.typeId) ?? 0
    if (cooldown > 0) await system.waitTicks(cooldown * 20)
    dimension.spawnItem(new ItemStack(['salmon', 'cod', 'pufferfish', 'tropical_fish'][Math.floor(Math.random() * 4)], Math.floor(Math.random() * 3 + 1)), location)
    entity.triggerEvent('cyd_dino:end_fisher')
}, { eventTypes: ['cyd_dino:fisher'] })

const validBlocks = new Set([
    "minecraft:acacia_log",
    "minecraft:birch_log",
    "minecraft:cherry_log",
    "minecraft:dark_oak_log",
    "minecraft:jungle_log",
    "minecraft:mangrove_log",
    "minecraft:oak_log",
    "minecraft:pale_oak_log",
    "minecraft:spruce_log",
    "minecraft:acacia_leaves",
    "minecraft:azalea_leaves",
    "minecraft:azalea_leaves_flowered",
    "minecraft:birch_leaves",
    "minecraft:cherry_leaves",
    "minecraft:dark_oak_leaves",
    "minecraft:jungle_leaves",
    "minecraft:mangrove_leaves",
    "minecraft:oak_leaves",
    "minecraft:pale_oak_leaves",
    "minecraft:spruce_leaves",
    "cyd_dino:sigillaria_leaves_bottom_middle",
    "cyd_dino:sigillaria_leaves_bottom_corner",
    "cyd_dino:metasequoia_leaves",
    "cyd_dino:araucaria_leaves",
    "cyd_dino:sigillaria_leaves_top_side",
    "cyd_dino:conifer_leaves",
    "cyd_dino:fan_palm_leaves_1",
    "cyd_dino:bennettitales_leaves",
    "cyd_dino:sigillaria_leaves_bottom_side",
    "cyd_dino:cordaites_leaves",
    "cyd_dino:podocarps_leaves",
    "cyd_dino:fan_palm_leaves_2",
    "cyd_dino:dicksonia_leaves_corner",
    "cyd_dino:ginkgo_leaves",
    "cyd_dino:dicksonia_leaves_corner_tip",
    "cyd_dino:dicksonia_leaves_tip",
    "cyd_dino:sigillaria_leaves_top_middle",
    "cyd_dino:cycads_leaves",
    "cyd_dino:dicksonia_leaves",
    "cyd_dino:fan_palm_leaves_apex",
    "cyd_dino:araucaria_broken_twigs",
    "cyd_dino:araucaria_log",
    "cyd_dino:bennettitales_log",
    "cyd_dino:cheirolepidiceae_thick",
    "cyd_dino:cheirolepidiceae_thin",
    "cyd_dino:conifer_log",
    "cyd_dino:burned_log",  
    "cyd_dino:burned_twigs",  
    "cyd_dino:cordaites_log",
    "cyd_dino:cycads_log",
    "cyd_dino:dicksonia_log",
    "cyd_dino:fan_palm_log_base",
    "cyd_dino:fan_palm_log",
    "cyd_dino:fan_palm_top_log",
    "cyd_dino:ginkgo_log",
    "cyd_dino:metasequoia_log",
    "cyd_dino:podocarps_log",
    "cyd_dino:sigillaria_log",
    "cyd_dino:fan_palm_apex_log",
    "cyd_dino:fan_palm_stems",
    "cyd_dino:bennettitales_fruit",
    "cyd_dino:bennettitales_outcrop"
])

system.afterEvents.scriptEventReceive.subscribe(event => {
    const { id, message, sourceEntity } = event;

    if (!sourceEntity) return;
    if (id != 'cyd_dino:tree_breaker') return;

    const dimension = sourceEntity.dimension
    const size = parseInt(message)
    const entityLocation = sourceEntity.location
    const viewVector = sourceEntity.getViewDirection()
    const right = { x: viewVector.z, y: 0, z: -viewVector.x }

    for (let t = 0; t < 4; t++) {
        const baseX = entityLocation.x + viewVector.x * (t + 1);
        const baseZ = entityLocation.z + viewVector.z * (t + 1);
        for (let i = -size; i < size; i++) {
            const offsetX = right.x * i;
            const offsetZ = right.z * i;
            for (let j = -1; j <= size + 2; j++) {
                const location = { x: Math.floor(baseX + offsetX), y: entityLocation.y + j, z: Math.floor(baseZ + offsetZ) };
                const block = dimension.getBlock(location);
                if (!block || !validBlocks.has(block.typeId)) continue;
                dimension.runCommand(`setblock ${location.x} ${location.y} ${location.z} air destroy`);
            }
        }
    }

}, { namespaces: ['cyd_dino'] })

function snapToCardinal(vec) {
    if (Math.abs(vec.x) > Math.abs(vec.z)) return { x: Math.sign(vec.x), y: 0, z: 0 }
    else return { z: Math.sign(vec.z), y: 0, x: 0 }
}

function getCube(entityPos, viewDir) {
    viewDir = snapToCardinal(viewDir)
    const right = { x: -viewDir.z, z: viewDir.x }

    const start = {
        x: entityPos.x + viewDir.x * 2,
        y: entityPos.y,
        z: entityPos.z + viewDir.z * 2
    };

    const from = {
        x: start.x + right.x * -2,
        y: start.y + 4,
        z: start.z + right.z * -2
    };

    const to = {
        x: start.x + right.x * 2 + viewDir.x * 1,
        y: start.y,
        z: start.z + right.z * 2 + viewDir.z * 1
    };

    return new BlockVolume(from, to);
}