package com.tiedup.remake.client.renderer;

import com.mojang.blaze3d.systems.RenderSystem;
import com.mojang.blaze3d.vertex.*;
import com.tiedup.remake.cells.CellDataV2;
import com.tiedup.remake.cells.MarkerType;
import java.util.Collection;
import java.util.Set;
import net.minecraft.client.Camera;
import net.minecraft.client.renderer.GameRenderer;
import net.minecraft.core.BlockPos;
import net.minecraft.world.phys.Vec3;
import org.joml.Matrix4f;

/**
 * Utility class for rendering cell outlines in the world.
 *
 * Simplified renderer - only filled blocks, no wireframe outlines.
 * Each marker type gets a semi-transparent colored block overlay.
 * Spawn point has a pulsating effect.
 *
 * Features:
 * - Depth test enabled (blocks hidden behind world geometry)
 * - Simple filled block rendering per marker type
 * - Pulsating spawn point indicator
 */
public class CellOutlineRenderer {

    /** Enable depth test (true = blocks hidden behind world blocks) */
    public static boolean DEPTH_TEST_ENABLED = true;

    // Simple colors per type (RGBA)
    private static final float[] COLOR_WALL = { 0.3f, 0.5f, 1.0f, 1.0f }; // Blue
    private static final float[] COLOR_ANCHOR = { 1.0f, 0.2f, 0.2f, 1.0f }; // Red
    private static final float[] COLOR_BED = { 0.9f, 0.4f, 0.9f, 1.0f }; // Violet
    private static final float[] COLOR_DOOR = { 0.2f, 0.9f, 0.9f, 1.0f }; // Cyan
    private static final float[] COLOR_ENTRANCE = { 0.2f, 1.0f, 0.4f, 1.0f }; // Green
    private static final float[] COLOR_PATROL = { 1.0f, 1.0f, 0.2f, 1.0f }; // Yellow
    private static final float[] COLOR_LOOT = { 1.0f, 0.7f, 0.0f, 1.0f }; // Gold
    private static final float[] COLOR_SPAWNER = { 0.8f, 0.1f, 0.1f, 1.0f }; // Dark red
    private static final float[] COLOR_TRADER_SPAWN = {
        1.0f,
        0.84f,
        0.0f,
        1.0f,
    }; // Gold
    private static final float[] COLOR_MAID_SPAWN = {
        1.0f,
        0.41f,
        0.71f,
        1.0f,
    }; // Hot pink
    private static final float[] COLOR_MERCHANT_SPAWN = {
        0.2f,
        0.9f,
        0.9f,
        1.0f,
    }; // Cyan
    private static final float[] COLOR_DELIVERY = { 1.0f, 0.8f, 0.2f, 1.0f }; // Orange/Yellow
    private static final float[] COLOR_SPAWN = { 1.0f, 0.0f, 1.0f, 1.0f }; // Magenta
    private static final float[] COLOR_WAYPOINT = { 0.0f, 1.0f, 0.5f, 1.0f }; // Bright green

    /**
     * Render filled blocks for all positions in a cell.
     * Renders V2 cell data: walls, anchors, beds, doors, and spawn/core positions.
     *
     * @param poseStack The pose stack from the render event
     * @param cell      The cell data to render
     * @param camera    The camera for view offset calculation
     */
    public static void renderCellOutlines(
        PoseStack poseStack,
        CellDataV2 cell,
        Camera camera
    ) {
        if (cell == null) return;

        Vec3 cameraPos = camera.getPosition();

        // Setup rendering state
        RenderSystem.enableBlend();
        RenderSystem.defaultBlendFunc();
        RenderSystem.enableDepthTest();
        RenderSystem.depthMask(false);
        RenderSystem.setShader(GameRenderer::getPositionColorShader);

        // 1. Render spawn point or core pos (pulsating magenta)
        BlockPos spawnPoint =
            cell.getSpawnPoint() != null
                ? cell.getSpawnPoint()
                : cell.getCorePos();
        float pulse =
            0.4f + 0.2f * (float) Math.sin(System.currentTimeMillis() / 300.0);
        float[] spawnColor = {
            COLOR_SPAWN[0],
            COLOR_SPAWN[1],
            COLOR_SPAWN[2],
            pulse,
        };
        renderFilledBlock(poseStack, spawnPoint, cameraPos, spawnColor);

        // 2. Render wall blocks
        renderPositionCollection(
            poseStack,
            cell.getWallBlocks(),
            cameraPos,
            COLOR_WALL
        );

        // 3. Render anchors
        renderPositionCollection(
            poseStack,
            cell.getAnchors(),
            cameraPos,
            COLOR_ANCHOR
        );

        // 4. Render beds
        renderPositionCollection(
            poseStack,
            cell.getBeds(),
            cameraPos,
            COLOR_BED
        );

        // 5. Render doors
        renderPositionCollection(
            poseStack,
            cell.getDoors(),
            cameraPos,
            COLOR_DOOR
        );

        // 6. Render delivery point if present
        BlockPos deliveryPoint = cell.getDeliveryPoint();
        if (deliveryPoint != null) {
            float[] deliveryFillColor = {
                COLOR_DELIVERY[0],
                COLOR_DELIVERY[1],
                COLOR_DELIVERY[2],
                0.35f,
            };
            renderFilledBlock(
                poseStack,
                deliveryPoint,
                cameraPos,
                deliveryFillColor
            );
        }

        // 7. Render path waypoints with pulsating effect and numbers
        java.util.List<BlockPos> waypoints = cell.getPathWaypoints();
        if (!waypoints.isEmpty()) {
            float waypointPulse =
                0.5f +
                0.3f * (float) Math.sin(System.currentTimeMillis() / 200.0);
            float[] waypointColor = {
                COLOR_WAYPOINT[0],
                COLOR_WAYPOINT[1],
                COLOR_WAYPOINT[2],
                waypointPulse,
            };

            for (int i = 0; i < waypoints.size(); i++) {
                BlockPos wp = waypoints.get(i);
                renderFilledBlock(poseStack, wp, cameraPos, waypointColor);
                // Render number above waypoint
                renderWaypointNumber(poseStack, wp, cameraPos, i + 1);
            }
        }

        // Restore rendering state
        RenderSystem.depthMask(true);
        RenderSystem.disableBlend();
    }

    /**
     * Render a collection of positions with the given base color at 0.35 alpha.
     */
    private static void renderPositionCollection(
        PoseStack poseStack,
        Collection<BlockPos> positions,
        Vec3 cameraPos,
        float[] baseColor
    ) {
        if (positions == null || positions.isEmpty()) return;

        float[] fillColor = { baseColor[0], baseColor[1], baseColor[2], 0.35f };

        for (BlockPos pos : positions) {
            renderFilledBlock(poseStack, pos, cameraPos, fillColor);
        }
    }

    /**
     * Render a filled block (semi-transparent cube).
     */
    public static void renderFilledBlock(
        PoseStack poseStack,
        BlockPos pos,
        Vec3 cameraPos,
        float[] color
    ) {
        poseStack.pushPose();

        double x = pos.getX() - cameraPos.x;
        double y = pos.getY() - cameraPos.y;
        double z = pos.getZ() - cameraPos.z;

        poseStack.translate(x, y, z);

        Matrix4f matrix = poseStack.last().pose();

        Tesselator tesselator = Tesselator.getInstance();
        BufferBuilder buffer = tesselator.getBuilder();

        buffer.begin(
            VertexFormat.Mode.QUADS,
            DefaultVertexFormat.POSITION_COLOR
        );

        float r = color[0];
        float g = color[1];
        float b = color[2];
        float a = color[3];

        float min = 0.0f;
        float max = 1.0f;

        // Bottom face
        vertex(buffer, matrix, min, min, min, r, g, b, a);
        vertex(buffer, matrix, max, min, min, r, g, b, a);
        vertex(buffer, matrix, max, min, max, r, g, b, a);
        vertex(buffer, matrix, min, min, max, r, g, b, a);

        // Top face
        vertex(buffer, matrix, min, max, min, r, g, b, a);
        vertex(buffer, matrix, min, max, max, r, g, b, a);
        vertex(buffer, matrix, max, max, max, r, g, b, a);
        vertex(buffer, matrix, max, max, min, r, g, b, a);

        // North face
        vertex(buffer, matrix, min, min, min, r, g, b, a);
        vertex(buffer, matrix, min, max, min, r, g, b, a);
        vertex(buffer, matrix, max, max, min, r, g, b, a);
        vertex(buffer, matrix, max, min, min, r, g, b, a);

        // South face
        vertex(buffer, matrix, min, min, max, r, g, b, a);
        vertex(buffer, matrix, max, min, max, r, g, b, a);
        vertex(buffer, matrix, max, max, max, r, g, b, a);
        vertex(buffer, matrix, min, max, max, r, g, b, a);

        // West face
        vertex(buffer, matrix, min, min, min, r, g, b, a);
        vertex(buffer, matrix, min, min, max, r, g, b, a);
        vertex(buffer, matrix, min, max, max, r, g, b, a);
        vertex(buffer, matrix, min, max, min, r, g, b, a);

        // East face
        vertex(buffer, matrix, max, min, min, r, g, b, a);
        vertex(buffer, matrix, max, max, min, r, g, b, a);
        vertex(buffer, matrix, max, max, max, r, g, b, a);
        vertex(buffer, matrix, max, min, max, r, g, b, a);

        tesselator.end();

        poseStack.popPose();
    }

    private static void vertex(
        BufferBuilder buffer,
        Matrix4f matrix,
        float x,
        float y,
        float z,
        float r,
        float g,
        float b,
        float a
    ) {
        buffer.vertex(matrix, x, y, z).color(r, g, b, a).endVertex();
    }

    /**
     * Render a waypoint number floating above the block.
     */
    public static void renderWaypointNumber(
        PoseStack poseStack,
        BlockPos pos,
        Vec3 cameraPos,
        int number
    ) {
        poseStack.pushPose();

        double x = pos.getX() + 0.5 - cameraPos.x;
        double y = pos.getY() + 1.5 - cameraPos.y;
        double z = pos.getZ() + 0.5 - cameraPos.z;

        poseStack.translate(x, y, z);

        // Billboard effect - face the camera
        net.minecraft.client.Minecraft mc =
            net.minecraft.client.Minecraft.getInstance();
        poseStack.mulPose(mc.getEntityRenderDispatcher().cameraOrientation());
        poseStack.scale(-0.05f, -0.05f, 0.05f);

        // Render the number
        String text = String.valueOf(number);
        net.minecraft.client.gui.Font font = mc.font;
        float textWidth = font.width(text);

        // Background
        net.minecraft.client.renderer.MultiBufferSource.BufferSource buffer = mc
            .renderBuffers()
            .bufferSource();

        font.drawInBatch(
            text,
            -textWidth / 2,
            0,
            0x00FF80, // Bright green
            false,
            poseStack.last().pose(),
            buffer,
            net.minecraft.client.gui.Font.DisplayMode.NORMAL,
            0x80000000, // Semi-transparent black background
            15728880
        );
        buffer.endBatch();

        poseStack.popPose();
    }

    /**
     * Get the color for a marker type.
     */
    public static float[] getColorForType(MarkerType type) {
        return switch (type) {
            case WALL -> COLOR_WALL;
            case ANCHOR -> COLOR_ANCHOR;
            case BED -> COLOR_BED;
            case DOOR -> COLOR_DOOR;
            case DELIVERY -> COLOR_DELIVERY;
            case ENTRANCE -> COLOR_ENTRANCE;
            case PATROL -> COLOR_PATROL;
            case LOOT -> COLOR_LOOT;
            case SPAWNER -> COLOR_SPAWNER;
            case TRADER_SPAWN -> COLOR_TRADER_SPAWN;
            case MAID_SPAWN -> COLOR_MAID_SPAWN;
            case MERCHANT_SPAWN -> COLOR_MERCHANT_SPAWN;
        };
    }

    /**
     * Get the spawn point color.
     */
    public static float[] getSpawnColor() {
        return COLOR_SPAWN;
    }
}
