import java.util.*;
import java.time.*;

/**
 * Verschiedene Aufgaben, die statisch direkt aus BlueJ heraus aufgerufen werden.
 * 
 * @author S.Gebert 
 * @version 12.2020
 */
public class Aufgabe
{

    /**
     * Aufgabe.a1
     * Schreibe eine Methode, die für das erste Element einer gegebene Filmsammlung (von Serien) die Bewertung auf 5.0 setzt
     * und
     * den Titel und die Anzahl der vorhandenen Episoden auf der Konsole ausgibt.
     * 
     * Hinweise:    - Betrachte die Dokumentation der Klasse Filmsammlung. Mit welcher Methode kannst du auf die Elemente der Sammlung zugreifen?
     *              - Prüfe an welcher Stelle eine Typkonvertierung nötig ist.
     * 
     * Teste deine Methode mit der Filmsammlung s und kf (Basis - Objektzustand wiederherstellen)
     *
     * @param fs Eine Filmsammlung
     */
    public static void a1(Filmsammlung fs)
    {
        fs.get(0).setBewertung( 5.0 ); // Durch Polymorphie wird die korrekte Methode der Serie bzw. des Films aufgerufen, bei einer Serie findet keine Änderung der Bewertung statt.
        System.out.println( "Anzahl Episoden der Serie '"+ fs.get(0).getTitel()+"': " + ( (Serie) fs.get(0) ).getAnzahlEpisoden() );
        /*
         * Falls fs eine Filmsammlung von Serien:
         * fs.get(0).getTitel(); //implitzite erweiternde Typumwandlung: Serie -> Film
         * ( (Serie) fs.getFilm(0) ).getAnzahlEpisoden(); //explizite einschränkende Typumwandlung: Film -> Serie
         * 
         * Falls fs eine Filmsammlung von Kinofilmen:
         * Exception: java.lang.ClassCastException (class Kinofilm cannot be cast to class Serie)
         */

    }

    /**
     * Aufgabe.a2
     * Schreibe eine Methode, die für das zweite Element einer gegebene Filmsammlung (von Serien) die Bewertung auf 5.0 setzt
     * und
     * den Titel und die Anzahl der vorhandenen Episoden auf der Konsole ausgibt.
     * 
     * Teste deine Methode mit der Filmsammlung s und kf (Basis - Objektzustand wiederherstellen)
     * 
     * @param fsG Eine Seriensammlung
     */
    public static void a2(FilmsammlungGeneric<Serie> fsG)
    {
        fsG.get(1).setBewertung( 5.0 ); // die Methode der Serie aufgerufen, es findet keine Änderung der Bewertung statt.
        System.out.println( "Anzahl Episoden der Serie '"+ fsG.get(1).getTitel()+"': " + fsG.get(1).getAnzahlEpisoden() );
        /*
         * fsG.get(1).getTitel(); //keine Typumwandlung, die von der Vaterklasse geerbte Methode wird aufgerufen.
         * fsG.get(1).getAnzahlEpisoden(); //keine Typumwandlung notwendig, weder implizit noch explizit 
         */

    }

    /**
     * Aufgabe.a3
     * Erstelle verschieden Filmsammlungen und teste deren Methoden.
     * Um einzelne Filme zu erhalten kannst die Methoden Daten.randomFilm(), Daten.randomKinofilm(), Daten.randomSerie() und Daten.randomEpisode() verwenden.
     * 
     * - Welche konkreten Typparameter sind erlaubt, welche nicht?
     * - Finde Beispiele für die es zu Compilierungs/Laufzeitfehlern kommen kann.
     */
    public static void a3()
    {
        //Beispiel
        FilmsammlungGeneric<SerienEpisode> sammlung1 = new FilmsammlungGeneric<>( new SerienEpisode[]{Daten.randomEpisode(), Daten.randomEpisode() });
        System.out.println(sammlung1.get(0).getTitel());

        //Lösungsbeispiel
        FilmsammlungGeneric<String> sammlung2 = new FilmsammlungGeneric<>( new String[]{"1","blabla" });
        //System.out.println(sammlung2.get(0).getTitel()); //Hier kommt es zum Compilierungsfehler "cannot find symbol - method getTitel() 

    }

    /**
     * Aufgabe.a4
     * Die gegebene Methode soll die Titel aller Filme in einer Filmsammlung ausgeben.
     * Teste die gegebene Methode mit den Basisdaten sG und kfG.
     * 
     *  - Notiere dir den Laufzeitfehler den du erhälst.
     *  - Ändere die Methode so ab, dass es mit sG bzw. kfG funktioniert. Gibt es eine allgemeine Lösung?
     * @param fsG Eine Filmsammlung
     */
    public static void a4(FilmsammlungGeneric<Film> fsG)
    {
        Iterator<Film> iter = fsG.iterator();

        while( iter.hasNext() ){
            System.out.print( iter.next().getTitel() +", ");
        }

    }

    /**
     * Musterlösung der Aufgabe.a4
     * generische Implementierung
     *
     * @param fsG Eine Filmsammlung
     */
    public static <E extends Film> void a4_generic(FilmsammlungGeneric<E> fsG)
    {
        Iterator<E> iter = fsG.iterator();

        while( iter.hasNext() ){
            System.out.print( iter.next().getTitel() +", ");
        }

        //Alternativ
        for( E f : fsG ){
            System.out.print( f.getTitel() + ", ");
        }
    }

    /**
     * Aufgabe.a5
     * Die gegebene Methode soll die Titel aller Filme in einer Filmsammlung von Kinofilmen ausgeben.
     * Implementiere die Methode mit der erweiterten for-Schleife
     * Teste mit den Basisdaten kfG.
     *
     * @param fsG Eine Filmsammlung
     */
    public static void a5(FilmsammlungGeneric<Kinofilm> fsG)
    {
        for( Kinofilm f : fsG ){
            System.out.print( f.getTitel() +", ");
        }
    }

    /**
     * Aufgabe.a6
     * Finde für jede gegebene Reihung jeweils das Minimum.
     * Schreibe dafür je eine private statische Methode und rufe diese auf.
     * Gib das Ergebnis auf der Konsole aus.
     * 
     */
    public static void a6()
    {
        //Gegebene Reihungen
        Integer[] rInt = {5, 90, 4, 1, -70, 400, 30};
        String[] rString = {"Bär","Affe","Nilpferd","Zebra"};
        Float[] rFloat = {3.4f, 9.78f,.01f};

        //Aufruf
        System.out.println( getMinInteger(rInt) );
        System.out.println( getMinString(rString) );
        System.out.println( getMinFloat(rFloat) );

        //Aufruf der generischen Implementierung
        System.out.println(getMinGeneric(Arrays.asList(rInt)));
        System.out.println(getMinGeneric(Arrays.asList(rString)));
        System.out.println(getMinGeneric(Arrays.asList(rFloat)));
    }

    private static Integer getMinInteger(Integer[] r)
    {

        Integer min = r[0];

        for(int i = 1; i < r.length; i++)
        {
            if(r[i] < min)
                min = r[i];
        }

        return min;
    }

    private static String getMinString(String[] r)
    {

        String min = r[0];

        for(int i = 1; i < r.length; i++)
        {
            if(r[i].toLowerCase().compareTo(min.toLowerCase()) < 0)
                min = r[i];
        }

        return min;
    }

    private static Float getMinFloat(Float[] r)
    {
        Float min = r[0];

        for(int i = 1; i < r.length; i++)
        {
            if(r[i] < min)
                min = r[i];
        }

        return min;
    }

    //verwende generische Methoden um Abhängigkeiten zwischen Parametern und/oder Rückgabewert auszudrücken.
    private static < T extends Comparable > T getMinGeneric(List<T> r)
    {
        T min = r.get(0);

        for( T t : r )
        {
            if(t.compareTo(min) < 0)
                min = t;
        }

        return min;

    }

    //Einzeiler für die Nutzung in der Direkteingabe. Siehe Beispielaufgaben im Unterrichtsgang
    private static void einZeiler(FilmsammlungGeneric<Kinofilm> kfG)
    {
        //Filme mit Bewertung schlechter als 7.0 aus Sammlung entfernen.
        kfG.removeIf(f -> (f.getBewertung()<7.0));

        //Alle Titel einer Sammlung ausgeben.
        kfG.forEach(f -> System.out.print(f.getTitel()+", "));

        //Alle Bewertungen um 0.5 erhöhen, aber nicht über 10.0
        kfG.forEach(f -> {if(f.getBewertung()<=9.5) f.setBewertung(f.getBewertung()+0.5);});

        //Filmtitel mit schlechtester Bewertung ausgeben
        System.out.println("Schlechteste Bewertung: " + java.util.Collections.min(kfG, java.util.Comparator.comparing(f -> f.getBewertung() )).getTitel());

        //Filmtitel mit schlechtester Bewertung via Stream bestimmen und ausgeben.
        System.out.println("Schlechteste Bewertung:"+ kfG.stream().reduce(kfG.get(0), (min, t) -> (t.getBewertung() < min.getBewertung()) ? t : min).getTitel() );

        // Anzahl aller Filme der Sammlung mit einer Bewertung besser als 7.0
        System.out.println("Anzahl Bewertungen über 7.0:" + kfG.stream().filter( f -> f.getBewertung()>7.0 ).mapToInt(f -> 1).sum() );

    }

}
