import java.util.ArrayList;
import java.util.List;
import java.util.function.BinaryOperator;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.function.Supplier;
import java.util.function.UnaryOperator;

/** Las interfaces funcionales de java.util.function y las referencias a métodos. */
public class FunctionsDemo {

    /** Una interfaz funcional propia: un solo método abstracto. */
    @FunctionalInterface
    interface Discount {
        int apply(int cents);
    }

    public static void main(String[] args) {
        Predicate<Song> isLong = s -> s.seconds() > 200;
        Function<Song, String> label = s -> s.artist() + " - " + s.title();
        Function<String, Integer> length = String::length;          // método de instancia, objeto por parámetro
        Consumer<String> show = IO::println;                         // método estático
        Supplier<List<Song>> newList = ArrayList::new;               // constructor
        UnaryOperator<String> shout = String::toUpperCase;
        BinaryOperator<Integer> add = Integer::sum;

        Song garua = Songs.sample().get(0);
        show.accept("isLong(Garúa) = " + isLong.test(garua));
        show.accept("negate        = " + isLong.negate().test(garua));
        show.accept("label         = " + label.apply(garua));
        show.accept("andThen       = " + label.andThen(length).apply(garua));
        show.accept("shout         = " + shout.apply("garúa"));
        show.accept("add(185, 210) = " + add.apply(185, 210));

        List<Song> longOnes = newList.get();
        for (Song s : Songs.sample()) {
            if (isLong.test(s)) {
                longOnes.add(s);
            }
        }
        show.accept("largas        = " + longOnes.size());

        Discount tenPercent = cents -> cents - cents / 10;
        Discount minusFive = cents -> Math.max(0, cents - 500);
        show.accept("descuentos    = " + tenPercent.apply(2000) + " " + minusFive.apply(2000));
    }
}
