package tars.commons.util;

import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.Arrays;
import java.util.List;

import tars.commons.exceptions.IllegalValueException;
import tars.commons.exceptions.InvalidRangeException;

/**
 * Helper functions for handling strings.
 */
public class StringUtil {

    private static final String INVALID_INDEX_ENTERED = "Invalid index entered";
    private static final String UNEXPECTED_ERROR_IN_GETING_INDEX_FROM_STRING =
            "Unexpected error in geting index from String.";

    public static final String STRING_NEWLINE = "\n";
    public static final String STRING_COLON = ": ";
    public static final String STRING_COMMA = ", ";
    public static final String STRING_FULLSTOP = ". ";
    public static final String STRING_FORWARD_SLASH = "/";
    public static final String STRING_SQUARE_BRACKET_OPEN = "[";
    public static final String STRING_SQUARE_BRACKET_CLOSE = "]";
    public static final String EMPTY_STRING = "";
    public static final String STRING_WHITESPACE = " ";
    public static final int EMPTY_STRING_LENGTH = 0;
    public static final int START_INDEX = 0;
    public static final int LAST_INDEX = 1;
    public static final int INVALID_POSITION = -1;

    private static final String RANGE_SEPARATOR = "..";

    public static boolean containsIgnoreCase(String source, String query) {
        String[] split = source.toLowerCase().split("\\s+");
        List<String> strings = Arrays.asList(split);
        return strings.stream().filter(s -> s.equals(query.toLowerCase()))
                .count() > 0;
    }

    /**
     * Returns a detailed message of the t, including the stack trace.
     */
    public static String getDetails(Throwable t) {
        StringWriter sw = new StringWriter();
        t.printStackTrace(new PrintWriter(sw));
        return t.getMessage() + STRING_NEWLINE + sw.toString();
    }

    /**
     * Returns true if s represents an unsigned integer e.g. 1, 2, 3, ... <br>
     * Will return false for null, empty string, "-1", "0", "+1", and " 2 " (untrimmed) "3 0"
     * (contains whitespace).
     * 
     * @param s should be trimmed.
     */
    public static boolean isUnsignedInteger(String s) {
        return s != null && s.matches("^0*[1-9]\\d*$");
    }

    /**
     * Handles three different cases of strings and return them in the appropriate format
     * 
     * @@author A0121533W
     */
    public static String indexString(String s)
            throws InvalidRangeException, IllegalValueException {
        if (s.isEmpty()) {
            return s;
        }
        if (isSingleNumber(s)) {
            return formatSingleNumber(s);
        } else if (isListOfIndexes(s)) {
            return formatListOfIndexes(s);
        } else if (isRangeOfIndexes(s)) {
            return formateRangeOfIndexes(s);
        } else {
            throw new IllegalValueException(
                    UNEXPECTED_ERROR_IN_GETING_INDEX_FROM_STRING);
        }
    }

    private static boolean isSingleNumber(String s) {
        return (s.indexOf(STRING_WHITESPACE) == INVALID_POSITION
                && !s.contains(RANGE_SEPARATOR));
    }

    private static String formatSingleNumber(String s)
            throws IllegalValueException {
        if (!isUnsignedInteger(s)) {
            throw new IllegalValueException(INVALID_INDEX_ENTERED);
        }
        return s;
    }

    private static boolean isListOfIndexes(String s) {
        return (s.indexOf(STRING_WHITESPACE) != INVALID_POSITION
                && !s.contains(RANGE_SEPARATOR));
    }

    /**
     * Formats a list of indexes to
     * 
     * @@author A0121533W
     */
    private static String formatListOfIndexes(String s)
            throws IllegalValueException {
        String indexString = EMPTY_STRING;
        String[] indexArray = s.split(STRING_WHITESPACE);
        for (int i = START_INDEX; i < indexArray.length; i++) {
            String index = formatSingleNumber(indexArray[i]);
            indexString += index + STRING_WHITESPACE;
        }
        return indexString.trim();
    }

    private static boolean isRangeOfIndexes(String s) {
        return s.contains(RANGE_SEPARATOR);
    }

    /**
     * Formats a range of indexes to a list of indexes separated by white space
     * 
     * @@author A0121533W
     */
    private static String formateRangeOfIndexes(String s)
            throws IllegalValueException, InvalidRangeException {
        String rangeToReturn = EMPTY_STRING;

        int toIndex = s.indexOf(RANGE_SEPARATOR);
        String start = s.substring(START_INDEX, toIndex);
        String end = s.substring(toIndex + RANGE_SEPARATOR.length());

        start = formatSingleNumber(start);
        end = formatSingleNumber(end);

        int startInt = Integer.parseInt(start);
        int endInt = Integer.parseInt(end);

        if (startInt > endInt) {
            throw new InvalidRangeException();
        }

        for (int i = startInt; i <= endInt; i++) {
            rangeToReturn += String.valueOf(i) + STRING_WHITESPACE;
        }

        return rangeToReturn.trim();
    }

}

@@ -2645,22 +2645,27 @@
  return 
-format
+getIndexFor
 SingleNu
@@ -2733,35 +2733,33 @@
  return 
-formatListOfIndexes
+getIndexesForList
 (s);%0A   
@@ -2816,37 +2816,34 @@
  return 
-formateRangeOfIndexes
+getIndexesForRange
 (s);%0A   
@@ -3153,32 +3153,84 @@
 RATOR));%0A    %7D%0A%0A
+    /**%0A     * Returns a valid single index%0A     */%0A
     private stat
@@ -3239,22 +3239,27 @@
  String 
-format
+getIndexFor
 SingleNu
@@ -3648,25 +3648,31 @@
 %0A     * 
-Formats a
+Returns a valid
  list of
@@ -3679,19 +3679,16 @@
  indexes
- to
 %0A     * 
@@ -3752,27 +3752,25 @@
 ing 
-formatListOfIndexes
+getIndexesForList
 (Str
@@ -4011,22 +4011,27 @@
 index = 
-format
+getIndexFor
 SingleNu
@@ -4283,37 +4283,15 @@
   * 
-Formats a range of indexes to
+Returns
  a l
@@ -4329,16 +4329,40 @@
 te space
+ from a range of indexes
 %0A     * 
@@ -4426,29 +4426,26 @@
 ing 
-formateRangeOfIndexes
+getIndexesForRange
 (Str
@@ -4758,22 +4758,27 @@
 start = 
-format
+getIndexFor
 SingleNu
@@ -4804,22 +4804,27 @@
   end = 
-format
+getIndexFor
 SingleNu

Update method header