package com.r3944realms.leashedplayer.client.renderer;

import com.mojang.blaze3d.vertex.PoseStack;
import com.mojang.blaze3d.vertex.VertexConsumer;
import com.mojang.datafixers.util.Either;
import com.r3944realms.leashedplayer.LeashedPlayer;
import com.r3944realms.leashedplayer.content.entities.LeashRopeArrow;
import com.r3944realms.leashedplayer.modInterface.IEntityLeadExtension;
import com.r3944realms.leashedplayer.modInterface.IPlayerRenderStateExtension;
import com.r3944realms.leashedplayer.modInterface.PlayerLeashable;
import net.minecraft.client.Camera;
import net.minecraft.client.Minecraft;
import net.minecraft.client.multiplayer.ClientLevel;
import net.minecraft.client.player.AbstractClientPlayer;
import net.minecraft.client.renderer.LightTexture;
import net.minecraft.client.renderer.MultiBufferSource;
import net.minecraft.client.renderer.RenderType;
import net.minecraft.client.renderer.entity.state.EntityRenderState;
import net.minecraft.client.renderer.entity.state.PlayerRenderState;
import net.minecraft.core.BlockPos;
import net.minecraft.util.Mth;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.Leashable;
import net.minecraft.world.entity.decoration.LeashFenceKnotEntity;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.entity.vehicle.AbstractMinecart;
import net.minecraft.world.entity.vehicle.NewMinecartBehavior;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.LightLayer;
import net.minecraft.world.phys.AABB;
import net.minecraft.world.phys.Vec3;
import net.neoforged.api.distmarker.Dist;
import net.neoforged.api.distmarker.OnlyIn;
import org.joml.Matrix4f;

import java.util.List;
import java.util.UUID;

@OnlyIn(Dist.CLIENT)
public class LeashRendererUtil {
    public static void renderLeash(PoseStack poseStack, MultiBufferSource buffer, EntityRenderState.LeashState leashState) {
        float f = 0.025F;

        float f1 = (float)(leashState.end.x - leashState.start.x);
        float f2 = (float)(leashState.end.y - leashState.start.y + 0.2);
        float f3 = (float)(leashState.end.z - leashState.start.z);
        float f4 = Mth.invSqrt(f1 * f1 + f3 * f3) * 0.025F / 2.0F;
        float f5 = f3 * f4;
        float f6 = f1 * f4;
        poseStack.pushPose();
        poseStack.translate(leashState.offset.add(0, -0.2, -0.2));
        VertexConsumer vertexconsumer = buffer.getBuffer(RenderType.leash());
        Matrix4f matrix4f = poseStack.last().pose();

        for (int i = 0; i <= 24; i++) {
            addVertexPair(
                    vertexconsumer,
                    matrix4f,
                    f1,
                    f2,
                    f3,
                    leashState.startBlockLight,
                    leashState.endBlockLight,
                    leashState.startSkyLight,
                    leashState.endSkyLight,
                    0.025F,
                    0.025F,
                    f5,
                    f6,
                    i,
                    false
            );
        }

        for (int j = 24; j >= 0; j--) {
            addVertexPair(
                    vertexconsumer,
                    matrix4f,
                    f1,
                    f2,
                    f3,
                    leashState.startBlockLight,
                    leashState.endBlockLight,
                    leashState.startSkyLight,
                    leashState.endSkyLight,
                    0.025F,
                    0.0F,
                    f5,
                    f6,
                    j,
                    true
            );
        }

        poseStack.popPose();
    }
    /**
     * <h1>1. 角度与弧度转换</h1>
     * {@snippet lang=java :
     * double d0 = (double)(pEntity.getPreciseBodyRotation(pPartialTick) * (float)(Math.PI / 180.0)) + (Math.PI / 2);
     * }
     * <ul>
     *      <li><code>pEntity.getPreciseBodyRotation(pPartialTick)</code> 返回实体的旋转角度（通常是以度为单位）。/li>
     *      <li> <code>(Math.PI / 180.0)</code> 是将度数转换为弧度的乘数，因为大多数三角函数（如 <code>cos</code> 和 <code>sin</code>）都需要弧度值。</li>
     *      <li><code>+ (Math.PI / 2)</code> 用于将结果平移90度（四分之一圆），可能是为了校正方向或设置起始方向。 </li>
     * </ul>
     *
     * <p>
     * <h1> 2. 三角函数计算位移</h1>
     * {@snippet lang=java :
     * double d1 = Math.cos(d0) * vec31.z + Math.sin(d0) * vec31.x;
     * double d2 = Math.sin(d0) * vec31.z - Math.cos(d0) * vec31.x;
     *  }
     *  <ul>
     *      <li><code>d1</code> 和 <code>d2</code> 是利用三角函数 <code>cos</code> 和 <code>sin</code> 计算出来的位移量，用于确定实体相对于其旋转的实际位置。</li>
     *      <li><code>Math.cos(d0) * vec31.z</code> 和 <code>Math.sin(d0) * vec31.x</code> 分别计算沿 X 和 Z 轴的位移分量，这种计算通常用于旋转一个点或向量。</li>
     *      <li>两个公式结合起来用于旋转平面内的一个点 <code>(vec31.x, vec31.z)</code>，从而得到旋转后的新坐标。</li>
     *  </ul>
     * <p>
     * <h1> 3. 线性插值 (Lerp) </h1>
     * {@snippet lang=java :
     * double d3 = Mth.lerp(pPartialTick, pEntity.xo, pEntity.getX()) + d1;
     * double d4 = Mth.lerp(pPartialTick, pEntity.yo, pEntity.getY()) + vec31.y;
     * double d5 = Mth.lerp(pPartialTick, pEntity.zo, pEntity.getZ()) + d2;
     * }
     * <ul>
     *     <li><code>Mth.lerp</code> 是线性插值函数，通常用于在两个值之间平滑过渡。</li>
     *     <li><code>pEntity.xo</code>, <code>pEntity.yo</code>, <code>pEntity.zo</code> 是实体在上一个刻度（tick）中的位置，而 <code>pEntity.getX()</code>, <code>pEntity.getY()</code>, <code>pEntity.getZ()</code> 是当前刻度的位置。</li>
     *     <li><code>pPartialTick</code> 介于 <code>0</code> 和 <code>1</code> 之间，用来平滑过渡，使得动画更加流畅。</li>
     * </ul>
     * <p>
     * <h1> 4. 向量差值 </h1>
     * {@snippet lang=java :
     * float f = (float)(vec3.x - d3);
     * float f1 = (float)(vec3.y - d4);
     * float f2 = (float)(vec3.z - d5);
     * }
     * <ul>
     *     <li>计算两个点（<code>vec3</code> 和 <code>(d3, d4, d5)</code>）之间的差值，得到的 <code>f</code>，<code>f1</code>，<code>f2</code> 是向量差，用于后续的渲染计算。</li>
     * </ul>
     * <p>
     * <h1> 5. 逆平方根与比例因子 </h1>
     * {@snippet lang=java :
     * float f4 = Mth.invSqrt(f * f + f2 * f2) * 0.025F / 2.0F;
     * }
     * <ul>
     *     <li><code>Mth.invSqrt</code> 计算的是逆平方根（通常用于归一化向量或调整比例）。</li>
     *     <li><code>f * f + f2 * f2</code> 是计算向量 <code>(f, f2)</code> 的平方和，用于得到其长度的平方。</li>
     *     <li>乘以 <code>0.025F / 2.0F</code> 用于缩放结果，使得线条在渲染时具有合适的比例。</li>
     * </ul>
     * <p>
     * <h1> 6. 循环绘制 </h1>
     * {@snippet lang=java :
     * for (int i1 = 0; i1 <= 24; i1++) {
     *     addVertexPair(vertexconsumer, matrix4f, f, f1, f2, i, j, k, l, 0.025F, 0.025F, f5, f6, i1, false);
     * }
     * }
     * <ul>
     *  <li>循环从 <code>0</code> 到 <code>24</code>，用于创建24个顶点对，形成一个链状结构（或绳索）的外观。</li>
     *  <li>每个循环迭代都会更新顶点的位置、颜色、光照等属性，使得链状结构被绘制出来。</li>
     * </ul>
     * <p>
     * <h1> 总结 </h1>
     * 这些数学运算主要用于计算实体在三维空间中的位置和方向，以确保在渲染链状结构（如拴住的绳索）时，链条能够跟随实体的移动和旋转并正确显示。在图形编程中，这些计算非常常见，尤其是在处理旋转、插值和光照效果时。
     */

    public static void renderLeash(
            PoseStack poseStack,
            MultiBufferSource bufferSource,
            PlayerRenderState playerRenderState
    ) {
        float f = 0.025F;
        // 获得绳索持有者的位置
        PlayerLeashState leashState = ((IPlayerRenderStateExtension)playerRenderState).getPlayerLeashState();
        if (leashState == null)
            return;
        Vec3 Holder = leashState.pos;//TODO：问题修复
        Vec3 o = leashState.o;
        // 计算实体的朝向角度（弧度）
        float partialTick = playerRenderState.partialTick;
        double entityRotationAngleRadians = (double)(leashState.getPreciseBodyRotation(partialTick) * (float) (Math.PI / 180.0)) + (Math.PI / 2);
        // 计算实体的绳索偏移，此处add偏移让渲染拴绳显示在玩家头部下（大约在脖子处
//        Logger.logger.info("eyeHeight:{}",playerRenderState.eyeHeight);
        Vec3 cameraEntityLeashOffset = new Vec3(0.0, leashState.eyeHeight , playerRenderState.boundingBoxWidth * 0.4F).add(0, -0.2, -0.2);
        double leashOffsetX = Math.cos(entityRotationAngleRadians) * cameraEntityLeashOffset.z + Math.sin(entityRotationAngleRadians) * cameraEntityLeashOffset.x;
        double leashOffsetZ = Math.sin(entityRotationAngleRadians) * cameraEntityLeashOffset.z - Math.cos(entityRotationAngleRadians) * cameraEntityLeashOffset.x;
        // 计算实体当前的实际位置
        double entityPosX = Mth.lerp(partialTick, o.x, playerRenderState.x) + leashOffsetX;
        double entityPosY = Mth.lerp(partialTick, o.y, playerRenderState.y) + cameraEntityLeashOffset.y;
        double entityPosZ = Mth.lerp(partialTick, o.z, playerRenderState.z) + leashOffsetZ;

        // 计算绳索的相对位置差
        float deltaX = (float)(Holder.x - entityPosX);
        float deltaY = (float)(Holder.y - entityPosY);
        float deltaZ = (float)(Holder.z - entityPosZ);
        // 计算比例因子，用于调节绳索的粗细
        float leashLengthRatio = Mth.invSqrt(deltaX * deltaX + deltaZ * deltaZ) * 0.025F / 2.0F;
        // 计算比例因子，用于调节绳索的粗细
        float leashXZScaleX = deltaZ * leashLengthRatio;
        float leashXZScaleZ = deltaX * leashLengthRatio;
        poseStack.pushPose();
        poseStack.translate(leashOffsetX, cameraEntityLeashOffset.y,leashOffsetZ);
        VertexConsumer vertexconsumer = bufferSource.getBuffer(RenderType.leash());
        Matrix4f matrix4f = poseStack.last().pose();

        for (int i = 0; i <= 24; i++) {
            addVertexPair(
                    vertexconsumer,
                    matrix4f,
                    deltaX,
                    deltaY,
                    deltaZ,
                    leashState.startBlockLight,
                    leashState.endBlockLight,
                    leashState.startSkyLight,
                    leashState.endSkyLight,
                    0.025F,
                    0.025F,
                    leashXZScaleX,
                    leashXZScaleZ,
                    i,
                    false
            );
        }

        for (int j = 24; j >= 0; j--) {
            addVertexPair(
                    vertexconsumer,
                    matrix4f,
                    deltaX,
                    deltaY,
                    deltaZ,
                    leashState.startBlockLight,
                    leashState.endBlockLight,
                    leashState.startSkyLight,
                    leashState.endSkyLight,
                    0.025F,
                    0.0F,
                    leashXZScaleX,
                    leashXZScaleZ,
                    j,
                    true
            );
        }

        poseStack.popPose();
    }
    
    protected static void addVertexPair(
            VertexConsumer buffer,
            Matrix4f pose,
            float startX,
            float startY,
            float startZ,
            int entityBlockLight,
            int holderBlockLight,
            int entitySkyLight,
            int holderSkyLight,
            float yOffset,
            float dy,
            float dx,
            float dz,
            int index,
            boolean reverse
    ) {
        float f = (float)index / 24.0F;
        int i = (int)Mth.lerp(f, (float)entityBlockLight, (float)holderBlockLight);
        int j = (int)Mth.lerp(f, (float)entitySkyLight, (float)holderSkyLight);
        int k = LightTexture.pack(i, j);
        float f1 = index % 2 == (reverse ? 1 : 0) ? 0.7F : 1.0F;
        float f2 = 0.5F * f1;
        float f3 = 0.4F * f1;
        float f4 = 0.3F * f1;
        float f5 = startX * f;
        float f6 = startY > 0.0F ? startY * f * f : startY - startY * (1.0F - f) * (1.0F - f);
        float f7 = startZ * f;
        buffer.addVertex(pose, f5 - dx, f6 + dy, f7 + dz).setColor(f2, f3, f4, 1.0F).setLight(k);
        buffer.addVertex(pose, f5 + dx, f6 + yOffset - dy, f7 - dz).setColor(f2, f3, f4, 1.0F).setLight(k);
    }
    public static  <E extends Entity> void renderLeashForCamera(
            Camera camera,
            float partialTick,
            PoseStack poseStack,
            MultiBufferSource bufferSource,
            E leashHolder,
            Vec3 holderOffset
    ) {

        poseStack.pushPose();
//        Logger.logger.info("eyeHeight{}", camera.getEntity().getEyeHeight());
        // 获得绳索持有者的位置
        Vec3 leashHolderPosition = leashHolder.getRopeHoldPosition(partialTick).add(holderOffset);

        // 获取当前观察的实体
        Entity cameraEntity = camera.getEntity();

        // 计算实体的朝向角度（弧度）
        double entityRotationAngleRadians = (double)(cameraEntity.getPreciseBodyRotation(partialTick) * (float) (Math.PI / 180.0)) + (Math.PI / 2);

        // 计算实体的绳索偏移，此处add偏移让渲染拴绳显示在玩家头部下（大约在脖子处
        Vec3 cameraEntityLeashOffset = ((Leashable)cameraEntity).getLeashOffset(partialTick).add(0, -0.2, -0.5);
        double leashOffsetX = Math.cos(entityRotationAngleRadians) * cameraEntityLeashOffset.z + Math.sin(entityRotationAngleRadians) * cameraEntityLeashOffset.x;
        double leashOffsetZ = Math.sin(entityRotationAngleRadians) * cameraEntityLeashOffset.z - Math.cos(entityRotationAngleRadians) * cameraEntityLeashOffset.x;

        // 计算实体当前的实际位置
        double entityPosX = Mth.lerp(partialTick, cameraEntity.xo, cameraEntity.getX()) + leashOffsetX;
        double entityPosY = Mth.lerp(partialTick, cameraEntity.yo, cameraEntity.getY()) + cameraEntityLeashOffset.y;
        double entityPosZ = Mth.lerp(partialTick, cameraEntity.zo, cameraEntity.getZ()) + leashOffsetZ;

        // 在当前变换矩阵上应用偏移
        poseStack.translate(leashOffsetX, cameraEntityLeashOffset.y , leashOffsetZ);

        // 计算绳索的相对位置差
        float deltaX = (float)(leashHolderPosition.x - entityPosX);
        float deltaY = (float)(leashHolderPosition.y - entityPosY);
        float deltaZ = (float)(leashHolderPosition.z - entityPosZ);

        // 获取顶点消费者，用于绘制绳索
        VertexConsumer vertexConsumer = bufferSource.getBuffer(RenderType.leash());
        Matrix4f matrix = poseStack.last().pose();

        // 计算比例因子，用于调节绳索的粗细
        float leashLengthRatio = Mth.invSqrt(deltaX * deltaX + deltaZ * deltaZ) * 0.025F / 2.0F;
        float leashXZScaleX = deltaZ * leashLengthRatio;
        float leashXZScaleZ = deltaX * leashLengthRatio;

        // 获取光照信息
        BlockPos cameraEntityBlockPos = BlockPos.containing(cameraEntity.getEyePosition(partialTick));
        BlockPos leashHolderBlockPos = BlockPos.containing(leashHolder.getEyePosition(partialTick));
        int cameraEntityBlockLightLevel = getBlockLightLevel(cameraEntity, cameraEntityBlockPos);
        int leashHolderBlockLightLevel = getBlockLightLevel(leashHolder, leashHolderBlockPos); //getBlockLightLevel(leashHolder, leashHolderBlockPos);
        int cameraEntitySkyLightLevel = cameraEntity.level().getBrightness(LightLayer.SKY, cameraEntityBlockPos);
        int leashHolderSkyLightLevel = cameraEntity.level().getBrightness(LightLayer.SKY, leashHolderBlockPos);

        // 绘制绳索的上半部分
        for (int segment = 0; segment <= 24; segment++) {
            addVertexPair(vertexConsumer, matrix, deltaX, deltaY, deltaZ, cameraEntityBlockLightLevel, leashHolderBlockLightLevel, cameraEntitySkyLightLevel, leashHolderSkyLightLevel, 0.025F, 0.025F, leashXZScaleX, leashXZScaleZ, segment, false);
        }

        // 绘制绳索的下半部分
        for (int segment = 24; segment >= 0; segment--) {
            addVertexPair(vertexConsumer, matrix, deltaX, deltaY, deltaZ, cameraEntityBlockLightLevel, leashHolderBlockLightLevel, cameraEntitySkyLightLevel, leashHolderSkyLightLevel, 0.025F, 0.0F, leashXZScaleX, leashXZScaleZ, segment, true);
        }

        poseStack.popPose();
    }
    public static void levelRenderLeash(ClientLevel level, Camera pCamera, PoseStack poseStack, MultiBufferSource.BufferSource multibuffersource$buffersource) {
        for(Entity entity : level.entitiesForRendering()) {
            //对于玩家实体拴绳渲染（从第一人称视角）
            if (entity instanceof AbstractClientPlayer abstractClientPlayer) {
                if(!(pCamera.getEntity() instanceof AbstractClientPlayer)) continue;
                Minecraft mc = Minecraft.getInstance();
                if (mc.options.getCameraType().isFirstPerson()) {
                    Leashable.LeashData leashDataFromEntityData = ((PlayerLeashable) abstractClientPlayer).getLeashData();
                    if(leashDataFromEntityData == null) continue;
                    Either<UUID, BlockPos> delayedLeashInfo = leashDataFromEntityData.delayedLeashInfo;
                    if(delayedLeashInfo != null) {
                        float partialTickTime = pCamera.getPartialTickTime();
                        Vec3 position = pCamera.getPosition();
                        double dX = Mth.lerp(partialTickTime, abstractClientPlayer.xOld, abstractClientPlayer.getX()) - position.x;
                        double dY = Mth.lerp(partialTickTime, abstractClientPlayer.yOld, abstractClientPlayer.getY()) - position.y;
                        double dZ = Mth.lerp(partialTickTime, abstractClientPlayer.zOld, abstractClientPlayer.getZ()) - position.z;
                        Vec3 vec3 = getRenderOffset(entity, partialTickTime);
                        double dX_ = dX + vec3.x();
                        double dY_ = dY + vec3.y();
                        double dZ_ = dZ + vec3.z();
                        poseStack.pushPose();
                        poseStack.translate(dX_, dY_, dZ_);
                        if (delayedLeashInfo.right().isPresent() && delayedLeashInfo.left().isEmpty()) {
                            renderLeashForCamera(pCamera, partialTickTime, poseStack, multibuffersource$buffersource, LeashFenceKnotEntity.getOrCreateKnot(level, delayedLeashInfo.right().get()), Vec3.ZERO);
                        } else if (delayedLeashInfo.right().isEmpty() && delayedLeashInfo.left().isPresent()) {
                            Player playerByUUID = level.getPlayerByUUID(delayedLeashInfo.left().get());
                            if (playerByUUID != null) {
                                renderLeashForCamera(pCamera, partialTickTime, poseStack, multibuffersource$buffersource, playerByUUID, Vec3.ZERO);
                            } else {
                                double MaxLeashLength = ((IEntityLeadExtension) abstractClientPlayer).getLeashLength() * LeashedPlayer.M1() * LeashedPlayer.M2();
                                List<Entity> entities = level.getEntities(
                                        null,
                                        new AABB(
                                                abstractClientPlayer.getX() - MaxLeashLength,
                                                abstractClientPlayer.getY() - MaxLeashLength,
                                                abstractClientPlayer.getZ() - MaxLeashLength,
                                                abstractClientPlayer.getX() + MaxLeashLength,
                                                abstractClientPlayer.getY() + MaxLeashLength,
                                                abstractClientPlayer.getZ() + MaxLeashLength
                                        )
                                );
                                Entity holder = null;
                                for (Entity entity_ : entities) {
                                    if(entity_.getUUID().equals(delayedLeashInfo.left().get())) {
                                        holder = entity_;
                                        break;
                                    }
                                }
                                if (holder != null) {
                                    if(holder instanceof LeashRopeArrow) {
                                        renderLeashForCamera(pCamera, partialTickTime, poseStack, multibuffersource$buffersource, holder, new Vec3(0.,-0.09, 0));//TODO: 待擴展Vec3吗？
                                    }
                                    else renderLeashForCamera(pCamera, partialTickTime, poseStack, multibuffersource$buffersource, holder, Vec3.ZERO);
                                }
                            }
                            break;
                        }

                    }
                }
            }
        }
    }
    public static Vec3 getRenderOffset(Entity entity, float partialTickTime) {
       Vec3 ret = Vec3.ZERO;
        if(entity.isPassenger()
                && entity.getVehicle() instanceof AbstractMinecart abstractminecart
                && abstractminecart.getBehavior() instanceof NewMinecartBehavior newminecartbehavior
                && newminecartbehavior.cartHasPosRotLerp()
        ) {
            double d2 = Mth.lerp(partialTickTime, abstractminecart.xOld, abstractminecart.getX());
            double d0 = Mth.lerp(partialTickTime, abstractminecart.yOld, abstractminecart.getY());
            double d1 = Mth.lerp(partialTickTime, abstractminecart.zOld, abstractminecart.getZ());
            ret = newminecartbehavior.getCartLerpPosition(partialTickTime).subtract(new Vec3(d2, d0, d1));
        }
        return ret;
    }

    public static void createPlayerLeashState(AbstractClientPlayer abstractClientPlayer, IPlayerRenderStateExtension playerRenderState, float partialTick) {
        Leashable.LeashData leashDataFromEntityData = ((PlayerLeashable) abstractClientPlayer).getLeashData();
        Leashable.LeashData leashData = ((PlayerLeashable) abstractClientPlayer).getLeashData();
        PlayerRenderState playerRs = (PlayerRenderState)playerRenderState;
        if (leashDataFromEntityData != null) {
            Entity leashHolder = leashDataFromEntityData.leashHolder;
            Either<UUID, BlockPos> delayedLeashInfo = leashDataFromEntityData.delayedLeashInfo;
            PlayerLeashState playerLeashState = new PlayerLeashState();
            if (delayedLeashInfo != null) {
                createPlayerLeashState_(abstractClientPlayer, playerRenderState, leashDataFromEntityData, delayedLeashInfo, playerLeashState, partialTick);
                BlockPos blockpos1 = BlockPos.containing(abstractClientPlayer.getEyePosition(partialTick));
                playerLeashState.startBlockLight = getBlockLightLevel(abstractClientPlayer, blockpos1);
                playerLeashState.startSkyLight =  abstractClientPlayer.level().getBrightness(LightLayer.SKY, blockpos1);
            } else if(leashHolder != null) {
                createPlayerLeashState__(abstractClientPlayer, playerRenderState, leashHolder, playerLeashState, partialTick);
            } else {
                playerRenderState.setPlayerLeashState(null);
            }
        } else if(leashData != null) {
            playerRs.leashStates = null;
            Entity leashHolder = ((PlayerLeashable) abstractClientPlayer).getLeashHolder();
            PlayerLeashState playerLeashState = playerRenderState.getPlayerLeashState();
            if (leashHolder != null & playerLeashState != null) {
                float f = leashHolder.getPreciseBodyRotation(partialTick) * (float) (Math.PI / 180.0);
                Vec3 vec3 = ((Leashable)abstractClientPlayer).getLeashOffset(partialTick).yRot(-f);
                BlockPos blockpos1 = BlockPos.containing(abstractClientPlayer.getEyePosition(partialTick));
                BlockPos blockpos = BlockPos.containing(leashHolder.getEyePosition(partialTick));
                playerLeashState.vanilaLeashState = new EntityRenderState.LeashState();

                EntityRenderState.LeashState entityrenderstate$leashstate = playerLeashState.vanilaLeashState;
                entityrenderstate$leashstate.offset = vec3;
                entityrenderstate$leashstate.start = abstractClientPlayer.getPosition(partialTick).add(vec3);
                entityrenderstate$leashstate.end = leashHolder.getRopeHoldPosition(partialTick);
                entityrenderstate$leashstate.startBlockLight = getBlockLightLevel(abstractClientPlayer, blockpos1);
                entityrenderstate$leashstate.endBlockLight = getBlockLightLevel(leashHolder, blockpos);
                entityrenderstate$leashstate.startSkyLight = abstractClientPlayer.level().getBrightness(LightLayer.SKY, blockpos1);
                entityrenderstate$leashstate.endSkyLight = abstractClientPlayer.level().getBrightness(LightLayer.SKY, blockpos);
            } else {
                playerRenderState.setPlayerLeashState(null);
            }
        } else {
            playerRenderState.setPlayerLeashState(null);
        }
    }
    private static <T extends Entity> int getBlockLightLevel(T entity, BlockPos pos) {
        return entity.isOnFire() ? 15 : entity.level().getBrightness(LightLayer.BLOCK, pos);
    }
    private static int getBlockLightLevel(Level level, BlockPos pos) {
        return level.getBrightness(LightLayer.BLOCK, pos);
    }


    private static void createPlayerLeashState__(AbstractClientPlayer abstractClientPlayer, IPlayerRenderStateExtension playerRenderState, Entity leashHolder, PlayerLeashState playerLeashState, float partialTick) {
        playerLeashState.pos = leashHolder.position().add(0.0, leashHolder.getEyeHeight() * 0.6, -0.2);
        playerLeashState.o = abstractClientPlayer.getPosition(partialTick);
        playerLeashState.yRotO = abstractClientPlayer.yRotO;
        playerLeashState.yRot = abstractClientPlayer.getYRot();
        playerLeashState.eyeHeight = abstractClientPlayer.getEyeHeight();
        playerRenderState.setPlayerLeashState(playerLeashState);
    }

    private static void createPlayerLeashState_(AbstractClientPlayer abstractClientPlayer, IPlayerRenderStateExtension playerRenderState, Leashable.LeashData leashDataFromEntityData, Either<UUID, BlockPos> delayedLeashInfo, PlayerLeashState playerLeashState, float partialTick) {
        Minecraft mc = Minecraft.getInstance();
        ClientLevel level = mc.level;
        if (delayedLeashInfo.right().isPresent() && delayedLeashInfo.left().isEmpty()) {
            assert level != null;
            playerLeashState.pos = delayedLeashInfo.right().get().getCenter();
            playerLeashState.endBlockLight = getBlockLightLevel(level, delayedLeashInfo.right().get());
            playerLeashState.endSkyLight = level.getBrightness(LightLayer.SKY, delayedLeashInfo.right().get());
        } else if (delayedLeashInfo.right().isEmpty() && delayedLeashInfo.left().isPresent()) {
            assert level != null;
            Player playerByUUID = level.getPlayerByUUID(delayedLeashInfo.left().get());
            if (playerByUUID != null) {
                playerLeashState.pos = playerByUUID.position().add(0.0, playerByUUID.getEyeHeight() * 0.6, 0);
            } else {
                double breakDistanceTime = (leashDataFromEntityData.leashHolder instanceof LeashRopeArrow) ? LeashedPlayer.M1() * LeashedPlayer.M2() : LeashedPlayer.M1();
                double MaxLeashLength = ((IEntityLeadExtension) abstractClientPlayer).getLeashLength() * breakDistanceTime;
                List<Entity> entities = level.getEntities(
                        null,
                        new AABB(
                                abstractClientPlayer.getX() - MaxLeashLength * LeashedPlayer.M2(),
                                abstractClientPlayer.getY() - MaxLeashLength * LeashedPlayer.M2(),
                                abstractClientPlayer.getZ() - MaxLeashLength * LeashedPlayer.M2(),
                                abstractClientPlayer.getX() + MaxLeashLength * LeashedPlayer.M2(),
                                abstractClientPlayer.getY() + MaxLeashLength * LeashedPlayer.M2(),
                                abstractClientPlayer.getZ() + MaxLeashLength * LeashedPlayer.M2()
                        )
                );
                Entity holder = null;
                for (Entity entity_ : entities) {
                    if (entity_.getUUID().equals(delayedLeashInfo.left().get())) {
                        holder = entity_;
                        break;
                    }
                }
                if (holder != null) {
                    playerLeashState.pos = holder.position().add(0.0, holder.getEyeHeight() * 0.6, 0);//TODO: 待擴展Vec3
                    playerLeashState.endBlockLight = getBlockLightLevel(level, holder.getOnPos());
                    playerLeashState.endSkyLight = level.getBrightness(LightLayer.SKY, holder.getOnPos());
                } else {
                    playerLeashState.pos = abstractClientPlayer.position();
                }

            }
        }

        playerLeashState.o = abstractClientPlayer.getPosition(partialTick);
        playerLeashState.yRotO = abstractClientPlayer.yRotO;
        playerLeashState.yRot = abstractClientPlayer.getYRot();
        playerLeashState.eyeHeight = abstractClientPlayer.getEyeHeight();

        playerRenderState.setPlayerLeashState(playerLeashState);
    }


}
