/*
 * This file is part of NucleusFramework for Bukkit, licensed under the MIT License (MIT).
 *
 * Copyright (c) JCThePants (www.jcwhatever.com)
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */

package com.jcwhatever.nucleus.utils.text;

import com.jcwhatever.nucleus.utils.PreCon;
import com.jcwhatever.nucleus.utils.text.TextFormat.TextFormats;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;

/**
 * A collection of {@link TextComponent} instances.
 */
public class TextComponents implements Iterable<TextComponent> {

    private final String _text;
    private final List<TextComponent> _components;
    private final Object _sync = new Object();
    private String _formatted;

    /**
     * Constructor.
     *
     * <p>Generates {@code TextComponent} collection from text.</p>
     *
     * @param text  The text.
     */
    public TextComponents(String text) {
        PreCon.notNull(text);

        _text = text;
        _components = getTextComponents(text);
    }

    /**
     * Constructor.
     *
     * @param textComponents  The text components to clone.
     */
    public TextComponents(TextComponents textComponents) {
        PreCon.notNull(textComponents);

        _text = textComponents._text;
        _components = new ArrayList<>(textComponents._components);
    }

    /**
     * Get the text used to generate the
     * {@code TextComponent}'s in the collection.
     */
    public String getText() {
        return _text;
    }

    /**
     * Get text generated from the components.
     */
    public String getFormatted() {
        if (_formatted == null) {
            synchronized (_sync) {

                if (_formatted != null)
                    return _formatted;

                StringBuilder buffer = new StringBuilder(_components.size() * 15);

                for (TextComponent component : this) {
                    buffer.append(component.getFormatted());
                }

                _formatted = buffer.toString();
            }
        }
        return _formatted;
    }

    /**
     * Get the number of {@code TextComponent}'s in
     * the collection.
     */
    public int size() {
        return _components.size();
    }

    /**
     * Get the {@code TextComponent} at the specified
     * index location.
     *
     * @param index  The index.
     */
    public TextComponent get(int index) {
        return _components.get(index);
    }

    @Override
    public Iterator<TextComponent> iterator() {
        return new Iterator<TextComponent>() {

            int index =0;

            @Override
            public boolean hasNext() {
                return index <= _components.size();
            }

            @Override
            public TextComponent next() {
                TextComponent n = _components.get(index);
                index ++;
                return n;
            }

            @Override
            public void remove() {
                throw new UnsupportedOperationException();
            }
        };
    }

    /**
     * Parse the given text into a list of {@code TextComponents}.
     *
     * @param text  The text to parse.
     */
    private static List<TextComponent> getTextComponents(String text) {
        PreCon.notNull(text);

        text = TextUtils.format(text);

        StringBuilder buffer = new StringBuilder(text.length());

        LinkedList<TextComponent> result = new LinkedList<>();

        for (int i = 0; i < text.length(); i++) {

            char ch = text.charAt(i);

            if (ch == TextFormat.CHAR && i < text.length() - 1) {

                TextFormat format = TextFormat.fromFormatChar(text.charAt(i + 1));
                if (format == null) {
                    buffer.append(ch);
                    continue;
                }

                if (buffer.length() > 0) {

                    TextFormats formats = TextFormat.getFormatAt(i, text);
                    TextComponent textComponent = new TextComponent(buffer.toString(), formats);

                    buffer.setLength(0);

                    if (textComponent.getText().length() > 0)
                        result.addLast(textComponent);
                }

                i += 1;
            }
            else {
                buffer.append(ch);
            }
        }

        if (buffer.length() > 0) {
            TextComponent textComponent = new TextComponent(buffer.toString(), null);
            result.addLast(textComponent);
        }

        return new ArrayList<>(result);
    }
}

@@ -4602,18 +4602,92 @@
         
-if
+boolean hasFormat = false;%0A            int startIndex = i;%0A            while
  (ch == 
@@ -4726,20 +4726,19 @@
 th() - 1
-) %7B%0A
+ &&
 %0A       
@@ -4750,27 +4750,11 @@
     
-TextFormat format =
+   
  Tex
@@ -4761,20 +4761,18 @@
 tFormat.
-from
+is
 FormatCh
@@ -4793,19 +4793,20 @@
 (i + 1))
-;%0A 
+) %7B%0A
         
@@ -4816,29 +4816,26 @@
     
-if (f
+ hasF
 ormat =
-= null) %7B
+ true;
 %0A   
@@ -4851,78 +4851,96 @@
     
-    buffer.append(ch);%0A                    continue;%0A                %7D
+i += 1;%0A                ch = text.charAt(i);%0A            %7D%0A%0A            if (hasFormat) %7B
 %0A%0A  
@@ -5046,17 +5046,30 @@
 ormatAt(
-i
+startIndex - 1
 , text);
@@ -5347,33 +5347,8 @@
    %7D
-%0A%0A                i += 1;
 %0A   
@@ -5377,16 +5377,17 @@
  else %7B%0A
+%0A
         
@@ -5469,24 +5469,107 @@
 th() %3E 0) %7B%0A
+            TextFormats formats = TextFormat.getFormatAt(text.length() - 1, text);%0A
             
@@ -5635,20 +5635,23 @@
 ring(), 
-null
+formats
 );%0A     

fix last component missing formats