package com.simon816.chatui.util;

import com.google.common.collect.Lists;
import it.unimi.dsi.fastutil.chars.Char2ObjectMap;
import it.unimi.dsi.fastutil.chars.Char2ObjectOpenHashMap;
import org.spongepowered.api.Sponge;
import org.spongepowered.api.text.LiteralText;
import org.spongepowered.api.text.Text;

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

public class TextUtils {

    private final boolean forceUnicode;
    private final FontData fontData;
    private final UUID playerUuid;

    public TextUtils(FontData fontData, boolean forceUnicode, UUID playerUuid) {
        this.fontData = fontData;
        this.forceUnicode = forceUnicode;
        this.playerUuid = playerUuid;
    }

    public FontData getFontData() {
        return this.fontData;
    }

    public double getWidth(int codePoint, boolean isBold) {
        return this.fontData.getWidth(codePoint, isBold, this.forceUnicode);
    }

    public int getStringWidth(String text, boolean isBold) {
        double width = 0;
        for (int i = 0; i < text.length(); ++i) {
            width += getWidth(text.codePointAt(i), isBold);
        }
        return (int) Math.ceil(width);
    }

    public int getWidth(char c, boolean isBold) {
        return (int) Math.ceil(getWidth((int) c, isBold));
    }

    public int getWidth(Text text) {
        return (int) Math.ceil(getWidth0(text, false));
    }

    private double getWidth0(Text text, boolean parentIsbold) {
        double width = 0;
        boolean thisIsBold = text.getStyle().isBold().orElse(parentIsbold);
        if (text instanceof LiteralText) {
            String content = ((LiteralText) text).getContent();
            width += getStringWidth(content, thisIsBold);
            for (Text child : text.getChildren()) {
                width += getWidth0(child, thisIsBold);
            }
        } else {
            width += getStringWidth(text.toPlain(), thisIsBold);
        }
        return width;
    }

    public List<String> splitLines(String original, int maxWidth) {
        List<String> output = Lists.newArrayList();
        TextSplitter.splitLines(original, output, maxWidth, this);
        return output;
    }

    public List<Text> splitLines(Text original, int maxWidth) {
        List<Text> output = Lists.newArrayList();
        TextSplitter.splitLines(original, output, maxWidth, Sponge.getServer().getPlayer(this.playerUuid).get().getLocale(), this);
        return output;
    }

    public int repeat(Text.Builder builder, char repChar, int length) {
        return startRepeatTerminate(builder, (char) 0, repChar, (char) 0, length);
    }

    public Text repeatAndTerminate(char repChar, char termChar, int length) {
        Text.Builder builder = Text.builder();
        repeatAndTerminate(builder, repChar, termChar, length);
        return builder.build();
    }

    public int repeatAndTerminate(Text.Builder builder, char repChar, char termChar, int length) {
        return startRepeatTerminate(builder, (char) 0, repChar, termChar, length);
    }

    public int startAndRepeat(Text.Builder builder, char startChar, char repChar, int length) {
        return startRepeatTerminate(builder, startChar, repChar, (char) 0, length);
    }

    public Text startRepeatTerminate(char startChar, char repChar, char termChar, int length) {
        Text.Builder builder = Text.builder();
        startRepeatTerminate(builder, startChar, repChar, termChar, length);
        return builder.build();
    }

    public int startRepeatTerminate(Text.Builder builder, char startChar, char repChar, char termChar, int length) {
        int repWidth = getWidth(repChar, false);
        int startWidth = 0;
        if (startChar != 0) {
            startWidth = getWidth(startChar, false);
        }
        int termWidth = 0;
        if (termChar != 0) {
            termWidth = getWidth(termChar, false);
        }

        length -= termWidth + startWidth;
        if (length < 0) {
            return length;
        }
        StringBuilder stringBuilder = new StringBuilder();
        if (startChar != 0) {
            stringBuilder.append(startChar);
        }
        while (length >= repWidth) {
            length -= repWidth;
            stringBuilder.append(repChar);
        }
        if (termChar != 0) {
            stringBuilder.append(termChar);
        }
        builder.append(Text.of(stringBuilder.toString()));
        return length;
    }

    private static final Char2ObjectMap<Text> charTextCache = new Char2ObjectOpenHashMap<>();

    public static Text charCache(char c) {
        Text t = charTextCache.get(c);
        if (t == null) {
            charTextCache.put(c, t = Text.of(c));
        }
        return t;
    }

    // Space is same width for both unicode and non-unicode
    private static final int SPACE_WIDTH = FontData.VANILLA.getWidthInt(' ', false, false);
    public static final char PIXEL_CHAR = '⁚'; // 1px wide character

    public static void padSpaces(StringBuilder builder, int width) {
        while (width >= SPACE_WIDTH) {
            width -= SPACE_WIDTH;
            builder.append(' ');
        }

        while (width-- > 0) {
            builder.append(PIXEL_CHAR);
        }
    }

}