package me.xdrop.ensurej.checks;

import me.xdrop.ensurej.Chain;
import me.xdrop.ensurej.Predicate;

import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class CheckString {

    public static Predicate<String> isAllUppercase() {
        return isAllUppercase(true);
    }

    public static Predicate<String> isAllUppercase(final boolean allowNonChar) {
        return new Predicate<String>() {
            @Override
            public boolean eval(String s) {
                for (int i = 0; i < s.length(); i++) {
                    char ch = s.charAt(i);
                    if (Character.isLowerCase(ch) || (!allowNonChar && !Character.isUpperCase(ch))) {
                        return false;
                    }
                }

                return true;

            }
        };
    }

    public static Predicate<String> isAllLowercase() {
        return isAllLowercase(true);
    }

    public static Predicate<String> isAllLowercase(final boolean allowNonChar){
        return new Predicate<String>() {
            @Override
            public boolean eval(String s) {

                for (int i = 0; i < s.length(); i++) {
                    char ch = s.charAt(i);
                    if ((Character.isUpperCase(ch)) || !allowNonChar && !Character.isLowerCase(ch)) {
                        return false;
                    }
                }
                return true;
            }
        };
    }

    public static Predicate<String> matches(final String pattern){
        return new Predicate<String>() {
            @Override
            public boolean eval(String s) {
                Pattern p = Pattern.compile(pattern);
                Matcher matcher = p.matcher(s);
                return matcher.matches();
            }
        };
    }

    public static Predicate<String> hasOnlyLetters(final boolean unicode){
        return new Predicate<String>() {
            @Override
            public boolean eval(String s) {
                if (unicode) {
                    for (int i = 0; i < s.length(); i++) {
                        char ch = s.charAt(i);
                        if (!Character.isLetter(ch)) {
                            return false;
                        }
                    }
                    return true;
                } else {
                    return matches("^[a-zA-Z]+$").eval(s);
                }

            }
        };
    }

    public static Predicate<String> hasOnlyDigits(){
        return new Predicate<String>() {
            @Override
            public boolean eval(String s) {

                for (int i = 0; i < s.length(); i++) {
                    char ch = s.charAt(i);
                    if (!Character.isDigit(ch)) {
                        return false;
                    }
                }
                return true;

            }
        };
    }

    public static Predicate<String> isNotEmpty(){
        return new Predicate<String>() {
            @Override
            public boolean eval(String s) {
                return s != null && matches("[^\\s+]").eval(s);
            }
        };
    }

    public static Predicate<String> isEmpty(){
        return new Predicate<String>() {
            @Override
            public boolean eval(String s) {
                return !isNotEmpty().eval(s);
            }
        };
    }

    public static Predicate<String> isNotNull(){
        return new Predicate<String>() {
            @Override
            public boolean eval(String s) {
                return s != null;
            }
        };
    }

    public static Predicate<String> hasWhitespaceStart(){
        return new Predicate<String>() {
            @Override
            public boolean eval(String s) {
                return matches("\\s+.*").eval(s);
            }
        };
    }

    public static Predicate<String> hasWhitespaceEnd(){
        return new Predicate<String>() {
            @Override
            public boolean eval(String s) {
                return matches(".*\\s").eval(s);
            }
        };
    }

    public static Predicate<String> isTrimmed(){
        return new Predicate<String>() {
            @Override
            public boolean eval(String s) {
                return !hasWhitespaceStart().eval(s) && !hasWhitespaceEnd().eval(s);
            }
        };
    }

    public static Predicate<String> lengthBetween(final int lower, final int upper){

        return new Predicate<String>() {
            @Override
            public boolean eval(String in) {
                int length = in.length();
                return length >= lower && length < upper;
            }
        };

    }

    public static Predicate<String> allLetters(final Character c){
        return new Predicate<String>() {
            @Override
            public boolean eval(String in) {
                for (int i = 0; i < in.length(); i++){
                    if(in.charAt(i) != c){
                        return false;
                    }
                }
                return true;
            }
        };
    }

    public static Predicate<String> allEqual() {
        return new Predicate<String>() {
            @Override
            public boolean eval(String in) {
                return allLetters(in.charAt(0)).eval(in);
            }
        };
    }

    public static Predicate<String> isInteger(){
        return new Predicate<String>() {
            @Override
            public boolean eval(String in) {
                try {
                    Integer.parseInt(in);
                    return true;
                } catch (NumberFormatException e){
                    return false;
                }
            }
        };
    }

    public static Predicate<String> isDecimal(){
        return new Predicate<String>() {
            @Override
            public boolean eval(String in) {
                try {
                    Double.parseDouble(in);
                    Float.parseFloat(in);

                    try {
                        Integer.parseInt(in);
                        return false;
                    } catch (NumberFormatException e){
                        return true;
                    }
                } catch (NumberFormatException e){
                    return false;
                }
            }
        };
    }

    public static Predicate<String> isNumber() {
        return new Predicate<String>() {
            @Override
            public boolean eval(String in) {
                return isInteger().eval(in) || isDecimal().eval(in);
            }
        };
    }

    public static Predicate<String> fromSet(final String ... set){
        return new Predicate<String>() {
            @Override
            public boolean eval(String in) {
                for (String s : set){
                    if (s.equals(in)){
                        return true;
                    }
                }
                return false;
            }
        };
    }

    public static Predicate<String> containsTimes(final char character, final int times){
        return new Predicate<String>() {
            @Override
            public boolean eval(String in) {
                int seenSoFar = times;
                for (char c : in.toCharArray()){
                    if (c == character) seenSoFar--;

                    if (seenSoFar == 0){
                        return true;
                    }
                }

                return false;
            }
        };
    }

    public static Predicate<String> beginsWith(final String prefix){
        return new Predicate<String>() {
            @Override
            public boolean eval(String in) {
                return in.startsWith(prefix);
            }
        };
    }

    public static Predicate<String> endsWith(final String suffix){
        return new Predicate<String>() {
            @Override
            public boolean eval(String in) {
                return in.endsWith(suffix);
            }
        };
    }

    public static Predicate<String> nLetters(final int lower, final int upper){
        return new Predicate<String>() {
            @Override
            public boolean eval(String in) {
                int count = 0;
                for (Character c : in.toCharArray()){
                    if (Character.isLetter(c)){
                        count++;
                    }
                }
                return CheckInt.inRange(lower, upper).eval(count);
            }
        };
    }
}

@@ -8510,11 +8510,511 @@
 %7D;%0A    %7D
+%0A%0A    public static Predicate%3CString%3E nNumbers(final int lower, final int upper)%7B%0A        return new Predicate%3CString%3E() %7B%0A            @Override%0A            public boolean eval(String in) %7B%0A                int count = 0;%0A                for (Character c : in.toCharArray())%7B%0A                    if (c.toString().matches(%22%5C%5Cd+%22))%7B%0A                        count++;%0A                    %7D%0A                %7D%0A                return CheckInt.inRange(lower, upper).eval(count);%0A            %7D%0A        %7D;%0A    %7D
 %0A%7D%0A

Add number of numbers check