package mezz.jei.common.util;

import net.minecraft.ChatFormatting;
import net.minecraft.client.StringSplitter;
import net.minecraft.client.gui.Font;
import net.minecraft.client.gui.GuiGraphicsExtractor;
import net.minecraft.network.chat.Component;
import net.minecraft.network.chat.FormattedText;
import net.minecraft.network.chat.MutableComponent;
import net.minecraft.network.chat.Style;
import net.minecraft.util.FormattedCharSequence;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;

public final class StringUtil {
	private StringUtil() {

	}

	public static Component stripStyling(Component textComponent) {
		MutableComponent text = textComponent.plainCopy();
		for (Component sibling : textComponent.getSiblings()) {
			text.append(stripStyling(sibling));
		}
		return text;
	}

	public static String removeChatFormatting(String string) {
		return ChatFormatting.stripFormatting(string);
	}

	public static FormattedText truncateStringToWidth(FormattedText text, int width, Font font) {
		int ellipsisWidth = font.width("...");

		FormattedText truncatedText = font.substrByWidth(text, width - ellipsisWidth);

		Style style = getStyleAtEnd(truncatedText);

		return FormattedText.composite(truncatedText, Component.literal("...").setStyle(style));
	}

	public static Style getStyleAtEnd(FormattedText text) {
		final Style[] endStyle = {Style.EMPTY};

		text.visit(
			(style, s) -> {
				endStyle[0] = style;
				return Optional.empty();
			},
			Style.EMPTY
		);

		return endStyle[0];
	}

	/**
	 * Split and wrap lines, and truncate the last line if it exceeds the max lines.
	 * @return the wrapped lines, and a boolean indicating if the last line was truncated.
	 */
	public static Pair<List<FormattedText>, Boolean> splitLines(Font font, List<FormattedText> lines, int width, int maxLines) {
		if (lines.isEmpty()) {
			return new Pair<>(List.of(), false);
		}
		if (maxLines <= 0) {
			return new Pair<>(List.of(), true);
		}
		if (width <= 0) {
			return new Pair<>(List.copyOf(lines), false);
		}

		StringSplitter splitter = font.getSplitter();
		List<FormattedText> result = new ArrayList<>();
		for (FormattedText line : lines) {
			List<FormattedText> splitLines;
			if (line.getString().isEmpty()) {
				splitLines = List.of(line);
			} else {
				splitLines = splitter.splitLines(line, width, Style.EMPTY);
			}

			for (FormattedText splitLine : splitLines) {
				if (result.size() == maxLines) {
					// result is at the max size, but we still have more to add.
					// Truncate the last line to indicate that there is more text that can't be displayed.
					FormattedText last = result.removeLast();
					last = truncateStringToWidth(last, width, font);
					result.add(last);
					return new Pair<>(result, true);
				}
				result.add(splitLine);
			}

		}

		return new Pair<>(result, false);
	}

	public static List<FormattedText> expandNewlines(Component... descriptionComponents) {
		List<FormattedText> descriptionLinesExpanded = new ArrayList<>();
		for (Component descriptionLine : descriptionComponents) {
			ExpandNewLineTextAcceptor newLineTextAcceptor = new ExpandNewLineTextAcceptor();
			descriptionLine.visit(newLineTextAcceptor, Style.EMPTY);
			newLineTextAcceptor.addLinesTo(descriptionLinesExpanded);
		}
		return descriptionLinesExpanded;
	}

	public static String intsToString(Collection<Integer> indexes) {
		return indexes.stream()
			.sorted()
			.map(i -> Integer.toString(i))
			.collect(Collectors.joining(", "));
	}

	public static void drawCenteredStringWithShadow(GuiGraphicsExtractor guiGraphics, Font font, String string, ImmutableRect2i area, int color) {
		ImmutableRect2i textArea = MathUtil.centerTextArea(area, font, string);
		guiGraphics.text(font, string, textArea.getX(), textArea.getY(), color);
	}

	public static void drawCenteredStringWithShadow(GuiGraphicsExtractor guiGraphics, Font font, FormattedCharSequence text, ImmutableRect2i area, int color) {
		ImmutableRect2i textArea = MathUtil.centerTextArea(area, font, text);
		guiGraphics.text(font, text, textArea.getX(), textArea.getY(), color);
	}
}
