/*
 * @author Ranides Atterwim {@literal <ranides@gmail.com>}
 * @copyright Ranides Atterwim
 * @license WTFPL
 * @url http://ranides.net/projects/assira
 */
package net.ranides.assira.functional.special;

import net.ranides.assira.text.StringUtils;

import java.util.Arrays;
import java.util.function.Predicate;
import java.util.regex.Pattern;

/**
 * Specialization of Predicate for characters.
 * It avoids uneccessary boxing and unboxing
 *
 * @author msieron
 */
public interface CharPredicate extends Predicate<Character> {

    /**
     * non-generic version of method {@link #test(Character)}
     *
     * @param c c
     * @return bool
     */
    boolean test(char c);
    
    @Override
    default boolean test(Character c) {
        return c!=null && test(c.charValue());
    }

    @Override
    default CharPredicate and(Predicate<? super Character> other) {
        return c -> test(c) && other.test(c);
    }

    /**
     * non-generic version of method {@link #and(Predicate)}
     *
     * @param other other
     * @return predicate
     */
    default CharPredicate and(CharPredicate other) {
        return c -> test(c) && other.test(c);
    }

    @Override
    default CharPredicate negate() {
        return c -> !test(c);
    }

    @Override
    default Predicate<Character> or(Predicate<? super Character> other) {
        return c -> test(c) || other.test(c);
    }

    /**
     * non-generic version of method {@link #or(Predicate)}
     *
     * @param other other
     * @return predicate
     */
    default CharPredicate or(CharPredicate other) {
        return c -> test(c) || other.test(c);
    }

    /**
     * Returns predicate accepting only specified character
     *
     * @param ch ch
     * @return predicate
     */
    static CharPredicate of(char ch) {
        return c -> c == ch;
    }

    /**
     * Returns predicate accepting only specified characters
     *
     * @param chars chars
     * @return predicate
     */
    static CharPredicate of(char... chars) {
        char[] sorted = chars.clone();
        Arrays.sort(sorted);
        return c -> Arrays.binarySearch(sorted, c) >= 0;
    }

    /**
     * Returns predicate accepting characters contained by specified string
     *
     * @param text text
     * @return predicate
     */
    static CharPredicate of(String text) {
        return of(text.toCharArray());
    }

    /**
     * Returns predicate accepting characters matched by provided pattern
     *
     * @param pattern pattern
     * @return predicate
     */
    static CharPredicate of(Pattern pattern) {
        return c -> pattern.matcher(StringUtils.wrap(new char[]{c})).matches();

    }

}
