// Generated by delombok at Fri Oct 24 23:16:37 UTC 2025
package net.md_5.bungee.api.chat;

import com.google.common.base.Preconditions;
import java.awt.Color;
import java.util.ArrayList;
import java.util.List;
import net.md_5.bungee.api.ChatColor;

/**
 * <p>
 * ComponentBuilder simplifies creating basic messages by allowing the use of a
 * chainable builder.
 * </p>
 * <pre>
 * new ComponentBuilder("Hello ").color(ChatColor.RED).
 * append("World").color(ChatColor.BLUE). append("!").bold(true).create();
 * </pre>
 * <p>
 * All methods (excluding {@link #append(String)} and {@link #create()} work on
 * the last part appended to the builder, so in the example above "Hello " would
 * be {@link net.md_5.bungee.api.ChatColor#RED} and "World" would be
 * {@link net.md_5.bungee.api.ChatColor#BLUE} but "!" would be bold and
 * {@link net.md_5.bungee.api.ChatColor#BLUE} because append copies the previous
 * part's formatting
 * </p>
 */
public final class ComponentBuilder {
    /**
     * The position for the current part to modify. Modified cursors will
     * automatically reset to the last part after appending new components.
     * Default value at -1 to assert that the builder has no parts.
     */
    private int cursor = -1;
    private final List<BaseComponent> parts = new ArrayList<BaseComponent>();
    private BaseComponent dummy;

    private ComponentBuilder(BaseComponent[] parts) {
        for (BaseComponent baseComponent : parts) {
            this.parts.add(baseComponent.duplicate());
        }
        resetCursor();
    }

    /**
     * Creates a ComponentBuilder from the other given ComponentBuilder to clone
     * it.
     *
     * @param original the original for the new ComponentBuilder.
     */
    public ComponentBuilder(ComponentBuilder original) {
        this(original.parts.toArray(new BaseComponent[0]));
    }

    /**
     * Creates a ComponentBuilder with the given text as the first part.
     *
     * @param text the first text element
     */
    public ComponentBuilder(String text) {
        this(new TextComponent(text));
    }

    /**
     * Creates a ComponentBuilder with the given component as the first part.
     *
     * @param component the first component element
     */
    public ComponentBuilder(BaseComponent component) {
        this(new BaseComponent[] {component});
    }

    private BaseComponent getDummy() {
        if (dummy == null) {
            dummy = new BaseComponent() {
                @Override
                public BaseComponent duplicate() {
                    return this;
                }
            };
        }
        return dummy;
    }

    /**
     * Resets the cursor to index of the last element.
     *
     * @return this ComponentBuilder for chaining
     */
    public ComponentBuilder resetCursor() {
        cursor = parts.size() - 1;
        return this;
    }

    /**
     * Sets the position of the current component to be modified
     *
     * @param pos the cursor position synonymous to an element position for a
     * list
     * @return this ComponentBuilder for chaining
     * @throws IndexOutOfBoundsException if the index is out of range
     * ({@code index < 0 || index >= size()})
     */
    public ComponentBuilder setCursor(int pos) throws IndexOutOfBoundsException {
        if ((this.cursor != pos) && (pos < 0 || pos >= parts.size())) {
            throw new IndexOutOfBoundsException("Cursor out of bounds (expected between 0 + " + (parts.size() - 1) + ")");
        }
        this.cursor = pos;
        return this;
    }

    /**
     * Appends a component to the builder and makes it the current target for
     * formatting. The component will have all the formatting from previous
     * part.
     *
     * @param component the component to append
     * @return this ComponentBuilder for chaining
     */
    public ComponentBuilder append(BaseComponent component) {
        return append(component, FormatRetention.ALL);
    }

    /**
     * Appends a component to the builder and makes it the current target for
     * formatting. You can specify the amount of formatting retained from
     * previous part.
     *
     * @param component the component to append
     * @param retention the formatting to retain
     * @return this ComponentBuilder for chaining
     */
    public ComponentBuilder append(BaseComponent component, FormatRetention retention) {
        BaseComponent previous = (parts.isEmpty()) ? null : parts.get(parts.size() - 1);
        if (previous == null) {
            previous = dummy;
            dummy = null;
        }
        if (previous != null && !component.isReset()) {
            component.copyFormatting(previous, retention, false);
        }
        parts.add(component);
        resetCursor();
        return this;
    }

    /**
     * Appends the components to the builder and makes the last element the
     * current target for formatting. The components will have all the
     * formatting from previous part.
     *
     * @param components the components to append
     * @return this ComponentBuilder for chaining
     */
    public ComponentBuilder append(BaseComponent[] components) {
        return append(components, FormatRetention.ALL);
    }

    /**
     * Appends the components to the builder and makes the last element the
     * current target for formatting. You can specify the amount of formatting
     * retained from previous part.
     *
     * @param components the components to append
     * @param retention the formatting to retain
     * @return this ComponentBuilder for chaining
     */
    public ComponentBuilder append(BaseComponent[] components, FormatRetention retention) {
        Preconditions.checkArgument(components.length != 0, "No components to append");
        for (BaseComponent component : components) {
            append(component, retention);
        }
        return this;
    }

    /**
     * Appends the {@link TranslationProvider} object to the builder and makes
     * the last element the current target for formatting. The components will
     * have all the formatting from previous part.
     *
     * @param translatable the translatable object to append
     * @return this ComponentBuilder for chaining
     */
    public ComponentBuilder append(TranslationProvider translatable) {
        return append(translatable, FormatRetention.ALL);
    }

    /**
     * Appends the {@link TranslationProvider} object to the builder and makes
     * the last element the current target for formatting. You can specify the
     * amount of formatting retained from previous part.
     *
     * @param translatable the translatable object to append
     * @param retention the formatting to retain
     * @return this ComponentBuilder for chaining
     */
    public ComponentBuilder append(TranslationProvider translatable, FormatRetention retention) {
        return append(translatable.asTranslatableComponent(), retention);
    }

    /**
     * Appends the text to the builder and makes it the current target for
     * formatting. The text will have all the formatting from previous part.
     *
     * @param text the text to append
     * @return this ComponentBuilder for chaining
     */
    public ComponentBuilder append(String text) {
        return append(text, FormatRetention.ALL);
    }

    /**
     * Parse text to BaseComponent[] with colors and format, appends the text to
     * the builder and makes it the current target for formatting. The component
     * will have all the formatting from previous part.
     *
     * @param text the text to append
     * @return this ComponentBuilder for chaining
     */
    public ComponentBuilder appendLegacy(String text) {
        return append(TextComponent.fromLegacyText(text));
    }

    /**
     * Appends the text to the builder and makes it the current target for
     * formatting. You can specify the amount of formatting retained from
     * previous part.
     *
     * @param text the text to append
     * @param retention the formatting to retain
     * @return this ComponentBuilder for chaining
     */
    public ComponentBuilder append(String text, FormatRetention retention) {
        return append(new TextComponent(text), retention);
    }

    /**
     * Allows joining additional components to this builder using the given
     * {@link Joiner} and {@link FormatRetention#ALL}.
     *
     * Simply executes the provided joiner on this instance to facilitate a
     * chain pattern.
     *
     * @param joiner joiner used for operation
     * @return this ComponentBuilder for chaining
     */
    public ComponentBuilder append(Joiner joiner) {
        return joiner.join(this, FormatRetention.ALL);
    }

    /**
     * Allows joining additional components to this builder using the given
     * {@link Joiner}.
     *
     * Simply executes the provided joiner on this instance to facilitate a
     * chain pattern.
     *
     * @param joiner joiner used for operation
     * @param retention the formatting to retain
     * @return this ComponentBuilder for chaining
     */
    public ComponentBuilder append(Joiner joiner, FormatRetention retention) {
        return joiner.join(this, retention);
    }

    /**
     * Remove the component part at the position of given index.
     *
     * @param pos the index to remove at
     * @throws IndexOutOfBoundsException if the index is out of range
     * ({@code index < 0 || index >= size()})
     */
    public void removeComponent(int pos) throws IndexOutOfBoundsException {
        if (parts.remove(pos) != null) {
            resetCursor();
        }
    }

    /**
     * Gets the component part at the position of given index.
     *
     * @param pos the index to find
     * @return the component
     * @throws IndexOutOfBoundsException if the index is out of range
     * ({@code index < 0 || index >= size()})
     */
    public BaseComponent getComponent(int pos) throws IndexOutOfBoundsException {
        return parts.get(pos);
    }

    /**
     * Gets the component at the position of the cursor.
     *
     * @return the active component or null if builder is empty
     */
    public BaseComponent getCurrentComponent() {
        return (cursor == -1) ? getDummy() : parts.get(cursor);
    }

    /**
     * Sets the color of the current part.
     *
     * @param color the new color
     * @return this ComponentBuilder for chaining
     */
    public ComponentBuilder color(ChatColor color) {
        getCurrentComponent().setColor(color);
        return this;
    }

    /**
     * Sets the shadow color of the current part.
     *
     * @param color the new shadow color
     * @return this ComponentBuilder for chaining
     * @since Minecraft 1.21.4-pre1
     */
    public ComponentBuilder shadowColor(Color color) {
        getCurrentComponent().setShadowColor(color);
        return this;
    }

    /**
     * Sets the font of the current part.
     *
     * @param font the new font
     * @return this ComponentBuilder for chaining
     */
    public ComponentBuilder font(String font) {
        getCurrentComponent().setFont(font);
        return this;
    }

    /**
     * Sets whether the current part is bold.
     *
     * @param bold whether this part is bold
     * @return this ComponentBuilder for chaining
     */
    public ComponentBuilder bold(boolean bold) {
        getCurrentComponent().setBold(bold);
        return this;
    }

    /**
     * Sets whether the current part is italic.
     *
     * @param italic whether this part is italic
     * @return this ComponentBuilder for chaining
     */
    public ComponentBuilder italic(boolean italic) {
        getCurrentComponent().setItalic(italic);
        return this;
    }

    /**
     * Sets whether the current part is underlined.
     *
     * @param underlined whether this part is underlined
     * @return this ComponentBuilder for chaining
     */
    public ComponentBuilder underlined(boolean underlined) {
        getCurrentComponent().setUnderlined(underlined);
        return this;
    }

    /**
     * Sets whether the current part is strikethrough.
     *
     * @param strikethrough whether this part is strikethrough
     * @return this ComponentBuilder for chaining
     */
    public ComponentBuilder strikethrough(boolean strikethrough) {
        getCurrentComponent().setStrikethrough(strikethrough);
        return this;
    }

    /**
     * Sets whether the current part is obfuscated.
     *
     * @param obfuscated whether this part is obfuscated
     * @return this ComponentBuilder for chaining
     */
    public ComponentBuilder obfuscated(boolean obfuscated) {
        getCurrentComponent().setObfuscated(obfuscated);
        return this;
    }

    /**
     * Applies the provided {@link ComponentStyle} to the current part.
     *
     * @param style the style to apply
     * @return this ComponentBuilder for chaining
     */
    public ComponentBuilder style(ComponentStyle style) {
        getCurrentComponent().applyStyle(style);
        return this;
    }

    /**
     * Sets the insertion text for the current part.
     *
     * @param insertion the insertion text
     * @return this ComponentBuilder for chaining
     */
    public ComponentBuilder insertion(String insertion) {
        getCurrentComponent().setInsertion(insertion);
        return this;
    }

    /**
     * Sets the click event for the current part.
     *
     * @param clickEvent the click event
     * @return this ComponentBuilder for chaining
     */
    public ComponentBuilder event(ClickEvent clickEvent) {
        getCurrentComponent().setClickEvent(clickEvent);
        return this;
    }

    /**
     * Sets the hover event for the current part.
     *
     * @param hoverEvent the hover event
     * @return this ComponentBuilder for chaining
     */
    public ComponentBuilder event(HoverEvent hoverEvent) {
        getCurrentComponent().setHoverEvent(hoverEvent);
        return this;
    }

    /**
     * Sets the current part back to normal settings. Only text is kept.
     *
     * @return this ComponentBuilder for chaining
     */
    public ComponentBuilder reset() {
        return retain(FormatRetention.NONE);
    }

    /**
     * Retains only the specified formatting. Text is not modified.
     *
     * @param retention the formatting to retain
     * @return this ComponentBuilder for chaining
     */
    public ComponentBuilder retain(FormatRetention retention) {
        getCurrentComponent().retain(retention);
        return this;
    }

    /**
     * Returns the component built by this builder. If this builder is empty, an
     * empty text component will be returned.
     *
     * @return the component
     */
    public BaseComponent build() {
        TextComponent base = new TextComponent();
        if (!parts.isEmpty()) {
            List<BaseComponent> cloned = new ArrayList<>(parts);
            cloned.replaceAll(BaseComponent::duplicate);
            base.setExtra(cloned);
        }
        return base;
    }

    /**
     * Returns the components needed to display the message created by this
     * builder.git
     * <p>
     * <strong>NOTE:</strong> {@link #build()} is preferred as it will
     * consolidate all components into a single BaseComponent with extra
     * contents as opposed to an array of components which is non-standard and
     * may result in unexpected behavior.
     *
     * @return the created components
     */
    public BaseComponent[] create() {
        BaseComponent[] cloned = new BaseComponent[parts.size()];
        int i = 0;
        for (BaseComponent part : parts) {
            cloned[i++] = part.duplicate();
        }
        return cloned;
    }


    public enum FormatRetention {
        /**
         * Specify that we do not want to retain anything from the previous
         * component.
         */
        NONE, /**
         * Specify that we want the formatting retained from the previous
         * component.
         */
        FORMATTING, /**
         * Specify that we want the events retained from the previous component.
         */
        EVENTS, /**
         * Specify that we want to retain everything from the previous
         * component.
         */
        ALL;
    }


    /**
     * Functional interface to join additional components to a ComponentBuilder.
     */
    public interface Joiner {
        /**
         * Joins additional components to the provided {@link ComponentBuilder}
         * and then returns it to fulfill a chain pattern.
         *
         * Retention may be ignored and is to be understood as an optional
         * recommendation to the Joiner and not as a guarantee to have a
         * previous component in builder unmodified.
         *
         * @param componentBuilder to which to append additional components
         * @param retention the formatting to possibly retain
         * @return input componentBuilder for chaining
         */
        ComponentBuilder join(ComponentBuilder componentBuilder, FormatRetention retention);
    }

    @java.lang.SuppressWarnings("all")
    @lombok.Generated
    public ComponentBuilder() {
    }

    /**
     * The position for the current part to modify. Modified cursors will
     * automatically reset to the last part after appending new components.
     * Default value at -1 to assert that the builder has no parts.
     */
    @java.lang.SuppressWarnings("all")
    @lombok.Generated
    public int getCursor() {
        return this.cursor;
    }

    @java.lang.SuppressWarnings("all")
    @lombok.Generated
    public List<BaseComponent> getParts() {
        return this.parts;
    }
}
