package eu.dnetlib.validator2.engine;

import java.util.HashSet;
import java.util.Objects;
import java.util.Set;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import java.util.stream.Stream;

public class Predicates {

    // The following are not used
    /*
    public static <T> Predicate<T> and(final Iterable<Predicate<T>> predicates) {
        return t -> {
            for(Predicate<T> predicate: predicates) {
                if (!predicate.test(t)) return false;
            }
            return true;
        };
    }

    public static <T> Predicate<T> or(final Iterable<Predicate<T>> predicates) {
        return t -> {
            for(Predicate<T> predicate: predicates) {
                if (predicate.test(t)) return true;
            }
            return false;
        };
    }

    // horn?
    public static <T> Predicate<T> conditional(Predicate<T> condition, final Iterable<Predicate<T>> predicates) {
        return t -> {
            if (!condition.test(t)) return true;
            return and(predicates).test(t);
        };
    }
    */

    public static class SetOfCaseInsensitiveAllowedValues implements Predicate<String> {

        private final Set<String> allowedValues;

        public SetOfCaseInsensitiveAllowedValues(String[] allowedValues) {
            this.allowedValues = Stream.of(allowedValues).
                    filter(Objects::nonNull).
                    map(String::trim).
                    filter(term -> !term.isEmpty()).
                    map(String::toLowerCase).   // we want to ignore case for equality
                    collect(Collectors.toCollection(HashSet::new));;
        }

        @Override
        public boolean test(String s) {
            String textToTest = Helper.canonicalize(s);
            if (textToTest.isEmpty()) return false;
            return allowedValues.contains(textToTest.toLowerCase()); // we want to ignore case for equality
        }

        public boolean isEmpty() {
            return allowedValues.isEmpty();
        }
    }

    public static <T> Predicate<T> all(Iterable<Rule<T>> rules) {
        return t -> {
            for(Predicate<T> predicate: rules) {
                if (!predicate.test(t)) return false;
            }
            return true;
        };
    }

    public static <T> Predicate<T> any(final Iterable<Rule<T>> rules) {
        return t -> {
            for(Predicate<T> predicate: rules) {
                if (predicate.test(t)) return true;
            }
            return false;
        };
    }

    public static <T> Predicate<T> not(Rule<T> rule) {
        return t -> !rule.test(t);
    }
}
