package zenith.zov.client.hud.elements.component;

import com.google.gson.JsonObject;
import com.mojang.authlib.GameProfile;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.UUID;
import java.util.stream.Collectors;
import net.minecraft.client.gui.DrawContext;
import net.minecraft.client.gui.PlayerSkinDrawer;
import net.minecraft.client.gui.hud.PlayerListHud.ScoreDisplayEntry;
import net.minecraft.client.network.PlayerListEntry;
import net.minecraft.client.render.entity.LivingEntityRenderer;
import net.minecraft.client.util.math.MatrixStack;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.scoreboard.ReadableScoreboardScore;
import net.minecraft.scoreboard.ScoreHolder;
import net.minecraft.scoreboard.Scoreboard;
import net.minecraft.scoreboard.ScoreboardDisplaySlot;
import net.minecraft.scoreboard.ScoreboardObjective;
import net.minecraft.scoreboard.ScoreboardCriterion.RenderType;
import net.minecraft.scoreboard.number.NumberFormat;
import net.minecraft.scoreboard.number.StyledNumberFormat;
import net.minecraft.text.OrderedText;
import net.minecraft.text.Text;
import net.minecraft.world.GameMode;
import org.jetbrains.annotations.Nullable;
import zenith.zov.base.animations.base.Animation;
import zenith.zov.base.animations.base.Easing;
import zenith.zov.client.hud.elements.draggable.DraggableHudElement;
import zenith.zov.client.modules.impl.render.Interface;
import zenith.zov.utility.render.display.base.CustomDrawContext;

public class PlayerListComponent extends DraggableHudElement {
    private final Animation animation = new Animation(250L, Easing.BAKEK_SIZE);

    public PlayerListComponent(String name) {
        super(name, 0.0F, 0.0F, 0.0F, 0.0F, 0.0F, 0.0F, null);
    }

    @Override
    protected void renderXLine(CustomDrawContext ctx, DraggableHudElement.SheetCode nearest) {
    }

    @Override
    protected void renderYLine(CustomDrawContext ctx, DraggableHudElement.SheetCode nearest) {
    }

    @Override
    public void set(float x, float y) {
    }

    @Override
    public void update(float widthScreen, float heightScreen) {
    }

    @Override
    public void release() {
    }

    @Override
    public void windowResized(float newWindowWidth, float newWindowHeight) {
    }

    @Override
    public void set(CustomDrawContext ctx, float x, float y, Interface dragManager, float widthScreen, float heightScreen) {
    }

    @Override
    public JsonObject save() {
        return new JsonObject();
    }

    @Override
    public void load(JsonObject obj) {
    }

    @Override
    public void render(CustomDrawContext ctx) {
        Scoreboard scoreboard = mc.world.getScoreboard();
        ScoreboardObjective scoreboardObjective = scoreboard.getObjectiveForSlot(ScoreboardDisplaySlot.LIST);
        this.animation
            .update(
                mc.options.playerListKey.isPressed()
                    && (!mc.isInSingleplayer() || mc.player.networkHandler.getListedPlayerListEntries().size() > 1 || scoreboardObjective != null)
            );
        if (this.animation.getValue() != 0.0F) {
            this.render(ctx, ctx.getScaledWindowWidth(), scoreboard, scoreboardObjective);
        }

        this.animation.setDuration(250L);
        this.animation.setEasing(Easing.BAKEK_SIZE);
    }

    public void render(DrawContext context, int scaledWindowWidth, Scoreboard scoreboard, @Nullable ScoreboardObjective objective) {
        float delta = this.animation.getValue();
        List<PlayerListEntry> players = mc.inGameHud.getPlayerListHud().collectPlayerEntries();
        List<ScoreDisplayEntry> scoreEntries = new ArrayList<>(players.size());
        int spaceWidth = mc.textRenderer.getWidth(" ");
        int nameColumnWidth = 0;
        int scoreColumnWidth = 0;

        for (PlayerListEntry entry : players) {
            Text nameText = mc.inGameHud.getPlayerListHud().getPlayerName(entry);
            nameColumnWidth = Math.max(nameColumnWidth, mc.textRenderer.getWidth(nameText));
            int rawScore = 0;
            Text formattedScoreText = null;
            int formattedScoreWidth = 0;
            if (objective != null) {
                ScoreHolder holder = ScoreHolder.fromProfile(entry.getProfile());
                ReadableScoreboardScore readableScore = scoreboard.getScore(holder, objective);
                if (readableScore != null) {
                    rawScore = readableScore.getScore();
                }

                if (objective.getRenderType() != RenderType.HEARTS) {
                    NumberFormat fmt = objective.getNumberFormatOr(StyledNumberFormat.YELLOW);
                    formattedScoreText = ReadableScoreboardScore.getFormattedScore(readableScore, fmt);
                    formattedScoreWidth = mc.textRenderer.getWidth(formattedScoreText);
                    scoreColumnWidth = Math.max(scoreColumnWidth, formattedScoreWidth > 0 ? spaceWidth + formattedScoreWidth : 0);
                }
            }

            scoreEntries.add(new ScoreDisplayEntry(nameText, rawScore, formattedScoreText, formattedScoreWidth));
        }

        if (!mc.inGameHud.getPlayerListHud().hearts.isEmpty()) {
            Set<UUID> visibleIds = players.stream().map(p -> p.getProfile().getId()).collect(Collectors.toSet());
            mc.inGameHud.getPlayerListHud().hearts.keySet().removeIf(id -> !visibleIds.contains(id));
        }

        int total = players.size();
        int rowsPerColumn = total;

        int columns;
        for (columns = 1; rowsPerColumn > 20; rowsPerColumn = (total + columns - 1) / columns) {
            columns++;
        }

        boolean showHead = true;
        int extraColumnWidth;
        if (objective != null) {
            extraColumnWidth = objective.getRenderType() == RenderType.HEARTS ? 90 : scoreColumnWidth;
        } else {
            extraColumnWidth = 0;
        }

        int columnWidth = Math.min(columns * ((showHead ? 9 : 0) + nameColumnWidth + extraColumnWidth + 13), scaledWindowWidth - 50) / columns;
        int leftX = scaledWindowWidth / 2 - (columnWidth * columns + (columns - 1) * 5) / 2;
        int yCursor = 10;
        int contentWidth = columnWidth * columns + (columns - 1) * 5;
        List<OrderedText> headerLines = null;
        if (mc.inGameHud.getPlayerListHud().header != null) {
            headerLines = mc.textRenderer.wrapLines(mc.inGameHud.getPlayerListHud().header, scaledWindowWidth - 50);

            for (OrderedText line : headerLines) {
                contentWidth = Math.max(contentWidth, mc.textRenderer.getWidth(line));
            }
        }

        List<OrderedText> footerLines = null;
        if (mc.inGameHud.getPlayerListHud().footer != null) {
            footerLines = mc.textRenderer.wrapLines(mc.inGameHud.getPlayerListHud().footer, scaledWindowWidth - 50);

            for (OrderedText line : footerLines) {
                contentWidth = Math.max(contentWidth, mc.textRenderer.getWidth(line));
            }
        }

        int headerHeight = headerLines != null ? headerLines.size() * 9 : 0;
        int headerGap = headerLines != null ? 1 : 0;
        int tableHeight = rowsPerColumn * 9;
        int footerGap = footerLines != null ? 1 : 0;
        int footerHeight = footerLines != null ? footerLines.size() * 9 : 0;
        int totalHeight = headerHeight + headerGap + tableHeight + footerGap + footerHeight;
        float centerX = (float)(scaledWindowWidth / 2 - 1);
        float centerY = 10.0F + (float)totalHeight / 2.0F;
        float scale = Math.max(1.0E-4F, delta);
        MatrixStack matrices = context.getMatrices();
        matrices.push();
        matrices.translate(centerX, centerY, 0.0F);
        matrices.scale(scale, scale, 1.0F);
        matrices.translate(-centerX, -centerY, 0.0F);
        if (headerLines != null) {
            context.fill(
                scaledWindowWidth / 2 - contentWidth / 2 - 1,
                yCursor - 1,
                scaledWindowWidth / 2 + contentWidth / 2 + 1,
                yCursor + headerLines.size() * 9,
                Integer.MIN_VALUE
            );

            for (OrderedText line : headerLines) {
                int lineWidth = mc.textRenderer.getWidth(line);
                context.drawTextWithShadow(mc.textRenderer, line, scaledWindowWidth / 2 - lineWidth / 2, yCursor, -1);
                yCursor += 9;
            }

            yCursor++;
        }

        context.fill(
            scaledWindowWidth / 2 - contentWidth / 2 - 1,
            yCursor - 1,
            scaledWindowWidth / 2 + contentWidth / 2 + 1,
            yCursor + rowsPerColumn * 9,
            Integer.MIN_VALUE
        );
        int rowBgColor = mc.options.getTextBackgroundColor(553648127);

        for (int index = 0; index < total; index++) {
            int col = index / rowsPerColumn;
            int row = index % rowsPerColumn;
            int cellLeft = leftX + col * columnWidth + col * 5;
            int cellTop = yCursor + row * 9;
            context.fill(cellLeft, cellTop, cellLeft + columnWidth, cellTop + 8, rowBgColor);
            if (index < players.size()) {
                PlayerListEntry ple = players.get(index);
                ScoreDisplayEntry sde = scoreEntries.get(index);
                GameProfile profile = ple.getProfile();
                int textX = cellLeft;
                if (showHead) {
                    PlayerEntity playerEntity = mc.world.getPlayerByUuid(profile.getId());
                    boolean flip = playerEntity != null && LivingEntityRenderer.shouldFlipUpsideDown(playerEntity);
                    PlayerSkinDrawer.draw(context, ple.getSkinTextures().texture(), cellLeft, cellTop, 8, ple.shouldShowHat(), flip, -1);
                    textX = cellLeft + 9;
                }

                int nameColor = ple.getGameMode() == GameMode.SPECTATOR ? -1859310289 : -1;
                context.drawTextWithShadow(mc.textRenderer, sde.name(), textX, cellTop, nameColor);
                if (objective != null && ple.getGameMode() != GameMode.SPECTATOR) {
                    int scoreLeft = textX + nameColumnWidth + 1;
                    int scoreRight = scoreLeft + extraColumnWidth;
                    if (scoreRight - scoreLeft > 5) {
                        mc.inGameHud.getPlayerListHud().renderScoreboardObjective(objective, cellTop, sde, scoreLeft, scoreRight, profile.getId(), context);
                    }
                }

                mc.inGameHud.getPlayerListHud().renderLatencyIcon(context, columnWidth, textX - (showHead ? 9 : 0), cellTop, ple);
            }
        }

        if (footerLines != null) {
            yCursor += rowsPerColumn * 9 + 1;
            context.fill(
                scaledWindowWidth / 2 - contentWidth / 2 - 1,
                yCursor - 1,
                scaledWindowWidth / 2 + contentWidth / 2 + 1,
                yCursor + footerLines.size() * 9,
                Integer.MIN_VALUE
            );

            for (OrderedText line : footerLines) {
                int lineWidth = mc.textRenderer.getWidth(line);
                context.drawTextWithShadow(mc.textRenderer, line, scaledWindowWidth / 2 - lineWidth / 2, yCursor, -1);
                yCursor += 9;
            }
        }

        matrices.pop();
    }
}
