/*
 *  Copyright 2025-2026 R3944Realms
 *
 *    Licensed under the Apache License, Version 2.0 (the "License");
 *    you may not use this file except in compliance with the License.
 *    You may obtain a copy of the License at
 *
 *        http://www.apache.org/licenses/LICENSE-2.0
 *
 *    Unless required by applicable law or agreed to in writing, software
 *    distributed under the License is distributed on an "AS IS" BASIS,
 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *    See the License for the specific language governing permissions and
 *    limitations under the License.
 */

package top.r3944realms.eroticdungeongame.content.block.blockentity;

import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.network.protocol.Packet;
import net.minecraft.network.protocol.game.ClientGamePacketListener;
import net.minecraft.network.protocol.game.ClientboundBlockEntityDataPacket;
import net.minecraft.util.Mth;
import net.minecraft.util.RandomSource;
import net.minecraft.world.DifficultyInstance;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.EntityType;
import net.minecraft.world.entity.LightningBolt;
import net.minecraft.world.level.GameRules;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.phys.AABB;
import net.minecraft.world.phys.Vec3;
import org.jetbrains.annotations.NotNull;
import software.bernie.geckolib.animatable.GeoBlockEntity;
import software.bernie.geckolib.core.animatable.instance.AnimatableInstanceCache;
import software.bernie.geckolib.core.animation.AnimatableManager;
import software.bernie.geckolib.core.animation.AnimationController;
import software.bernie.geckolib.core.animation.RawAnimation;
import software.bernie.geckolib.util.GeckoLibUtil;
import top.r3944realms.eroticdungeongame.EroticDungeon;
import top.r3944realms.eroticdungeongame.api.event.LoveMachineTickEvent;
import top.r3944realms.eroticdungeongame.client.util.RendererUtil;
import top.r3944realms.eroticdungeongame.content.EDGDamageTypes;
import top.r3944realms.eroticdungeongame.content.block.type.LoveMachineBlock;
import top.r3944realms.eroticdungeongame.core.register.EDGBlockEntities;
import top.r3944realms.eroticdungeongame.other.geckolib.AutoPlayingSoundKeyframeHandler;
import top.r3944realms.lib39.util.nbt.NBTReader;
import top.r3944realms.lib39.util.nbt.NBTWriter;

import java.util.HashMap;
import java.util.Map;

public class LoveMachineBlockEntity extends BlockEntity implements GeoBlockEntity {
    private final AnimatableInstanceCache cache = GeckoLibUtil.createInstanceCache(this);
    public static final RawAnimation ACTIVE_ANIMATION = RawAnimation.begin().thenPlay("love_machine.active");
    public static final RawAnimation STOPPING_ANIMATION = RawAnimation.begin().thenPlay("love_machine.stopping").thenPlay("love_machine.stopped");
    public static final RawAnimation STOPPED_ANIMATION = RawAnimation.begin().thenPlay("love_machine.stopped");
    public static final Map<String, SpecialAction> ACTION = new HashMap<>();
    private volatile boolean isActive = false;
    private volatile boolean isStopping = false;
    public final RandomSource random = RandomSource.create();
    static {
        ACTION.put("lightning_rod", ((level, machineBlockEntity, entity, pos, state, ticks) -> {
            if (level.isThundering()) {
                if(level.canSeeSky(pos)) {
                    if (level.isRainingAt(pos)) {
                        DifficultyInstance difficultyinstance = level.getCurrentDifficultyAt(pos);
                        boolean flag1 = level.getGameRules().getBoolean(GameRules.RULE_DOMOBSPAWNING) && level.random.nextDouble() < (double)difficultyinstance.getEffectiveDifficulty() * 0.01D ;
                        if (flag1) {
                            LightningBolt lightningbolt = EntityType.LIGHTNING_BOLT.create(level);
                            if (lightningbolt != null) {
                                lightningbolt.moveTo(Vec3.atBottomCenterOf(pos));
                                lightningbolt.setVisualOnly(true);
                                level.addFreshEntity(lightningbolt);
                            }
                        }
                    }
                }
            }
        }));
    }
    private float offsetX = 0.5f;
    private float offsetY = 0.0f;
    private float offsetZ = 0.5f;
    private final float MIN_OFFSET = 0.0f;
    private final float MAX_OFFSET = 1.0f;
    private boolean offsetEnabled = true;

    private float speed = 0.05f;
    private boolean movingForward = true;

    public LoveMachineBlockEntity(BlockPos pos, BlockState blockState) {
        super(EDGBlockEntities.LOVE_MACHINE_BLOCK_ENTITY.get(), pos, blockState);
    }
    public int ticks = 0;
    public static void serverTick(Level pLevel, BlockPos pPos, BlockState pState, @NotNull LoveMachineBlockEntity pBlockEntity) {
        if (pBlockEntity.isActive() && pBlockEntity.isOffsetEnabled()) {
            // 获取方块朝向
            Direction facing = pState.getValue(LoveMachineBlock.FACING);

            // 计算偏移后的碰撞箱位置
            BlockPos offsetPos = calculateOffsetPosition(pPos, facing, pBlockEntity);

            // 获取偏移前方一格的位置
            BlockPos frontPos = offsetPos.relative(facing);

            // 检查前方一格是否有实体进入碰撞箱
            AABB frontBox = new AABB(frontPos);
            if (pState.getBlock() instanceof LoveMachineBlock loveMachineBlock) {
                for (Entity entity : pLevel.getEntitiesOfClass(Entity.class, frontBox)) {
                    // 检查实体是否与碰撞箱相交
                    if (entity.getBoundingBox().intersects(frontBox)) {
                        // 发布事件
                        if (!EroticDungeon.EVENT_BUS.post(new LoveMachineTickEvent(entity, pBlockEntity, pLevel.getGameTime()))) {
                            ACTION.getOrDefault(loveMachineBlock.type, SpecialAction.EMPTY).doTick(pLevel, pBlockEntity, entity, pPos, pState, pLevel.getGameTime());
                            if (pBlockEntity.speed < 0.05f) {
                                if (pLevel.getGameTime() % 100 == 0) {
                                    entity.hurt(EDGDamageTypes.causeFuckedDamage(pLevel.registryAccess()), calcAnimSpeed(pBlockEntity.speed) * 10f);
                                }
                            } else entity.hurt(EDGDamageTypes.causeFuckedDamage(pLevel.registryAccess()), calcAnimSpeed(pBlockEntity.speed));
                        }

                    }
                }
            }
        }

    }

    /**
     * 计算偏移后的位置
     */
    public static @NotNull BlockPos calculateOffsetPosition(@NotNull BlockPos originalPos, Direction facing, @NotNull LoveMachineBlockEntity blockEntity) {
        // 获取偏移向量
        Vec3 offsetVec = blockEntity.getOffsetVector();

        // 将偏移向量转换为方块坐标偏移
        int offsetX = (int) Math.round(offsetVec.x);
        int offsetY = (int) Math.round(offsetVec.y);
        int offsetZ = (int) Math.round(offsetVec.z);

        // 根据方块朝向旋转偏移
        Vec3 rotatedOffset = RendererUtil.rotateVectorByFacing(new Vec3(offsetX, offsetY, offsetZ), facing);

        // 计算偏移后的位置
        return originalPos.offset(
                (int) (facing.get3DDataValue() <= 1 ? -Math.round(rotatedOffset.x) : Math.round(rotatedOffset.x)),
                (int) Math.round(rotatedOffset.y),
                (int) (facing.get3DDataValue() <= 1 ? -Math.round(rotatedOffset.z) : Math.round(rotatedOffset.z))
        );
    }

    @Override
    public void registerControllers(AnimatableManager.@NotNull ControllerRegistrar controllerRegistrar) {
        controllerRegistrar.add(
                new AnimationController<>(this, "love_machine", 0, state -> {
                    if (isActive) {
                        state.setControllerSpeed(calcAnimSpeed(speed));
                      return state.setAndContinue(ACTIVE_ANIMATION);
                    } else {
                        if (isStopping) {
                            isStopping = false;
                            return state.setAndContinue(STOPPING_ANIMATION);
                        }
                        if (state.isCurrentAnimationStage("love_machine.stopping")) {
                            return state.setAndContinue(STOPPING_ANIMATION);
                        }
                        state.setControllerSpeed(1.0f);
                        return state.setAndContinue(STOPPED_ANIMATION);
                    }
                })
                    .setSoundKeyframeHandler(new AutoPlayingSoundKeyframeHandler<>())
        );
    }
    private static float calcAnimSpeed(float speed) {
        float minInput = 0.005f;
        float maxInput = 0.1f;

        float t = (speed - minInput) / (maxInput - minInput);
        t = Mth.clamp(t, 0f, 1f);

        // 指数映射
        return 0.1f * (float)Math.pow(100f, t);
    }

    public float getOffsetX() {
        return offsetX;
    }

    public float getOffsetY() {
        return offsetY;
    }

    public float getOffsetZ() {
        return offsetZ;
    }

    public void setOffset(float x, float y, float z) {
        offsetX = x;
        offsetY = y;
        offsetZ = z;
    }

    public float getRenderOffsetX() {
        return (offsetX - 0.5f) * 0.5f;
    }

    public float getRenderOffsetY() {
        return offsetY;
    }

    public float getRenderOffsetZ() {
        return (offsetZ - 0.5f) * 0.5f;
    }

    public Vec3 getOffsetVector() {
        return new Vec3(getRenderOffsetX(), getRenderOffsetY(), getRenderOffsetZ());
    }

    public void setOffsetEnabled(boolean enabled) {
        this.offsetEnabled = enabled;
        setChanged();
    }

    public boolean isOffsetEnabled() {
        return offsetEnabled;
    }

    public void setSpeed(float speed) {
        this.speed = Math.max(0.001f, Math.min(0.1f, speed)); // 限制速度范围
        setChanged();
    }

    public float getSpeed() {
        return speed;
    }

    public void resetOffset() {
        offsetX = 0.5f;
        offsetY = MIN_OFFSET;
        offsetZ = 0.5f;
        movingForward = true;
        setChanged();
    }

    @Override
    public AnimatableInstanceCache getAnimatableInstanceCache() {
        return cache;
    }
    public void setStopping(boolean stopping) {
        isStopping = stopping;
    }

    public void setActive(boolean active) {
        isActive = active;
    }

    public boolean isStopping() {
        return isStopping;
    }

    public boolean isActive() {
        return isActive;
    }

    @Override
    public void saveAdditional(@NotNull CompoundTag compoundTag) {
        super.saveAdditional(compoundTag);
        NBTWriter.of(compoundTag)
                .booleanValue("isEnable", isActive)
                .booleanValue("isStopping", isStopping)
                .floatValue("offsetX", offsetX)
                .floatValue("offsetY", offsetY)
                .floatValue("offsetZ", offsetZ)
                .floatValue("speed", speed)
                .booleanValue("movingForward", movingForward)
                .booleanValue("offsetEnabled", offsetEnabled);
    }

    @Override
    public void load(@NotNull CompoundTag compoundTag) {
        super.load(compoundTag);
        NBTReader.of(compoundTag)
                .booleanValue("isEnable", this::setActive)
                .booleanValue("isStopping", this::setStopping)
                .floatValue("offsetX", v -> offsetX = v)
                .floatValue("offsetY", v -> offsetY = v)
                .floatValue("offsetZ", v -> offsetZ = v)
                .floatValue("speed", this::setSpeed)
                .booleanValue("movingForward", v -> movingForward = v)
                .booleanValue("offsetEnabled", this::setOffsetEnabled);
    }

    @Override
    public AABB getRenderBoundingBox() {
        BlockPos pos = this.getBlockPos();
        return new AABB(pos.getX() - 2, pos.getY() - 2, pos.getZ() - 2, pos.getX() + 2, pos.getY() + 2, pos.getZ() + 2);
    }

    @Override
    public Packet<ClientGamePacketListener> getUpdatePacket() {
        return ClientboundBlockEntityDataPacket.create(this);
    }

    @Override
    public @NotNull CompoundTag getUpdateTag() {
        return saveWithoutMetadata();
    }
    @FunctionalInterface
    public interface SpecialAction {
        SpecialAction EMPTY = (level, machineBlockEntity, entity, pos, state, ticks) -> {};

        void doTick(Level level, LoveMachineBlockEntity machineBlockEntity, Entity entity, BlockPos pos , BlockState state, long ticks);
    }
}
