package com.tiedup.remake.client.renderer;

import com.mojang.blaze3d.vertex.DefaultVertexFormat;
import com.mojang.blaze3d.vertex.PoseStack;
import com.mojang.blaze3d.vertex.VertexConsumer;
import com.mojang.blaze3d.vertex.VertexFormat;
import com.tiedup.remake.blocks.entity.CellCoreBlockEntity;
import net.minecraft.client.renderer.GameRenderer;
import net.minecraft.client.renderer.MultiBufferSource;
import net.minecraft.client.renderer.RenderStateShard;
import net.minecraft.client.renderer.RenderType;
import net.minecraft.client.renderer.blockentity.BlockEntityRenderer;
import net.minecraft.client.renderer.blockentity.BlockEntityRendererProvider;
import net.minecraft.core.Direction;
import net.minecraft.world.level.block.SlabBlock;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.block.state.properties.SlabType;
import org.joml.Matrix4f;

/**
 * Block entity renderer for CellCore.
 *
 * Renders a small pulsing cyan diamond indicator on the interior face
 * of the Cell Core block. This helps players identify which side of the
 * block faces into the cell, and confirms the block is a Cell Core.
 *
 * Extends RenderStateShard to access protected render state fields
 * (standard Forge pattern for custom RenderTypes).
 */
public class CellCoreRenderer
    extends RenderStateShard
    implements BlockEntityRenderer<CellCoreBlockEntity>
{

    private static final float DIAMOND_SIZE = 0.15f;
    private static final float FACE_OFFSET = 0.001f;

    // Cyan indicator color (#44FFFF)
    private static final float COLOR_R = 0.267f;
    private static final float COLOR_G = 1.0f;
    private static final float COLOR_B = 1.0f;

    /**
     * Custom RenderType: POSITION_COLOR quads with translucent blending, no texture.
     * Avoids block atlas UV issues that made the diamond invisible.
     */
    private static final RenderType INDICATOR = RenderType.create(
        "tiedup_indicator",
        DefaultVertexFormat.POSITION_COLOR,
        VertexFormat.Mode.QUADS,
        256,
        false,
        true,
        RenderType.CompositeState.builder()
            .setShaderState(
                new ShaderStateShard(GameRenderer::getPositionColorShader)
            )
            .setTransparencyState(TRANSLUCENT_TRANSPARENCY)
            .setCullState(NO_CULL)
            .setDepthTestState(LEQUAL_DEPTH_TEST)
            .setWriteMaskState(COLOR_DEPTH_WRITE)
            .createCompositeState(false)
    );

    public CellCoreRenderer(BlockEntityRendererProvider.Context context) {
        super("tiedup_cell_core_renderer", () -> {}, () -> {});
    }

    @Override
    public void render(
        CellCoreBlockEntity blockEntity,
        float partialTick,
        PoseStack poseStack,
        MultiBufferSource bufferSource,
        int packedLight,
        int packedOverlay
    ) {
        Direction interiorFace = blockEntity.getInteriorFace();
        if (interiorFace == null) return;
        if (blockEntity.getLevel() == null) return;

        // Calculate pulsing alpha synced to game time (pauses when game pauses)
        float time = blockEntity.getLevel().getGameTime() + partialTick;
        float alpha = 0.4f + 0.2f * (float) Math.sin(time * 0.15);

        // Compute vertical center: adapt to slab shape if disguised as a slab
        float centerY = 0.5f;
        BlockState disguise = blockEntity.getDisguiseState();
        if (disguise == null && blockEntity.getLevel() != null) {
            // Auto-detect: check resolved model data (same logic as CellCoreBlockEntity)
            disguise = blockEntity
                .getModelData()
                .get(
                    com.tiedup.remake.client.model.CellCoreBakedModel.DISGUISE_PROPERTY
                );
        }
        if (disguise != null && disguise.getBlock() instanceof SlabBlock) {
            SlabType slabType = disguise.getValue(SlabBlock.TYPE);
            if (slabType == SlabType.BOTTOM) {
                centerY = 0.25f; // lower half
            } else if (slabType == SlabType.TOP) {
                centerY = 0.75f; // upper half
            }
            // DOUBLE = 0.5f (full block)
        }

        poseStack.pushPose();

        VertexConsumer consumer = bufferSource.getBuffer(INDICATOR);
        Matrix4f pose = poseStack.last().pose();

        renderDiamond(consumer, pose, interiorFace, alpha, centerY);

        poseStack.popPose();
    }

    private void renderDiamond(
        VertexConsumer consumer,
        Matrix4f pose,
        Direction face,
        float alpha,
        float centerY
    ) {
        float[][] verts = getDiamondVertices(face, 0.5f, centerY, 0.5f);

        for (float[] v : verts) {
            consumer
                .vertex(pose, v[0], v[1], v[2])
                .color(COLOR_R, COLOR_G, COLOR_B, alpha)
                .endVertex();
        }
    }

    /**
     * Get the 4 vertices of a diamond shape on the given face.
     * The diamond is centered on the face and offset slightly outward to avoid z-fighting.
     */
    private float[][] getDiamondVertices(
        Direction face,
        float cx,
        float cy,
        float cz
    ) {
        float s = DIAMOND_SIZE;
        float o = FACE_OFFSET;

        return switch (face) {
            case NORTH -> new float[][] {
                { cx, cy + s, 0.0f - o }, // top
                { cx + s, cy, 0.0f - o }, // right
                { cx, cy - s, 0.0f - o }, // bottom
                { cx - s, cy, 0.0f - o }, // left
            };
            case SOUTH -> new float[][] {
                { cx, cy + s, 1.0f + o },
                { cx - s, cy, 1.0f + o },
                { cx, cy - s, 1.0f + o },
                { cx + s, cy, 1.0f + o },
            };
            case WEST -> new float[][] {
                { 0.0f - o, cy + s, cz },
                { 0.0f - o, cy, cz - s },
                { 0.0f - o, cy - s, cz },
                { 0.0f - o, cy, cz + s },
            };
            case EAST -> new float[][] {
                { 1.0f + o, cy + s, cz },
                { 1.0f + o, cy, cz + s },
                { 1.0f + o, cy - s, cz },
                { 1.0f + o, cy, cz - s },
            };
            case DOWN -> {
                // Bottom face: y=0.0 for full blocks & bottom slabs, y=0.5 for top slabs only
                float downY = (cy >= 0.75f) ? 0.5f - o : 0.0f - o;
                yield new float[][] {
                    { cx, downY, cz + s },
                    { cx + s, downY, cz },
                    { cx, downY, cz - s },
                    { cx - s, downY, cz },
                };
            }
            case UP -> {
                // Top face: y=1.0 for full blocks & top slabs, y=0.5 for bottom slabs only
                float upY = (cy <= 0.25f) ? 0.5f + o : 1.0f + o;
                yield new float[][] {
                    { cx, upY, cz - s },
                    { cx + s, upY, cz },
                    { cx, upY, cz + s },
                    { cx - s, upY, cz },
                };
            }
        };
    }
}
