package eu.mhsl.minecraft.pixelblocks.pixelblock;

import eu.mhsl.minecraft.pixelblocks.Main;
import eu.mhsl.minecraft.pixelblocks.utils.Direction;
import eu.mhsl.minecraft.pixelblocks.utils.EntityTagUtil;
import eu.mhsl.minecraft.pixelblocks.utils.ListUtil;
import org.bukkit.Location;
import org.bukkit.NamespacedKey;
import org.bukkit.block.Banner;
import org.bukkit.block.BlockFace;
import org.bukkit.block.BlockState;
import org.bukkit.block.data.Directional;
import org.bukkit.block.data.Rotatable;
import org.bukkit.block.data.type.Bed;
import org.bukkit.block.data.type.Chest;
import org.bukkit.block.data.type.EnderChest;
import org.bukkit.entity.BlockDisplay;
import org.bukkit.entity.Entity;
import org.bukkit.entity.EntityType;
import org.bukkit.util.Transformation;
import org.bukkit.util.Vector;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.joml.Vector3d;

import java.util.List;

public class Pixels {
    private static final NamespacedKey pixelOfTag = new NamespacedKey(Main.plugin(), "pixel_of");

    private final @NotNull PixelBlock parentBlock;

    public Pixels(@NotNull PixelBlock parentBlock) {
        this.parentBlock = parentBlock;
    }

    public void spawn() {
        ListUtil.splitListInParts(10, this.parentBlock.getPixelData())
            .forEach(pixels -> parentBlock.getBlockTaskChain()
                .delay(1)
                .sync(() -> pixels.forEach(this::spawnPixel))
                .execute());
    }

    private void spawnPixel(PixelBlockWorld.PixelData pixelData) {
        Location pixelBlockLocation = this.parentBlock.getPixelBlockLocation();
        Location pixelLocation = pixelBlockLocation.add(pixelData.relativeLocation().multiply(pixelData.scale()));
        Vector3d centerOffset = new Vector3d(0.5, 0.5, 0.5).mul(pixelData.scale());
        BlockDisplay entity = (BlockDisplay) pixelBlockLocation.getWorld().spawnEntity(
            pixelLocation.add(Vector.fromJOML(centerOffset)),
            EntityType.BLOCK_DISPLAY
        );

        if(!(pixelData.directional() instanceof Bed bed && bed.getPart().equals(Bed.Part.FOOT))) entity.setBlock(pixelData.block());
        this.setEntityDirection(entity, pixelData.directional());
        this.setEntityRotation(entity, pixelData.rotatable(), pixelData.state());

        Transformation transform = entity.getTransformation();
        transform.getScale().set(pixelData.scale());
        transform.getTranslation().set(centerOffset.mul(-1));
        entity.setTransformation(transform);

        EntityTagUtil.tag(entity, pixelOfTag, this.parentBlock.getBlockUUID());
    }

    public void destroy() {
        List<BlockDisplay> entities = EntityTagUtil.findTagged(
            this.parentBlock.getPixelBlockLocation(),
            BlockDisplay.class,
            pixelOfTag,
            this.parentBlock.getBlockUUID()
        );

        ListUtil.splitListInParts(10, entities)
            .forEach(pixels -> parentBlock.getBlockTaskChain()
                .delay(1)
                .sync(() -> pixels.forEach(Entity::remove))
                .execute());
    }

    private void setEntityRotation(Entity entity, @Nullable Rotatable rotatable, BlockState blockState) {
        if(rotatable == null) return;

        Vector rotation = rotatable.getRotation().getDirection();

        float yaw = (float) Math.toDegrees(Math.atan2(rotation.getX(), -rotation.getZ()));
        if(blockState instanceof Banner) yaw += 180;
        entity.setRotation(entity.getYaw()+yaw, entity.getPitch());
    }

    private void setEntityDirection(Entity entity, @Nullable Directional direction) {
        Direction blockDirection = parentBlock.getFacingDirection();
        float angle = switch (blockDirection) {
            case Direction.north -> 180;
            case Direction.south -> 0;
            case Direction.west -> 90;
            case Direction.east -> -90;
        };
        entity.setRotation(entity.getYaw()+angle, entity.getPitch());

        if (!(direction instanceof EnderChest || direction instanceof Chest || direction instanceof Bed)) return;

        BlockFace blockFace = direction.getFacing();
        float yaw = switch (blockFace) {
            case NORTH -> 180;
            case SOUTH -> 0;
            case WEST -> 90;
            case EAST -> -90;
            default -> entity.getLocation().getYaw();
        };

        float pitch = switch (blockFace) {
            case UP -> 90;
            case DOWN -> -90;
            default -> 0;
        };
        entity.setRotation(entity.getYaw()+yaw, entity.getPitch()+pitch);
    }
}
