package net.knarcraft.paidsigns.utility;

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

/**
 * A tokenizer for being able to support quotes in commands
 */
public final class Tokenizer {

    private Tokenizer() {

    }

    /**
     * Tokenizes a string
     *
     * @param input <p>A string</p>
     * @return <p>A list of tokens</p>
     */
    public static List<String> tokenize(String input) {
        return tokenize(input, true);
    }

    /**
     * Tokenizes a string
     *
     * @param input            <p>A string</p>
     * @param allowEmptyQuotes <p>Whether to treat "" as a token</p>
     * @return <p>A list of tokens</p>
     */
    public static List<String> tokenize(String input, boolean allowEmptyQuotes) {
        List<String> tokens = new ArrayList<>();
        boolean startedQuote = false;
        StringBuilder currentToken = new StringBuilder();
        for (int index = 0; index < input.length(); index++) {
            char character = input.charAt(index);
            switch (character) {
                case ' ':
                    if (tokenizeSpace(startedQuote, currentToken, tokens)) {
                        currentToken = new StringBuilder();
                    }
                    break;
                case '"':
                    if (startedQuote) {
                        //This quote signifies the end of the argument
                        if (allowEmptyQuotes || isNotEmpty(currentToken)) {
                            tokens.add(currentToken.toString());
                            currentToken = new StringBuilder();
                        }
                        startedQuote = false;
                    } else {
                        //This quote signifies the start of the argument
                        startedQuote = true;
                        currentToken = new StringBuilder();
                    }
                    break;
                default:
                    tokenizeNormalCharacter(currentToken, character, input.length(), index, tokens);
                    break;
            }
        }
        return tokens;
    }

    /**
     * Adds a normal character to the token. Adds the current token to tokens if at the end of the input
     *
     * @param currentToken <p>The string builder containing the current token.</p>
     * @param character    <p>The character found in the input.</p>
     * @param inputLength  <p>The length of the given input</p>
     * @param index        <p>The index of the read character.</p>
     * @param tokens       <p>The list of processed tokens.</p>
     */
    private static void tokenizeNormalCharacter(StringBuilder currentToken, char character, int inputLength, int index,
                                                List<String> tokens) {
        currentToken.append(character);
        if (index == inputLength - 1) {
            tokens.add(currentToken.toString());
        }
    }

    /**
     * Tokenizes a space character
     *
     * @param startedQuote <p>Whether this space is inside a pair of quotes.</p>
     * @param currentToken <p>The string builder containing the current token.</p>
     * @param tokens       <p>The list of processed tokens.</p>
     * @return <p>True if the token is finished.</p>
     */
    private static boolean tokenizeSpace(boolean startedQuote, StringBuilder currentToken, List<String> tokens) {
        if (!startedQuote) {
            //If not inside "", a space marks the end of a parameter
            if (isNotEmpty(currentToken)) {
                tokens.add(currentToken.toString());
            }
            return true;
        } else {
            currentToken.append(' ');
            return false;
        }
    }

    /**
     * Checks whether a string builder is empty
     *
     * @param builder <p>The string builder to check.</p>
     * @return <p>True if the string builder is non empty.</p>
     */
    private static boolean isNotEmpty(StringBuilder builder) {
        return !builder.toString().trim().equals("");
    }

}