import { BlockComponentTypes, EntityComponentTypes, ItemStack, system, world } from "@minecraft/server";
import { pushEntity, shrinkIfInSurvival, splitRes, transmuteBlock } from "../util/miscUtils";
import { res } from "../main";
import { getConnectedBlock } from "../util/blockProperties";
import { SuppParticles } from "../reg/suppParticles";
import { McBlocks } from "../reg/mcBlocks";
import { SuppItems } from "../reg/suppItems";
import { SuppEntities } from "../reg/suppEntities";
import { McEntities } from "../reg/mcEntities";
import { blockEntityManager } from "../blockEntitiesManager";
import { random, randomBool, vecAdd, vecNormalize, vecScale } from "../util/mthUtils";
import { setCushionColor } from "./cushionStuff";
const COLORS = [
    "light_gray",
    "light_blue",
    "white",
    "orange",
    "magenta",
    "yellow",
    "lime",
    "pink",
    "gray",
    "cyan",
    "purple",
    "blue",
    "brown",
    "black",
    "green",
    "red",
    "pink"
];
const BLACKLIST = [
    "glazed_terracotta"
];
const SPECIAL = new Map([
    [McBlocks.StickyPiston, McBlocks.Piston],
    [McBlocks.WhiteStainedGlass, McBlocks.Glass],
    [McBlocks.WhiteStainedGlassPane, McBlocks.GlassPane],
    [McBlocks.WaxedCopperBlock, McBlocks.CopperBlock],
    [McBlocks.StickyPistonArmCollision, McBlocks.PistonArmCollision],
    ["razz_sup:bamboo_spikes_decay" /* SuppBlocks.SpikesDecay */.toString(), "razz_sup:bamboo_spikes" /* SuppBlocks.Spikes */.toString()],
    ["razz_sup:bamboo_spikes_wind_charging" /* SuppBlocks.SpikesWindCharging */.toString(), "razz_sup:bamboo_spikes" /* SuppBlocks.Spikes */.toString()],
    ["razz_sup:bamboo_spikes_harming" /* SuppBlocks.SpikesHarming */.toString(), "razz_sup:bamboo_spikes" /* SuppBlocks.Spikes */.toString()],
    ["razz_sup:bamboo_spikes_infestation" /* SuppBlocks.SpikesInfestation */.toString(), "razz_sup:bamboo_spikes" /* SuppBlocks.Spikes */.toString()],
    ["razz_sup:bamboo_spikes_oozing" /* SuppBlocks.SpikesOozing */.toString(), "razz_sup:bamboo_spikes" /* SuppBlocks.Spikes */.toString()],
    ["razz_sup:bamboo_spikes_poison" /* SuppBlocks.SpikesPoison */.toString(), "razz_sup:bamboo_spikes" /* SuppBlocks.Spikes */.toString()],
    ["razz_sup:bamboo_spikes_slowness" /* SuppBlocks.SpikesSlowness */.toString(), "razz_sup:bamboo_spikes" /* SuppBlocks.Spikes */.toString()],
    ["razz_sup:bamboo_spikes_weakness" /* SuppBlocks.SpikesWeakness */.toString(), "razz_sup:bamboo_spikes" /* SuppBlocks.Spikes */.toString()],
    ["razz_sup:bamboo_spikes_weaving" /* SuppBlocks.SpikesWeaving */.toString(), "razz_sup:bamboo_spikes" /* SuppBlocks.Spikes */.toString()],
    ["razz_sup:globe_sepia" /* SuppBlocks.SepiaGlobe */.toString(), "razz_sup:globe" /* SuppBlocks.Globe */.toString()]
]);
function isBlacklisted(suffixWithoutColor) {
    return BLACKLIST.includes(suffixWithoutColor);
}
//color
function getUncoloredBlock(block) {
    const id = block.typeId;
    const [namespace, suffix] = splitRes(id);
    // Match colors in any position (prefix, suffix, or middle)
    const matchedColor = COLORS.find(color => {
        return (suffix === color || // Exact match (e.g., "white_wool" → "wool")
            suffix.startsWith(`${color}_`) || // Prefix (e.g., "blue_wool")
            suffix.endsWith(`_${color}`) || // Suffix (e.g., "wool_blue")
            suffix.includes(`_${color}_`) // Middle (e.g., "stained_glass_blue")
        );
    });
    if (!matchedColor)
        return undefined;
    // Remove the color and try (e.g., "blue_wool" → "wool")
    let suffixWithoutColor = suffix
        .replace(new RegExp(`^${matchedColor}_`), '') // Remove prefix
        .replace(new RegExp(`_${matchedColor}$`), '') // Remove suffix
        .replace(new RegExp(`_${matchedColor}_`), '_'); // Remove middle
    if (isBlacklisted(suffixWithoutColor))
        return undefined;
    if (suffixWithoutColor == "terracotta") {
        suffixWithoutColor = "hardened_clay"; //bs bedrock stuff
    }
    const newIdWithoutColor = `${namespace}:${suffixWithoutColor}`;
    const permWithoutColor = transmuteBlock(block.permutation, newIdWithoutColor, ["color"]);
    if (permWithoutColor)
        return permWithoutColor;
    // Replace the color with "white" (e.g., "blue_wool" → "white_wool")
    const replacedSuffix = suffix
        .replace(new RegExp(`^${matchedColor}(?=_|$)`), 'white') // Prefix
        .replace(new RegExp(`(?<=_)${matchedColor}(?=_|$)`), 'white'); // Middle/Suffix
    const newIdWithColor = res(namespace, replacedSuffix);
    return transmuteBlock(block.permutation, newIdWithColor, ["color"]);
}
//waxed
function getUnWaxedBlock(block) {
    const id = block.typeId;
    const [namespace, suffix] = splitRes(id);
    if (suffix.startsWith("waxed_")) {
        const suffixWithoutWax = suffix.replace("waxed_", "");
        const newIdWithoutWax = res(namespace, suffixWithoutWax);
        return transmuteBlock(block.permutation, newIdWithoutWax, ["waxed"]);
    }
    else if (suffix.endsWith("_waxed")) {
        const suffixWithoutWax = suffix.replace("_waxed", "");
        const newIdWithoutWax = res(namespace, suffixWithoutWax);
        return transmuteBlock(block.permutation, newIdWithoutWax, ["waxed"]);
    }
    return undefined;
}
//special
function getSpacialWashedBlock(block) {
    const id = block.type.id;
    const [namespace, suffix] = splitRes(id);
    const s = SPECIAL.get(id);
    if (s != undefined) {
        return transmuteBlock(block, s);
    }
    return undefined;
}
function getWashedBlock(block) {
    let newBlock = getUncoloredBlock(block);
    newBlock = getSpacialWashedBlock(newBlock ?? block.permutation) ?? newBlock;
    if (newBlock)
        return newBlock;
    newBlock = getUnWaxedBlock(block);
    if (newBlock)
        return newBlock;
    if (newBlock) {
        return newBlock;
    }
    return undefined;
}
function washBlockComponents(block) {
    const component = block.getComponent(BlockComponentTypes.Sign);
    if (component) {
        if (component.isWaxed) {
            system.run(() => {
                component.setWaxed(false);
            });
            return true;
        }
    }
    return false;
}
export const SoapItemComponent = {
    onUseOn(event) {
        //just for anim
    }
};
world.beforeEvents.playerInteractWithBlock.subscribe((event => {
    if (!event.isFirstEvent)
        return;
    const item = event.itemStack;
    if (item && item.typeId == SuppItems.Soap) {
        const block = event.block;
        let success = washBlockComponents(block);
        if (!success) {
            const newBlockState = getWashedBlock(block);
            if (newBlockState) {
                system.run(() => {
                    if (newBlockState.type.id == McBlocks.Piston) {
                        block.setType(McBlocks.Air);
                    }
                    //ugly but whatever. manager actually sets the block
                    blockEntityManager.transmuteBlockEntity(block, newBlockState);
                    const connected = getConnectedBlock(block);
                    if (connected) {
                        const newConnectedState = getWashedBlock(connected);
                        if (newConnectedState) {
                            connected.setPermutation(newConnectedState);
                        }
                    }
                });
                success = true;
            }
        }
        if (success) {
            system.run(() => {
                shrinkIfInSurvival(event.player, event.itemStack);
                block.dimension.spawnParticle(SuppParticles.SoapWash, block.center());
                block.dimension.playSound("bucket.empty", block.center(), { pitch: 1.3, volume: 0.8 });
                //TODO: wash sound here
            });
            event.cancel = true;
        }
    }
}));
world.beforeEvents.playerInteractWithEntity.subscribe((event) => {
    const item = event.itemStack;
    if (item && item.typeId == SuppItems.Soap) {
        let success = false;
        const entity = event.target;
        if (entity.typeId == SuppEntities.Cushion) {
            const variant = entity.getComponent(EntityComponentTypes.Variant);
            if (variant != undefined && variant.value != 0) {
                system.run(() => {
                    setCushionColor(entity, new ItemStack(SuppItems.CushionWhite));
                    //TODO:figure out better particles for entities
                    //oh oh hear this.
                    //add an animation system thats decoupled from entities. Really cool!
                    //then proceed it to hard couple JUSt so you can have "friendly names" in the aniation. Yes you need to define sounds and particles IN AN ENTITY.....ANd we cant edit an existing entity. that defeats the point of the decoupling
                    //entity.playAnimation("animation.razz_sup.entity_wash")
                    entity.dimension.spawnParticle(SuppParticles.SoapWash, entity.location);
                    entity.dimension.playSound("bucket.empty", entity.location, { pitch: 1.3, volume: 0.8 });
                    //TODO: proper sound
                });
                success = true;
            }
        }
        else if (entity.typeId == McEntities.Sheep) {
            const color = entity.getComponent(EntityComponentTypes.Color);
            if (color.value != 0) {
                system.run(() => {
                    color.value = 0;
                    entity.dimension.spawnParticle(SuppParticles.SoapWash, entity.location);
                });
                //sound
                success = true;
            }
        }
        if (success) {
            event.cancel = true;
        }
    }
});
const isOnSoap = new Set();
export const SoapBlockComponent = {
    onStepOn(event) {
        isOnSoap.add(event.entity.id);
    },
    onStepOff(event) {
        const entity = event.entity;
        if (!isFeetBlockSoap(entity)) {
            isOnSoap.delete(entity.id);
        }
    }
};
function isFeetBlockSoap(entity) {
    const feetState = entity.dimension.getBlock(vecAdd(entity.location, 0, -0.1, 0));
    return !(!feetState || feetState.typeId != "razz_sup:soap_block" /* SuppBlocks.SoapBlock */);
}
system.runInterval(() => {
    for (const id of isOnSoap) {
        const entity = world.getEntity(id);
        if (!entity || !entity.isValid()) {
            isOnSoap.delete(id);
            continue;
        }
        if (entity.isSneaking) {
            continue;
        }
        const vel = entity.getVelocity();
        const horizontalVelSq = vel.x * vel.x + vel.z * vel.z;
        if (horizontalVelSq > 0.0013 && Math.random() < 0.6) {
            entity.dimension.spawnParticle(SuppParticles.SoapSlip, entity.location);
            //TODO: slip sound
            const horizontalVec = { x: vel.x, y: 0, z: vel.z };
            //rotate this vec by a random angle between 90 and -90. then noralize it
            const angle = (randomBool() ? random(-110, -70) : random(70, 110)) * Math.PI / 180;
            const cos = Math.cos(angle);
            const sin = Math.sin(angle);
            const rotatedVec = {
                x: horizontalVec.x * cos - horizontalVec.z * sin,
                y: 0,
                z: horizontalVec.x * sin + horizontalVec.z * cos
            };
            const [len, norm] = vecNormalize(rotatedVec);
            const scaled = vecScale(norm, 0.2);
            pushEntity(entity, scaled);
        }
    }
}, 10);
