
import java.util.Scanner;
import java.util.List;
import java.util.ArrayList;
import java.util.stream.Collectors;

/**
 * A program to demonstrate the basics of Streams.
 *
 * @author Mark Young (A00000000)
 */
public class Streams {

    public static void main(String[] args) {
        // create some lists
        List<String> words = makeWordList();
        List<Integer> numbers = makeNumberList();
        List<String> fWords, uppers;
        List<Integer> smallEven, squares;

        // show them
        System.out.println("Here are a couple of Lists:");
        System.out.println("\t" + words);
        System.out.println("\t" + numbers);
        pause();
        
        // stream.forEach
        System.out.println("words, printed using stream().forEach:");
        words.stream().forEach(t -> System.out.println("\t" + t));
        pause();

        // make a list of the F words
        System.out.println("Making a List of the words that start with F:");
        fWords = words.stream()
                    .filter(t -> t.toUpperCase().startsWith("F"))
//                    .toList(); // Available starting in Java 16
                    .collect(Collectors.toList());
        System.out.println("\t" + fWords);
        pause();

        // make a list of the small, even numbers
        System.out.println("Making a List of the small, even numbers:");
        smallEven = numbers.stream()
//                    .peek(e -> System.out.println("Original: " + e))
                    .filter(t -> t < 20)
//                    .peek(e -> System.out.println(" - Small: " + e))
                    .filter(t -> t % 2 == 0)
//                    .peek(e -> System.out.println(" -- Even: " + e))
                    .collect(Collectors.toList());
        System.out.println("\t" + smallEven);
        pause();

        // print list out in special format
        System.out.println("A fancy way of writing the f-words: "
                + fWords.stream()
                    .collect(Collectors.joining("-", "==", "==")));
        pause();

        // mapping numbers to their squares
        System.out.println("Making a list of the squares:");
        squares = numbers.stream()
                    .map(n -> n * n)
                    .collect(Collectors.toList());
        System.out.println("\toriginals: " + numbers);
        System.out.println("\tsquares:   " + squares);
        pause();

        // mapping strings to upper case
        System.out.println("Making a list of upper-case words:");
        uppers = words.stream()
                    .map(s -> s.toUpperCase())
                    .collect(Collectors.toList());
        System.out.println("\toriginals: " + words);
        System.out.println("\tupperCase: " + uppers);
        pause();

        // summing numbers
        System.out.println("Summing number lists:");
        System.out.println("\toriginal:     " 
                + numbers.stream().reduce(0, (a, b) -> a + b));
        System.out.println("\tsmall, evens: " 
                + smallEven.stream().reduce(0, (a, b) -> a + b));
        System.out.println("\tsquares:      " 
                + squares.stream().reduce(0, (a, b) -> a + b));
        pause();

        // using named methods
        System.out.println("Printing the fWords using System.out::println:");
        fWords.stream().forEach(System.out::println);
        pause();

        // capitalizing using named method
        System.out.println("Capitalizing using String::toUpperCase: "
                + fWords.stream()
                        .map(String::toUpperCase)
                        .collect(Collectors.toList()));
        pause();

        // summing lists using named methods
        System.out.println("Summing list using Integer::sum:");
        System.out.println("\toriginal:     " 
                + numbers.stream().reduce(0, Integer::sum));
        System.out.println("\tsmall, evens: " 
                + smallEven.stream().reduce(0, Integer::sum));
        System.out.println("\tsquares:      " 
                + squares.stream().reduce(0, Integer::sum));
        pause();

        // show them again
        System.out.println("The lists themselves are unchanged:");
        System.out.println("\t" + words);
        System.out.println("\t" + numbers);
        pause();
    }

    /**
     * Create a list of words.
     *
     * @return a list of words.
     */
    private static List<String> makeWordList() {
        List<String> result = new ArrayList<>();

        result.add("Ten");
        result.add("Fifteen");
        result.add("Twenty");
        result.add("Thirty");
        result.add("Forty");
        result.add("Fifty");
        result.add("Sixty");

        return result;
    }
    
    /**
     * Create a list of numbers.
     *
     * @return a list of numbers.
     */
    private static List<Integer> makeNumberList() {
        List<Integer> result = new ArrayList<>();

        result.add(5);
        result.add(10);
        result.add(17);
        result.add(18);
        result.add(20);
        result.add(22);
        result.add(67);

        return result;
    }
    
    private static final Scanner kbd = new Scanner(System.in);

    /**
     * Prompt the user and wait for them to press enter.
     */
    private static void pause() {
        System.out.println();
        System.out.print("...press enter...");
        kbd.nextLine();
        System.out.println();
    }
    
}
