package totale.p240213;

import java.util.TreeMap;
import java.util.HashMap;

import java.util.Set;
import java.util.TreeSet;

import java.util.Comparator;

import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.HashSet;

public class Discendenza {
    
    /*
    <CF_Figlio, CF_Padre>
    */
    private TreeMap<String, String> discendenza = new TreeMap<String, String>();

    // Metodo 1
    public String aggiungiRelazione(String cfFiglio, String cfPadre) {
        // Null Check
        if (cfFiglio == null || cfPadre == null) throw new NullPointerException();
        // CF Check
        if (cfFiglio.length() != 16 || cfPadre.length() != 16) throw new IllegalArgumentException();
        // Otteniamo il vecchio valore se presente
        String vecchioPadre = discendenza.get(cfFiglio);
        // Aggiungiamo il nuovo valore
        discendenza.put(cfFiglio, cfPadre);
        // Restituisco il valore 
        return vecchioPadre;
    }

    // Metodo 2
    public String cancellaRelazione(String cfFiglio) {
        // Null Check
        if (cfFiglio == null) throw new NullPointerException();
        // Rimuoviamo il vecchio valore
        return discendenza.remove(cfFiglio);
    }

    // Metodo 3
    public Set<String> getFigli() {
        return new TreeSet<String>(discendenza.keySet());
    }

    // Metodo 4
    public Set<String> getPadri() {
        // Costruzione della struttura dati.
        Set<String> padriByMeseNascita = new TreeSet<String>(new Comparator<String>() {
            @Override
            public int compare(String s1, String s2) {
                Character c1 = s1.charAt(8);
                Character c2 = s2.charAt(8);
                
                int cmp = Character.compare(c1, c2);
                if (cmp != 0) return cmp;

                return s1.compareTo(s2);
            }
        });
        // Aggiunta di tutti i padri
        for (String cfFiglio : discendenza.keySet()) {
            String cfPadre = discendenza.get(cfFiglio);
            if (cfPadre == null) continue;
            padriByMeseNascita.add(cfPadre);
        }
        // Restituisco la struttura dati
        return padriByMeseNascita;
    }

    // Metodo 5
    public Set<String> getAscendenti(String cfFiglio) {
        // Null Check
        if (cfFiglio == null) throw new NullPointerException();
        // Se la mappa è vuota
        if (discendenza.isEmpty()) return null;
        // Se la mappa non contiene il cfFiglio
        if (!discendenza.containsKey(cfFiglio)) return null;
        // E' presente il cfFiglio, possiamo cercare
        Set<String> ascendenti = new LinkedHashSet<String>();
        while (true) {
            String currentPadre = discendenza.get(cfFiglio);
            if (currentPadre == null) break;
            // Se è presente un ciclo esci
            if (ascendenti.contains(currentPadre)) break;
            ascendenti.add(currentPadre);
            cfFiglio = currentPadre;
        }
        // Ritorno degli ascendenti
        return ascendenti;
    }

    // Metodo  6
    public double averageNumFigli() {
        // Mappa <Padri, nFigli>
        HashMap<String, Integer> occorrenzeFigli = new HashMap<String, Integer>();
        // Conto quanti figli ha ciascun padre
        for (String cfFiglio : discendenza.keySet()) {
            String cfPadre = discendenza.get(cfFiglio);
            if (cfPadre == null) continue;
            if (occorrenzeFigli.get(cfPadre) == null) occorrenzeFigli.put(cfPadre, 1);
            else occorrenzeFigli.put(cfPadre, occorrenzeFigli.get(cfPadre) + 1);
        }
        // Verifico se non sono presenti figli 
        if (occorrenzeFigli.isEmpty()) return 0;
        // Calcolo la media
        Double avg = 0.0;
        for (String cfPadre : occorrenzeFigli.keySet()) avg += occorrenzeFigli.get(cfPadre);
        return avg / occorrenzeFigli.size(); 
    }

}
