/*
 * Copyright 2015-2024 MicroEJ Corp. All rights reserved.
 * This library is provided in source code for use, modification and test, subject to license terms.
 * Any modification of the source code will break MicroEJ Corp. warranties on the whole library.
 */
package ej.widget.render;

import java.util.ArrayList;
import java.util.List;

import ej.microui.display.Font;

/**
 * Provides some text facilities.
 */
public class TextHelper {

	private static final char SPACE = ' ';
	private static final char NEW_LINE = '\n';
	private static final char CARRIAGE_RETURN = '\r';

	private TextHelper() {
	}

	/**
	 * Wraps a text in lines.
	 * <p>
	 * A new line is started:
	 * <ul>
	 * <li>after a '\n',</li>
	 * <li>when there is no more space on the current line.</li>
	 * </ul>
	 *
	 * @param string
	 *            the text.
	 * @param font
	 *            the font used to render the text.
	 * @param width
	 *            the size of a line.
	 * @return the array of lines.
	 */
	public static String[] wrap(String string, Font font, int width) { // NOSONAR Splitting the method would require
																		// creating an object.
		if (string.isEmpty()) {
			return new String[0];
		}

		List<String> lines = new ArrayList<>(1);

		int lastIndex = 0;
		int lastSplitIndex = 0;
		int currentWidth = 0;

		int spaceWidth = font.charWidth(SPACE);

		int nextSpace = string.indexOf(SPACE);
		boolean moreSpaces = nextSpace != -1;
		int nextLine = string.indexOf(NEW_LINE);
		boolean moreLines = nextLine != -1;

		boolean stop = false;
		do {
			int index;
			boolean isSpace;
			if (moreLines && (!moreSpaces || nextSpace > nextLine)) {
				// New line before space.
				isSpace = false;
				index = nextLine;
				nextLine = string.indexOf(NEW_LINE, index + 1);
				moreLines = nextLine != -1;
			} else if (moreSpaces) {
				isSpace = true;
				index = nextSpace;
				nextSpace = string.indexOf(SPACE, index + 1);
				moreSpaces = nextSpace != -1;
			} else {
				// No more split characters.
				isSpace = false;
				index = string.length();
				stop = true;
			}

			int wordWidth = font.substringWidth(string, lastIndex, index - lastIndex);
			if (wordWidth + currentWidth > width) {
				// The last word exceeds the max width.
				try {
					// Split to previous word.
					lines.add(string.substring(lastSplitIndex, lastIndex - 1));
					lastSplitIndex = lastIndex;
					currentWidth = 0;
				} catch (IndexOutOfBoundsException e) {
					// The word is longer than the available width: lastIndex > lastSplitIndex.
				}
			}
			lastIndex = index + 1;
			if (isSpace) {
				// Do not split for spaces, just save the last index.
				currentWidth += wordWidth + spaceWidth;
			} else if (stop) {
				lines.add(string.substring(lastSplitIndex, index));
				break;
			} else {
				// Split on explicit new line.
				if (index > 0 && string.charAt(index - 1) == CARRIAGE_RETURN) {
					// Remove carriage return.
					lines.add(string.substring(lastSplitIndex, index - 1));
				} else {
					lines.add(string.substring(lastSplitIndex, index));
				}
				lastSplitIndex = lastIndex;
				currentWidth = 0;
			}
		} while (true);

		return lines.toArray(new String[lines.size()]);
	}

	/**
	 * Gets the lines in a text.
	 * <p>
	 * A new line is started after a '\n'.
	 *
	 * @param string
	 *            the text to split.
	 * @return the array of lines.
	 */
	public static String[] getLines(String string) {
		return splitString(string, NEW_LINE);
	}

	/**
	 * Gets the words in a text.
	 * <p>
	 * A new word is started after a space ' '.
	 *
	 * @param string
	 *            the text.
	 * @return the array of words.
	 */
	public static String[] getWords(String string) {
		return splitString(string, SPACE);
	}

	private static String[] splitString(String string, char separator) {
		if (string.isEmpty()) {
			return new String[0];
		}
		List<String> elements = new ArrayList<>(1);
		int newSeparatorIndex = 0;

		do {
			int separatorIndex = string.indexOf(separator, newSeparatorIndex);

			if (separatorIndex == -1) {
				// End of string reached.
				elements.add(string.substring(newSeparatorIndex));
				break;
			} else {
				elements.add(string.substring(newSeparatorIndex, separatorIndex));
				newSeparatorIndex = separatorIndex + 1;
			}

		} while (true);

		return elements.toArray(new String[elements.size()]);
	}

}
