import java.util.*;
import java.util.function.*;

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

    // ──────────────────────────────────────────────
    // Exercise 1: Generic Swap
    // ──────────────────────────────────────────────
    /**
     * Swap the elements at positions i and j in the given array.
     *
     * @param arr the array
     * @param i   first index
     * @param j   second index
     */
    public static <T> void swap(T[] arr, int i, int j) {
        // TODO: Swap arr[i] and arr[j].
    }

    // ──────────────────────────────────────────────
    // Exercise 2: Generic Triple Class
    // ──────────────────────────────────────────────
    /**
     * A generic class that holds three values of potentially different types.
     * Implement getFirst(), getSecond(), getThird(), and toString().
     */
    static class Triple<A, B, C> {
        // TODO: Declare private fields for the three values.

        // TODO: Write a constructor Triple(A first, B second, C third).

        // TODO: Implement getFirst(), getSecond(), getThird().

        // TODO: Override toString() to return "(first, second, third)".

        // Placeholder so the file compiles – remove or replace when implementing.
        Triple(A first, B second, C third) {}
        public A getFirst()  { return null; }
        public B getSecond() { return null; }
        public C getThird()  { return null; }
        @Override public String toString() { return ""; }
    }

    // ──────────────────────────────────────────────
    // Exercise 3: Generic findMax
    // ──────────────────────────────────────────────
    /**
     * Find and return the maximum element in the list using natural ordering.
     *
     * @param list a non-empty list of Comparable elements
     * @return the maximum element
     */
    public static <T extends Comparable<T>> T findMax(List<T> list) {
        // TODO: Iterate through the list and find the maximum element.
        //       Use compareTo() for comparison.
        return null;
    }

    // ──────────────────────────────────────────────
    // Exercise 4: Lambda Comparator – Sort by Length
    // ──────────────────────────────────────────────
    /**
     * Return a Comparator<String> (as a lambda) that compares strings
     * by their length (shorter strings first).
     *
     * @return comparator that sorts strings by length ascending
     */
    public static Comparator<String> lengthComparator() {
        // TODO: Return a lambda expression that compares two strings
        //       based on their length.
        return null;
    }

    // ──────────────────────────────────────────────
    // Exercise 5: Predicate – Filter Positives
    // ──────────────────────────────────────────────
    /**
     * Filter the given list of integers, keeping only positive numbers.
     * Use a Predicate<Integer>.
     *
     * @param numbers the input list
     * @return a new list with only positive numbers
     */
    public static List<Integer> filterPositives(List<Integer> numbers) {
        // TODO: Create a Predicate<Integer> that tests if a number > 0.
        //       Use it to filter the list and return a new list of positives.
        return null;
    }

    // ──────────────────────────────────────────────
    // Exercise 6: Function – String to Length
    // ──────────────────────────────────────────────
    /**
     * Convert each string in the list to its length using Function<String, Integer>.
     *
     * @param strings the input list of strings
     * @return a list of integers representing the lengths
     */
    public static List<Integer> toLengths(List<String> strings) {
        // TODO: Create a Function<String, Integer> that returns a string's length.
        //       Apply it to each element and return a list of lengths.
        return null;
    }

    // ──────────────────────────────────────────────
    // Exercise 7: Chain Predicates with and() / or()
    // ──────────────────────────────────────────────
    /**
     * Given a list of integers, use two Predicates:
     *   - isPositive: number > 0
     *   - isEven: number % 2 == 0
     *
     * Return two lists:
     *   [0] elements that satisfy isPositive AND isEven
     *   [1] elements that satisfy isPositive OR isEven
     *
     * @param numbers the input list
     * @return an array of two lists: [andResult, orResult]
     */
    @SuppressWarnings("unchecked")
    public static List<Integer>[] chainPredicates(List<Integer> numbers) {
        // TODO: Define isPositive and isEven predicates.
        //       Create andPredicate = isPositive.and(isEven).
        //       Create orPredicate  = isPositive.or(isEven).
        //       Filter the list with each and return as a two-element array.
        return null;
    }

    // ──────────────────────────────────────────────
    // Exercise 8: Consumer – Print Uppercase
    // ──────────────────────────────────────────────
    /**
     * Use a Consumer<String> to print each string in the list in uppercase.
     * Each string should be printed on its own line.
     *
     * @param strings the list of strings to print
     */
    public static void printUpperCase(List<String> strings) {
        // TODO: Create a Consumer<String> that prints s.toUpperCase().
        //       Use forEach() on the list with this consumer.
    }

    // ──────────────────────────────────────────────
    // Exercise 9: Supplier – Random Number Generator
    // ──────────────────────────────────────────────
    /**
     * Use a Supplier<Double> to generate a list of n random numbers
     * between 0.0 (inclusive) and 1.0 (exclusive).
     *
     * @param n the number of random values to generate
     * @return a list of n random doubles
     */
    public static List<Double> generateRandomNumbers(int n) {
        // TODO: Create a Supplier<Double> using Math.random().
        //       Call it n times and collect the results into a list.
        return null;
    }

    // ──────────────────────────────────────────────
    // Exercise 10: Method Reference – Case-Insensitive Sort
    // ──────────────────────────────────────────────
    /**
     * Sort the given list of strings in case-insensitive order using
     * a method reference to String.compareToIgnoreCase.
     *
     * @param strings the list to sort
     * @return the sorted list
     */
    public static List<String> sortCaseInsensitive(List<String> strings) {
        // TODO: Sort the list using String::compareToIgnoreCase as
        //       the comparator (method reference). Return the list.
        return null;
    }

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

        System.out.println("=== Exercise 1: Generic Swap ===");
        Integer[] nums = {10, 20, 30, 40, 50};
        System.out.println("Before swap: " + Arrays.toString(nums));
        swap(nums, 1, 3);
        System.out.println("After swap(1,3): " + Arrays.toString(nums));
        // Expected: [10, 40, 30, 20, 50]

        System.out.println("\n=== Exercise 2: Triple Class ===");
        Triple<String, Integer, Double> triple = new Triple<>("Ali", 25, 3.8);
        System.out.println("Triple: " + triple);
        System.out.println("First: " + triple.getFirst());
        System.out.println("Second: " + triple.getSecond());
        System.out.println("Third: " + triple.getThird());
        // Expected: (Ali, 25, 3.8)

        System.out.println("\n=== Exercise 3: Find Max ===");
        List<Integer> intList = Arrays.asList(34, 12, 78, 56, 23);
        System.out.println("Max integer: " + findMax(intList));
        // Expected: 78
        List<String> strList = Arrays.asList("banana", "apple", "cherry");
        System.out.println("Max string: " + findMax(strList));
        // Expected: cherry

        System.out.println("\n=== Exercise 4: Sort by Length ===");
        List<String> words = new ArrayList<>(Arrays.asList("elephant", "cat", "dog", "butterfly", "ant"));
        words.sort(lengthComparator());
        System.out.println("Sorted by length: " + words);
        // Expected: [cat, dog, ant, elephant, butterfly]

        System.out.println("\n=== Exercise 5: Filter Positives ===");
        List<Integer> mixed = Arrays.asList(-5, 3, -1, 7, 0, -2, 4);
        List<Integer> positives = filterPositives(mixed);
        System.out.println("Positives: " + positives);
        // Expected: [3, 7, 4]

        System.out.println("\n=== Exercise 6: String to Lengths ===");
        List<String> names = Arrays.asList("Ali", "Fatma", "Hassan", "Zainab");
        List<Integer> lengths = toLengths(names);
        System.out.println("Lengths: " + lengths);
        // Expected: [3, 5, 6, 6]

        System.out.println("\n=== Exercise 7: Chain Predicates ===");
        List<Integer> numbers = Arrays.asList(-4, -3, -2, -1, 0, 1, 2, 3, 4);
        List<Integer>[] results = chainPredicates(numbers);
        System.out.println("Positive AND Even: " + results[0]);
        // Expected: [2, 4]
        System.out.println("Positive OR Even:  " + results[1]);
        // Expected: [-4, -2, 0, 1, 2, 3, 4]

        System.out.println("\n=== Exercise 8: Print Uppercase ===");
        List<String> greetings = Arrays.asList("hello", "jambo", "habari", "salaam");
        printUpperCase(greetings);
        // Expected: HELLO  JAMBO  HABARI  SALAAM (each on its own line)

        System.out.println("\n=== Exercise 9: Supplier – Random Numbers ===");
        List<Double> randoms = generateRandomNumbers(5);
        System.out.println("Random numbers: " + randoms);
        // Expected: 5 random doubles between 0.0 and 1.0

        System.out.println("\n=== Exercise 10: Case-Insensitive Sort ===");
        List<String> unsorted = new ArrayList<>(Arrays.asList("banana", "Apple", "cherry", "apricot", "Blueberry"));
        List<String> sortedList = sortCaseInsensitive(unsorted);
        System.out.println("Sorted: " + sortedList);
        // Expected: [Apple, apricot, banana, Blueberry, cherry]
    }
}
