import java.util.*;
import java.util.stream.*;

/**
 * IT6003 - Advanced Java Programming
 * Practice Exercises: Stream API
 *
 * Instructions:
 *   - Implement each method where you see the // TODO comment.
 *   - Do NOT change the method signatures.
 *   - Run main() to test your implementations.
 */
public class ExercisesStreams {

    // ──────────────────────────────────────────────
    // Helper class used by Exercise 5
    // ──────────────────────────────────────────────
    static class Student {
        String name;
        String department;
        double gpa;

        Student(String name, String department, double gpa) {
            this.name = name;
            this.department = department;
            this.gpa = gpa;
        }

        @Override
        public String toString() {
            return name + " (" + department + ")";
        }
    }

    // ──────────────────────────────────────────────
    // Exercise 1: Filter Names Starting with 'A'
    // ──────────────────────────────────────────────
    /**
     * Filter the list of names, keeping only those that start with 'A'
     * (case-sensitive).
     *
     * @param names list of names
     * @return a new list containing only names starting with 'A'
     */
    public static List<String> filterByA(List<String> names) {
        // TODO: Use stream().filter() to keep names starting with "A".
        //       Collect the results into a list.
        return null;
    }

    // ──────────────────────────────────────────────
    // Exercise 2: Convert to Uppercase
    // ──────────────────────────────────────────────
    /**
     * Convert every string in the list to uppercase using the Stream map()
     * operation.
     *
     * @param strings the input list
     * @return a new list with all strings in uppercase
     */
    public static List<String> toUpperCase(List<String> strings) {
        // TODO: Use stream().map(String::toUpperCase) and collect to a list.
        return null;
    }

    // ──────────────────────────────────────────────
    // Exercise 3: Sum of Squares of Even Numbers
    // ──────────────────────────────────────────────
    /**
     * Given a list of integers, compute the sum of squares of the even numbers.
     * Use filter(), map(), and reduce() (or mapToInt + sum).
     *
     * Example: [1,2,3,4,5] -> 2^2 + 4^2 = 4 + 16 = 20
     *
     * @param numbers the input list
     * @return sum of squares of even numbers
     */
    public static int sumOfSquaresOfEvens(List<Integer> numbers) {
        // TODO: Use stream(), filter for even, map to square, and reduce/sum.
        return 0;
    }

    // ──────────────────────────────────────────────
    // Exercise 4: Find Longest String
    // ──────────────────────────────────────────────
    /**
     * Find the longest string in the list. If the list is empty, return "".
     * Use stream().max() with an appropriate Comparator.
     *
     * @param strings the input list
     * @return the longest string, or "" if the list is empty
     */
    public static String findLongest(List<String> strings) {
        // TODO: Use stream().max(Comparator.comparingInt(String::length))
        //       and return the result (or "" if empty).
        return null;
    }

    // ──────────────────────────────────────────────
    // Exercise 5: Group Students by Department
    // ──────────────────────────────────────────────
    /**
     * Group the list of students by their department.
     * Use Collectors.groupingBy().
     *
     * @param students list of students
     * @return a Map from department name to list of students
     */
    public static Map<String, List<Student>> groupByDepartment(List<Student> students) {
        // TODO: Use stream().collect(Collectors.groupingBy(s -> s.department)).
        return null;
    }

    // ──────────────────────────────────────────────
    // Exercise 6: Partition Numbers into Even / Odd
    // ──────────────────────────────────────────────
    /**
     * Partition the list of integers into even and odd groups.
     * Use Collectors.partitioningBy().
     *
     * @param numbers the input list
     * @return a Map: true -> even numbers, false -> odd numbers
     */
    public static Map<Boolean, List<Integer>> partitionEvenOdd(List<Integer> numbers) {
        // TODO: Use stream().collect(Collectors.partitioningBy(n -> n % 2 == 0)).
        return null;
    }

    // ──────────────────────────────────────────────
    // Exercise 7: Join Strings with Comma
    // ──────────────────────────────────────────────
    /**
     * Join all strings in the list with ", " as a separator.
     * Use Collectors.joining().
     *
     * @param strings the input list
     * @return a single string with elements separated by ", "
     */
    public static String joinWithComma(List<String> strings) {
        // TODO: Use stream().collect(Collectors.joining(", ")).
        return null;
    }

    // ──────────────────────────────────────────────
    // Exercise 8: Average of Doubles
    // ──────────────────────────────────────────────
    /**
     * Compute the average of a list of doubles.
     * Use Collectors.averagingDouble().
     *
     * @param numbers list of doubles
     * @return the average value
     */
    public static double average(List<Double> numbers) {
        // TODO: Use stream().collect(Collectors.averagingDouble(d -> d)).
        return 0.0;
    }

    // ──────────────────────────────────────────────
    // Exercise 9: Word Occurrence Count (Streams)
    // ──────────────────────────────────────────────
    /**
     * Count the occurrences of each word in the given sentence.
     * Convert words to lowercase.
     * Use stream with Collectors.groupingBy() and Collectors.counting().
     *
     * @param sentence a string of words separated by spaces
     * @return a map of word -> count
     */
    public static Map<String, Long> wordCount(String sentence) {
        // TODO: Split the sentence into words, create a stream,
        //       map to lowercase, and collect using
        //       Collectors.groupingBy(w -> w, Collectors.counting()).
        return null;
    }

    // ──────────────────────────────────────────────
    // Exercise 10: Flatten List of Lists (flatMap)
    // ──────────────────────────────────────────────
    /**
     * Given a list of lists of integers, flatten them into a single list.
     * Use flatMap().
     *
     * @param listOfLists a list containing inner lists
     * @return a single flat list of all elements
     */
    public static List<Integer> flatten(List<List<Integer>> listOfLists) {
        // TODO: Use stream().flatMap(Collection::stream).collect(Collectors.toList()).
        return null;
    }

    // ──────────────────────────────────────────────
    // Main – test your implementations here
    // ──────────────────────────────────────────────
    public static void main(String[] args) {

        // Sample data
        List<String> names = Arrays.asList("Ali", "Ahmed", "Fatma", "Amina", "Hassan", "Aisha");
        List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);

        System.out.println("=== Exercise 1: Filter Names Starting with 'A' ===");
        System.out.println("Names with 'A': " + filterByA(names));
        // Expected: [Ali, Ahmed, Amina, Aisha]

        System.out.println("\n=== Exercise 2: Convert to Uppercase ===");
        List<String> fruits = Arrays.asList("apple", "banana", "cherry");
        System.out.println("Uppercase: " + toUpperCase(fruits));
        // Expected: [APPLE, BANANA, CHERRY]

        System.out.println("\n=== Exercise 3: Sum of Squares of Evens ===");
        System.out.println("Sum of squares of evens in " + numbers + ": " + sumOfSquaresOfEvens(numbers));
        // Expected: 4+16+36+64+100 = 220

        System.out.println("\n=== Exercise 4: Longest String ===");
        List<String> words = Arrays.asList("cat", "elephant", "dog", "butterfly", "ant");
        System.out.println("Longest: " + findLongest(words));
        // Expected: butterfly

        System.out.println("\n=== Exercise 5: Group Students by Department ===");
        List<Student> students = Arrays.asList(
            new Student("Ali", "CS", 3.5),
            new Student("Fatma", "CS", 3.8),
            new Student("Hassan", "IT", 3.2),
            new Student("Zainab", "IT", 3.6),
            new Student("Amina", "CS", 3.1),
            new Student("Omar", "IS", 3.4)
        );
        Map<String, List<Student>> grouped = groupByDepartment(students);
        grouped.forEach((dept, studs) -> System.out.println("  " + dept + ": " + studs));
        // Expected: CS: [Ali, Fatma, Amina], IT: [Hassan, Zainab], IS: [Omar]

        System.out.println("\n=== Exercise 6: Partition Even / Odd ===");
        Map<Boolean, List<Integer>> partitioned = partitionEvenOdd(numbers);
        System.out.println("Even: " + partitioned.get(true));
        System.out.println("Odd:  " + partitioned.get(false));
        // Expected: Even: [2,4,6,8,10], Odd: [1,3,5,7,9]

        System.out.println("\n=== Exercise 7: Join with Comma ===");
        System.out.println("Joined: " + joinWithComma(names));
        // Expected: Ali, Ahmed, Fatma, Amina, Hassan, Aisha

        System.out.println("\n=== Exercise 8: Average ===");
        List<Double> grades = Arrays.asList(85.5, 92.0, 78.5, 90.0, 88.0);
        System.out.println("Average: " + average(grades));
        // Expected: 86.8

        System.out.println("\n=== Exercise 9: Word Count (Streams) ===");
        String sentence = "the cat sat on the mat the cat";
        Map<String, Long> counts = wordCount(sentence);
        System.out.println("Word counts: " + counts);
        // Expected: {the=3, cat=2, sat=1, on=1, mat=1}

        System.out.println("\n=== Exercise 10: Flatten Lists ===");
        List<List<Integer>> nested = Arrays.asList(
            Arrays.asList(1, 2, 3),
            Arrays.asList(4, 5),
            Arrays.asList(6, 7, 8, 9)
        );
        System.out.println("Flattened: " + flatten(nested));
        // Expected: [1, 2, 3, 4, 5, 6, 7, 8, 9]
    }
}
