package cz.jzitnik.tui.utils;

/**
 * Utility class for combining ANSI-colored text sprites line-by-line,
 * preserving non-transparent pixels (those that are not "empty").
 * Useful for rendering layers (e.g. items over blocks) in a terminal UI.
 */
public class SpriteCombiner {
    /**
     * Combines multiple ANSI-colored text sprites into a single sprite.
     * Later sprites overlay earlier ones, preserving non-transparent characters.
     *
     * @param sprites An array of ANSI-formatted sprite strings.
     * @return A single string representing the combined sprite.
     */
    public static String combineSprites(String[] sprites) {
        if (sprites == null || sprites.length == 0) {
            return "";
        }

        String combinedSprite = sprites[0];

        for (int i = 1; i < sprites.length; i++) {
            combinedSprite = combineTwoSprites(combinedSprite, sprites[i]);
        }

        return combinedSprite;
    }

    /**
     * Combines two ANSI-formatted sprites into one, line by line.
     * Characters from {@code sprite2} are used unless they are transparent (spaces with \033[0m or \033[49m).
     *
     * @param sprite1 The base (background) sprite.
     * @param sprite2 The overlay (foreground) sprite.
     * @return A string representing the resulting combined sprite.
     */
    public static String combineTwoSprites(String sprite1, String sprite2) {
        String[] rows1 = sprite1.split("\n");
        String[] rows2 = sprite2.split("\n");

        StringBuilder combinedSprite = new StringBuilder();

        int numRows = Math.min(rows1.length, rows2.length);

        for (int i = 0; i < numRows; i++) {
            String row1 = rows1[i];
            String row2 = rows2[i];
            StringBuilder combinedRow = new StringBuilder();

            int cursor1 = 0;
            int cursor2 = 0;

            // Ensure we stay within the bounds of both rows.
            int prev = -1;
            while (cursor1 < row1.length() && cursor2 < row2.length()) {
                String color1 = extractColorCode(row1, cursor1);
                int pixelIndex1 = cursor1 + color1.length();
                if (pixelIndex1 >= row1.length())
                    break; // Avoid out-of-bounds
                char pixel1 = row1.charAt(pixelIndex1);

                String color2 = extractColorCode(row2, cursor2);
                int pixelIndex2 = cursor2 + color2.length();
                if (pixelIndex2 >= row2.length())
                    break; // Avoid out-of-bounds
                char pixel2 = row2.charAt(pixelIndex2);

                if (color2.equals("\033[0m") && pixel2 == ' ' || color2.equals("\033[49m") && pixel2 == ' ') {
                    if (prev != 1 && color1.isEmpty()) {
                        combinedRow.append(getColorCode(row1, cursor1));
                        prev = 1;
                    }
                    combinedRow.append(color1).append(pixel1);
                } else {
                    if (prev != 2) {
                        prev = 2;
                    }
                    combinedRow.append(color2).append(pixel2);
                }

                cursor1 += color1.length() + 1;
                cursor2 += color2.length() + 1;
            }

            combinedSprite.append(combinedRow).append("\n");
        }

        return combinedSprite.toString();
    }

    /**
     * Recursively finds the last ANSI escape code preceding the given index.
     *
     * @param row   The sprite row string.
     * @param index The index to search from.
     * @return The ANSI color code at or before the index.
     */
    private static String getColorCode(String row, int index) {
        if (row.charAt(index) != '\033') {
            return getColorCode(row, index - 1);
        }

        return extractColorCode(row, index);
    }

    /**
     * Extracts an ANSI color escape code starting at the specified index in a row.
     *
     * @param row   The sprite row string.
     * @param index The starting index (should be at '\033').
     * @return The full ANSI escape code (e.g., {@code "\033[48;2;255;255;255m"}).
     */
    private static String extractColorCode(String row, int index) {
        StringBuilder colorCode = new StringBuilder();

        if (row.charAt(index) == '\033') {
            while (index < row.length() && row.charAt(index) != 'm') {
                colorCode.append(row.charAt(index));
                index++;
            }
            colorCode.append('m');
        }

        return colorCode.toString();
    }
}
