/*
 *  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.capability;

import net.minecraft.core.BlockPos;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.NbtUtils;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.phys.AABB;
import net.minecraft.world.phys.Vec3;
import org.jetbrains.annotations.NotNull;
import top.r3944realms.eroticdungeongame.core.event.CommonHandler;
import top.r3944realms.lib39.util.nbt.NBTReader;
import top.r3944realms.lib39.util.nbt.NBTWriter;

import java.util.function.Consumer;

public final class PlayerDungeonData extends AbstractPlayerDungeonData {
    public static final String SEAT_POS = "seat_pos";
    public static final String SEAT_ANIMATION = "seat_animation";
    public static final String CURRENT_BB = "current_bb";
    public static final String CURRENT_EYE_HEIGHT = "current_eye_height";
    public static final AABB ZERO = AABB.ofSize(Vec3.ZERO, 0, 0, 0);
    public Player player;
    public BlockPos pos;
    public ResourceLocation anim;
    public AABB currentBB;
    public float currentEyeHeight;

    public PlayerDungeonData(Player player) {
        super(CommonHandler.Game.DUNGEON_SYNC);
        this.player = player;
    }

    @Override
    public CompoundTag serializeNBT() {
        return NBTWriter.builder()
                    .compoundIf(SEAT_POS, pos != null, () -> NbtUtils.writeBlockPos(pos))
                    .stringIf(SEAT_ANIMATION, anim != null, () -> anim.toString())
                    .compoundIf(CURRENT_BB, currentBB != null, () -> writeBB(currentBB))
                    .floatValue(CURRENT_EYE_HEIGHT, currentEyeHeight, -1)
                    .build();
    }

    private void readBB(@NotNull CompoundTag aabbTag, @NotNull Consumer<AABB> consumer) {
        double minX = aabbTag.getDouble("minX");
        double minY = aabbTag.getDouble("minY");
        double minZ = aabbTag.getDouble("minZ");
        double maxX = aabbTag.getDouble("maxX");
        double maxY = aabbTag.getDouble("maxY");
        double maxZ = aabbTag.getDouble("maxZ");
        consumer.accept(new AABB(minX, minY, minZ, maxX, maxY, maxZ));
    }

    private @NotNull CompoundTag writeBB(@NotNull AABB aabb) {
        CompoundTag aabbTag = new CompoundTag();
        aabbTag.putDouble("minX", aabb.minX);
        aabbTag.putDouble("minY", aabb.minY);
        aabbTag.putDouble("minZ", aabb.minZ);
        aabbTag.putDouble("maxX", aabb.maxX);
        aabbTag.putDouble("maxY", aabb.maxY);
        aabbTag.putDouble("maxZ", aabb.maxZ);
        return aabbTag;
    }

    @Override
    public void deserializeNBT(CompoundTag compoundTag) {
        NBTReader.of(compoundTag)
                .compound(SEAT_POS, i -> setDeviceMainBlockPos(i == null ? null : NbtUtils.readBlockPos(i)), null)
                .string(SEAT_ANIMATION, i -> setAnimationResourceLocation(i == null ? null : ResourceLocation.read(i).getOrThrow(false, e->{})), null)
                .compound(CURRENT_BB, i -> {
                    if (i == null) {
                        setPlayerBoundingBox(null);
                    } else readBB(i, j-> {
                        if (!j.equals(ZERO)) {
                            setPlayerBoundingBox(j);
                        }
                    });
                }, null)
                .floatValue(CURRENT_EYE_HEIGHT, this::setEyeHeight, -1.0f);
    }


    @Override
    public int entityId() {
        return player.getId();
    }

    @Override
    public void setDeviceMainBlockPos(BlockPos pos) {
        this.pos = pos;
        markDirty();
    }

    @Override
    public BlockPos getDeviceMainBlockPos() {
        return pos;
    }

    @Override
    public void setAnimationResourceLocation(ResourceLocation location) {
        this.anim = location;
        markDirty();
    }

    @Override
    public ResourceLocation getAnimationResourceLocation() {
        return anim;
    }

    @Override
    public AABB getPlayerBoundingBox() {
        return currentBB;
    }

    @Override
    public void setPlayerBoundingBox(AABB boundingBox) {
        this.currentBB = boundingBox;
        markDirty();
    }

    @Override
    public float getEyeHeight() {
        return currentEyeHeight;
    }

    @Override
    public void setEyeHeight(float eyeHeight) {
        this.currentEyeHeight = eyeHeight;
        markDirty();
    }
}
