//we cant access the blocks properties so we hardcode everything
import { McBlocks } from "../reg/mcBlocks";
import { BlockComponentTypes, BlockPermutation, BlockPistonState, BlockTypes, Direction, ItemStack, LiquidType } from "@minecraft/server";
import { SuppTags } from "../reg/suppTags";
import { McTags } from "../reg/mcTags";
import { McItems } from "../reg/mcItems";
import { SuppItems } from "../reg/suppItems";
import { splitRes } from "./miscUtils";
import { McStates } from "../reg/mcStates";
import { flipDir, getADirection, offsetPos } from "./dirUtils";
import { SuppStates } from "../reg/suppStates";
// hardcoded vanilla properties due to UTTER lack of API & tags. thanks mojank.
// this took a long time...
const specialItemsToBlocks = new Map([
    [McItems.Seeds, McBlocks.Wheat],
    [McItems.MelonSeeds, McBlocks.MelonStem],
    [McItems.PumpkinSeeds, McBlocks.PumpkinStem],
    [McItems.BeetrootSeeds, McBlocks.Beetroot],
    [McItems.TorchFlowerSeeds, McBlocks.Torchflower],
    [McItems.PitcherPod, McBlocks.PitcherCrop],
    [McItems.Carrot, McBlocks.Carrots],
    [McItems.Potato, McBlocks.Potatoes],
    [SuppItems.FlaxSeeds.toString(), "razz_sup:flax_crop" /* SuppBlocks.Flax */.toString()]
]);
const inventories = new Set([
    McBlocks.Chest, McBlocks.Barrel, McBlocks.Hopper, McBlocks.EnderChest, McBlocks.Dispenser, McBlocks.Dropper,
    McBlocks.Furnace, McBlocks.BlastFurnace, McBlocks.Smoker, McBlocks.Grindstone, McBlocks.Crafter,
    McBlocks.Anvil, McBlocks.EnchantingTable, McBlocks.BrewingStand, McBlocks.CraftingTable,
    McBlocks.Stonecutter, McBlocks.Loom, McBlocks.CartographyTable, McBlocks.SmithingTable, McBlocks.ChippedAnvil, McBlocks.DamagedAnvil,
    McBlocks.CommandBlock, McBlocks.ChainCommandBlock, McBlocks.RepeatingCommandBlock, McBlocks.Jigsaw, McBlocks.StructureBlock
]);
const fourStageFlower = new Set([
    McBlocks.LeafLitter, McBlocks.WildFlowers, McBlocks.LeafLitter
]);
const opBlocks = new Set([
    McBlocks.CommandBlock, McBlocks.ChainCommandBlock, McBlocks.RepeatingCommandBlock,
    McBlocks.StructureBlock, McBlocks.StructureVoid, McBlocks.Jigsaw, McBlocks.Allow, McBlocks.Deny,
    McBlocks.BorderBlock, McBlocks.Barrier //no light too bad
]);
const amethystBudBlocks = [
    McBlocks.AmethystBudMedium, McBlocks.AmethystBudSmall, McBlocks.AmethystCluster,
    McBlocks.AmethystBudLarge
];
const mushrooms = new Set([
    McBlocks.BrownMushroom, McBlocks.RedMushroom, McBlocks.CrimsonFungus, McBlocks.WarpedFungus
]);
//cant be broken by explosion
const unbreakableBlocks = new Set([
    McBlocks.Bedrock, McBlocks.ReinforcedDeepslate, McBlocks.EnderChest,
    McBlocks.EnchantingTable, McBlocks.CryingObsidian,
    McBlocks.Obsidian, McBlocks.EndPortal,
    McBlocks.EndPortalFrame, McBlocks.NetherPortal, McBlocks.Vault, McBlocks.EndStone,
    McBlocks.RespawnAnchor, McBlocks.Spawner, McBlocks.NetheriteBlock,
    McBlocks.AncientDebris, McBlocks.Lodestone, McBlocks.TrialSpawner
]);
const replaceableBlocks = new Set([
    McBlocks.Air, McBlocks.FlowingWater, McBlocks.Water, McBlocks.FlowingLava,
    McBlocks.Lava, McBlocks.Vines, McBlocks.Fire, McBlocks.SoulFire,
    McBlocks.ShortGrass, McBlocks.SeaGrass, McBlocks.ShortDryGrass, McBlocks.Fern, McBlocks.Bush,
    McBlocks.TallDryGrass, McBlocks.TallGrass, McBlocks.LargeFern, McBlocks.NetherSprouts, McBlocks.DeadBush,
    McBlocks.SculkVein, McBlocks.GlowLichen, McBlocks.LeafLitter, McBlocks.CrimsonRoots, McBlocks.WarpedRoots
]);
const clickables = new Set([
    McBlocks.Jukebox, McBlocks.Lever, McBlocks.Bell, McBlocks.DecoratedPot,
    McBlocks.Comparator, McBlocks.UnpoweredComparator, McBlocks.Repeater,
    McBlocks.DaylightDetector, McBlocks.Cake, McBlocks.Vault, McBlocks.DragonEgg
]);
//write all we know here
const groundSmallPlants = new Set([
    McBlocks.SeaGrass, McBlocks.ShortDryGrass, McBlocks.ShortGrass, McBlocks.DeadBush,
    McBlocks.SmallDripleafBlock, McBlocks.BigDripleaf, McBlocks.FireflyBush, McBlocks.Kelp,
    McBlocks.TallDryGrass, McBlocks.TallGrass, McBlocks.LargeFern, McBlocks.ClosedEyeblossom,
    McBlocks.WildFlowers, McBlocks.LeafLitter, McBlocks.PinkPetals, McBlocks.CactusFlower,
    McBlocks.MangrovePropagule, McBlocks.PumpkinStem, McBlocks.MelonStem, McBlocks.Torchflower,
    McBlocks.Rosebush, McBlocks.Lilac, McBlocks.PitcherPlant, McBlocks.Bush, McBlocks.NetherSprouts,
    McBlocks.CrimsonRoots, McBlocks.WarpedRoots, McBlocks.RedTulip, McBlocks.OrangeTulip,
    McBlocks.WhiteTulip, McBlocks.PinkTulip, McBlocks.LilyOfTheValley, McBlocks.Azalea, McBlocks.FloweringAzalea,
    McBlocks.Dandelion, McBlocks.Poppy, McBlocks.Allium, McBlocks.Fern,
    McBlocks.Cornflower, McBlocks.OxeyeDaisy, McBlocks.WitherRose
]);
const groundSmallPlants3DItem = new Set([
    McBlocks.CactusFlower, McBlocks.SmallDripleafBlock, McBlocks.BigDripleaf,
    McBlocks.Azalea, McBlocks.FloweringAzalea
]);
const needSolidBelow = new Set([
    McBlocks.CocoaBeans, McBlocks.TurtleEgg, McBlocks.SeaPickle, McBlocks.Cake,
    McBlocks.PointedDripstone, McBlocks.Comparator, McBlocks.UnpoweredComparator,
    McBlocks.Repeater, McBlocks.RedstoneWire, McBlocks.Lantern, McBlocks.SoulLantern
]);
const doubleBlocks = new Set([
    McBlocks.TallGrass, McBlocks.LargeFern,
    McBlocks.Rosebush, McBlocks.Lilac, McBlocks.PitcherPlant, McBlocks.SmallDripleafBlock, McBlocks.Peony
]);
const pistons = new Set([
    McBlocks.Piston, McBlocks.StickyPiston, McBlocks.PistonArmCollision,
    McBlocks.StickyPistonArmCollision
]);
const hangingPlants = new Set([
    McBlocks.Vines, McBlocks.TwistingVines, McBlocks.WeepingVines,
    McBlocks.PaleHangingMoss, McBlocks.HangingRoots
]);
const items2d = new Set([
    McBlocks.Ladder, McBlocks.Hopper, McBlocks.Cake, McBlocks.BrewingStand, McBlocks.Bell,
    McBlocks.Lantern, McBlocks.SoulLantern, McBlocks.Torch, McBlocks.SoulLantern, McBlocks.RedstoneTorch,
    McBlocks.RedstoneWire, McBlocks.Cauldron, McBlocks.SeaPickle, McBlocks.IronBars, McBlocks.Lever,
    McBlocks.Campfire, McBlocks.SoulCampfire, McBlocks.ItemFrame, McBlocks.GlowItemFrame, McBlocks.LilyPad,
    McBlocks.Chain, McBlocks.FrogSpawn, McBlocks.SculkVein, McBlocks.GlowLichen, McBlocks.Cobweb,
    McBlocks.TripwireHook, McBlocks.FlowerPot
]);
const extraNoCollision = new Set([
    McBlocks.Cobweb, McBlocks.GlowLichen, McBlocks.SculkVein
]);
export function isHangingPlant(blockId) {
    return hangingPlants.has(blockId);
}
export function isItem2d(item) {
    const block = getBlockOfItem(item);
    if (block == undefined) {
        return true;
    }
    else {
        //this would be enough but we cant call it
        //  console.log(item.hasComponent("minecraft:block_placer"));
        const blockPerm = BlockPermutation.resolve(block.id);
        const id = item.typeId;
        if (items2d.has(item.typeId)
            || item.hasTag(SuppTags.Inventory2D) || isDoor(blockPerm) || isBed(id) ||
            isSign(id) || isRail(blockPerm) || isAmethystBud(id) || isStandingCoral(id) ||
            isCandle(id) || isGroundSmallPlant2D(blockPerm) || isHangingPlant(id)) {
            return true;
        }
        const [namespace, path] = splitRes(id);
        if (namespace == "minecraft" || namespace == "razz_sup" || true) {
            return false;
        }
        return true;
    }
}
export function isRail(block) {
    return block.hasTag(McTags.Rails);
}
export function isCandle(blockId) {
    return blockId.endsWith("candle");
}
export function isFourStageFlower(blockId) {
    return fourStageFlower.has(blockId);
}
export function isCrop(block) {
    return block.hasTag(McTags.Crops);
}
export function isOpBlock(blockId) {
    return opBlocks.has(blockId);
}
export function isGroundSmallPlant(block) {
    const id = block.type.id;
    return block.hasTag(McTags.Plant) ||
        isCrop(block) || groundSmallPlants.has(id) ||
        isSapling(id) || isMushroom(id);
}
export function isGroundSmallPlant2D(block) {
    return isGroundSmallPlant(block) && !groundSmallPlants3DItem.has(block.type.id);
}
//survives explosion basically
export function isUnbreakable(block) {
    let id = block.type.id;
    return unbreakableBlocks.has(id) || block.hasTag(SuppTags.Unbreakable) || isOpBlock(id);
}
export function isReplaceable(block) {
    let id = block.type.id;
    return replaceableBlocks.has(id) || block.hasTag(SuppTags.Replaceable);
}
export function isMushroom(blockId) {
    return mushrooms.has(blockId);
}
export function hasUI(blockId) {
    return inventories.has(blockId) || isSign(blockId) || isShulkerBox(blockId);
}
export function isSign(blockId) {
    return blockId.endsWith("sign");
}
export function isWallSign(blockId) {
    return blockId.endsWith("wall_sign");
}
export function isStandingSign(blockId) {
    return blockId.endsWith("standing_sign");
}
export function isWallBanner(blockId) {
    return blockId.endsWith("wall_banner");
}
export function isStandingBanner(blockId) {
    return blockId.endsWith("standing_banner");
}
export function isShulkerBox(blockId) {
    return blockId.endsWith("shulker_box");
}
export function isFence(blockId) {
    return blockId.endsWith("fence");
}
export function isWall(blockId) {
    return blockId.endsWith("wall");
}
export function isWallFan(blockId) {
    return blockId.endsWith("coral_wall_fan") || blockId.endsWith("coral_fan");
}
export function isStandingCoral(blockId) {
    return blockId.endsWith("coral") || blockId.endsWith("coral_fan");
}
export function isButton(blockId) {
    return blockId.endsWith("button");
}
export function isFenceGate(blockId) {
    return blockId.endsWith("fence_gate");
}
export function isBed(blockId) {
    return blockId.endsWith("bed");
}
export function isTorch(blockId) {
    return blockId.endsWith("torch");
}
export function isCarpet(blockId) {
    return blockId.endsWith("carpet");
}
export function isSlab(blockId) {
    return blockId.endsWith("slab");
}
export function isBottomSlab(block) {
    const id = block.typeId;
    if (isSlab(id)) {
        const half = block.permutation.getState(McStates.VerticalHalf);
        if (half != undefined && half == "bottom") {
            return true;
        }
    }
    return false;
}
export function isDoor(perm) {
    const id = perm.type.id;
    return id.endsWith("door") && !perm.hasTag(McTags.Trapdoors);
}
export function isAmethystBud(blockId) {
    return amethystBudBlocks.includes(blockId);
}
export function isFluid(blockId) {
    return blockId == McBlocks.Water || blockId == McBlocks.FlowingWater ||
        blockId == McBlocks.Lava || blockId == McBlocks.FlowingLava;
}
export function isLava(block) {
    return block.typeId == McBlocks.Lava ||
        block.typeId == McBlocks.FlowingLava;
}
export function hasWater(block) {
    return block.typeId == McBlocks.Water ||
        block.typeId == McBlocks.FlowingWater ||
        block.isWaterlogged;
}
export function isIce(block) {
    return block.typeId == McBlocks.Ice || block.typeId == McBlocks.PackedIce ||
        block.typeId == McBlocks.BlueIce;
}
export function isSapling(blockId) {
    return blockId.endsWith("sapling");
}
//vertical
export function isDoubleBlock(perm) {
    const id = perm.type.id;
    return doubleBlocks.has(id) || isDoor(perm);
}
export function isMultiBlock(perm) {
    const blockId = perm.type.id;
    //yes chests dont have a connected state... we'll have to deal with it
    return isDoubleBlock(perm) || isBed(blockId) || isPiston(blockId);
}
export function getDoubleBlockHalf(permutation) {
    let upperBit = permutation.getState(SuppStates.UpperBlock);
    if (upperBit == undefined) {
        upperBit = permutation.getState(McStates.UpperBlock);
    }
    if (upperBit != undefined) {
        return upperBit ? Direction.Up : Direction.Down;
    }
}
export function setDoubleBlockHalf(permutation, half) {
    if (permutation.getState(SuppStates.UpperBlock) != undefined) {
        return permutation.withState(SuppStates.UpperBlock, half == Direction.Up);
    }
    if (permutation.getState(McStates.UpperBlock) != undefined) {
        return permutation.withState(McStates.UpperBlock, half == Direction.Up);
    }
}
export function getConnectedBlock(block) {
    const id = block.typeId;
    if (isPiston(id)) {
        const piston = block.getComponent(BlockComponentTypes.Piston);
        if (piston && piston.state == BlockPistonState.Retracted)
            return undefined;
        let dir = getADirection(block);
        if (id == McBlocks.PistonArmCollision || id == McBlocks.PistonArmCollision) {
            dir = flipDir(dir);
        }
        return offsetPos(block, dir);
    }
    if (isDoubleBlock(block.permutation)) {
        const upper = getDoubleBlockHalf(block.permutation);
        return offsetPos(block, flipDir(upper));
    }
    if (isBed(id)) {
        const head = block.getComponent(McStates.BedHead);
        let dir = getADirection(block);
        dir = head ? flipDir(dir) : dir;
        return offsetPos(block, dir);
    }
    return undefined;
}
export function isPiston(blockId) {
    return pistons.has(blockId);
}
export function isPressurePlate(blockId) {
    return blockId.endsWith("pressure_plate");
}
export function hasStrongClickAction(block) {
    const id = block.typeId;
    //check if the block is in the inventories array
    return hasUI(id) || isButton(id) || isFenceGate(id) || isBed(id) || isDoor(block.permutation) ||
        block.hasTag(SuppTags.HasEmptyHandUseAction) || clickables.has(id);
}
export function needsSolidBlockBehind(block) {
    const id = block.type.id;
    return isTorch(id) || isWallFan(id) || isButton(id) || isWallSign(id) ||
        isWallBanner(id) || isAmethystBud(id) || id == McBlocks.GlowLichen ||
        id == McBlocks.SculkVein || id == McBlocks.Lever || id == McBlocks.CocoaBeans || id == McBlocks.Vines ||
        block.hasTag(SuppTags.NeedsSolidBehind);
}
//only and always below
export function needsSolidBlockBelow(block) {
    const id = block.type.id;
    return block.hasTag(McTags.Rails) || isGroundSmallPlant(block) ||
        isStandingBanner(id) || isStandingSign(id) ||
        isCarpet(id) || isStandingCoral(id) ||
        isDoor(block) || isPressurePlate(id) || needSolidBelow.has(id) ||
        block.hasTag(SuppTags.NeedsSolidBelow);
}
export function isMovingBlock(block) {
    const id = block.typeId;
    return id == McBlocks.StickyPistonArmCollision || id == McBlocks.PistonArmCollision || id == McBlocks.MovingBlock;
}
export function isUnPushable(block) {
    const piston = block.getComponent(BlockComponentTypes.Piston);
    if (piston) {
        return piston.isMoving || piston.state != BlockPistonState.Retracted;
    }
    return isUnbreakable(block.permutation) || (isMultiBlock(block.permutation) && !breakWhenPushed(block)) || isMovingBlock(block);
}
export function breakWhenPushed(block) {
    return block.canBeDestroyedByLiquidSpread(LiquidType.Water) ||
        isReplaceable(block) || needsSolidBlockBelow(block.permutation) || needsSolidBlockBehind(block.permutation);
}
export function isDirt(block) {
    return block.hasTag(McTags.Dirt);
}
export function isGlass(block) {
    return block.endsWith("glass");
}
export function isSand(block) {
    return block.hasTag(McTags.Sand);
}
export function isCorrectSoilForCrop(toPlace, belowBlock) {
    if (isCrop(toPlace)) {
        return belowBlock.typeId == McBlocks.Farmland;
    }
    if (isGroundSmallPlant(toPlace)) {
        return isDirt(belowBlock.permutation);
    }
    return undefined;
}
export function hasNoCollision(block) {
    return isGroundSmallPlant2D(block.permutation) || extraNoCollision.has(block.typeId) ||
        block.hasTag(SuppTags.NoCollision);
}
export function getItemOfBlock(block) {
    const id = block.typeId;
    return new ItemStack(id);
}
export function getBlockOfItem(item) {
    let id = item.typeId;
    const special = specialItemsToBlocks.get(id);
    if (special) {
        id = special;
    }
    return BlockTypes.get(id);
}
