
package ch.epfl.isochrone.timetable;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

/**
 * Classe servant à représenter l'arbre des trajets les plus rapides
 * 
 * @author Charles Thiebaut (238935) 
 * @author Arnaud Delamare (234841)
 *
 */
public final class FastestPathTree {
    private final Stop startingStop;
    private final Map<Stop, Integer> arrivalTime;
    private final Map<Stop, Stop> predecessor;
    

    /**
    *construit un arbre des trajets les plus rapides avec l'arrêt de départ, la table des heures d'arrivée et la table des prédécesseurs donnés.
    *@throw IllegalArgumentException
    *       si l'ensemble des clefs de la table des heures n'est pas égal à celui des clefs de la tables des prédécesseurs plus l'arrêt de départ.
    *@param startingStop
    *       L'arrêt de déapart
    *@param arrivalTime
    *       la tabée des heures d'arrivée 
    *       (associant à un certain nombre d'arrêt l'heure de la première arrivée (en seconde après minuit)). 
    *@param predecessor
    *       la table des prédécesseurs  
    *       (associant chaque arrêt à son prédécesseur dans l'arbre)
    */ 
    public FastestPathTree(Stop startingStop, Map<Stop, Integer> arrivalTime, Map<Stop, Stop> predecessor)
    {
        Set<Stop> set1 = new HashSet<Stop>(arrivalTime.keySet());
        Set<Stop> set2 = new HashSet<Stop>(predecessor.keySet());
        set2.add(startingStop);
        
        if (!set1.equals(set2))
        {
            throw new IllegalArgumentException();
        }
        
        this.startingStop = startingStop;
        this.arrivalTime = new HashMap<Stop, Integer>(arrivalTime);
        this.predecessor = new HashMap<Stop, Stop>(predecessor);
    }
    
    /**
    *@return l'arrêt de départ
    */ 
    public Stop startingStop()
    {
        return startingStop;
    }

    /**
    *@return l'heure de départ
    */ 
    public int startingTime()
    {
        return arrivalTime.get(startingStop);
    }
    
    /**
    *@return l'ensemble des arrêts pour lesquels une heure de première arrivée existe. 
    */ 
    public Set<Stop> stops()
    {
        return new HashSet<Stop>(arrivalTime.keySet());
    }
    
    /**
    *@return l'heure d'arrivée ou SecondsPastMidnight.INFINITE si l'arrêt donné n'est pas dans la table des heures d'arrivée passée au constructeur.
    */ 
    public int arrivalTime(Stop stop)
    {
        if (arrivalTime.containsKey(stop))
        {
            return arrivalTime.get(stop);
        }
        else
        {
            return SecondsPastMidnight.INFINITE;
        }
    }
    
    /**
    *@throw IllegalArgumentException
    *       si l'arrêt passé n'est pas présent dans la table des heures d'arrivée. 
    *@param stop
    *       arrêt auquel au souhaite aller. 
    *@return le chamin pour aller de l'arrêt de départ à celui passé en argument 
    *        (le première élément de la liste étant l'arrêt de départ et le dernier celui d'arrivée). 
    */ 
    public List<Stop> pathTo(Stop stop)
    {
        if (!arrivalTime.containsKey(stop))
        {
            throw new IllegalArgumentException();
        }
        
        List<Stop> liste = new ArrayList<Stop>();
        
        Stop s = stop;
        liste.add(stop);  // liste dans le mauvais sens
        do
        {
            s = predecessor.get(s);
            liste.add(s);
        } while(!s.equals(startingStop));
        
        List<Stop> liste2 = new ArrayList<Stop>();
        
        for (int i=0; i<liste.size(); i++)
        {
            liste2.add(liste.get(liste.size()-1-i));
        }
        
        return liste2;
    }
    
    
    
   
    /**
    * class qui sert de Builer pour un FastestPathTree
    */ 
    public final static class Builder
    {
        private final Stop startingStop;
        private final Map<Stop, Integer> arrivalTime;
        private final Map<Stop, Stop> predecessor;
        
        /**
        *qui construit un bâtisseur pour un arbre des trajets les plus rapides avec l'arrêt et l'heure de départ donnés. 
        *@throw IllegalArgumentException
        *       si l'heure de départ est négative.
        *@param startingStop
        *       l'arrêt de départ.
        *@param startingTime
        *       l'heure de départ.
        *       (l'heure de première arrivée de l'arrêt de départ est l'heure de départ). 
        */
        public Builder(Stop startingStop, int startingTime)
        {
            if (startingTime<0)
            {
                throw new IllegalArgumentException();
            }
            
            this.startingStop=startingStop;
            this.arrivalTime = new HashMap<Stop, Integer>();
            this.predecessor = new HashMap<Stop, Stop>();
            
            arrivalTime.put(startingStop, startingTime);
        }
        
        /**
        *(re)définit l'heure de première arrivée et le prédécesseur de l'arrêt donné dans l'arbre en construction
        *@throw IllegalArgumentException
        *       si l'heure donnée est antérieure à l'heure de départ. 
        *@param stop
        *       l'arrêt dont l'on souhaite modifié les données
        *@param time
        *       la nouvelle première heure d'arrivée
        *@param predecessor
        *       le nouveau prédecesseur 
        *@return le builder modifié
        */
        public Builder setArrivalTime(Stop stop, int time, Stop predecessor)
        {
            if (time<arrivalTime.get(startingStop))
            {
                throw new IllegalArgumentException();
            }
            
            this.arrivalTime.put(stop, time);
            this.predecessor.put(stop, predecessor);
            
            return this;
        }
        /**
        *retourne l'heure de première arrivée à l'arrêt donné
        *@param stop 
        *       l'arrêt pour lequel on souhaite obtenir la première arrivée. 
        *@return l'heure d'arrivée à l'arrêt souhaite ou SecondsPastMidnight.INFINITE si aucune heure d'arrivée n'a été attribuée à cet arrêt jusqu'ici.
        */ 
        public int arrivalTime(Stop stop)
        {
            if (arrivalTime.containsKey(stop))
            {
                return arrivalTime.get(stop);
            }
            else
            {
                return SecondsPastMidnight.INFINITE;
            }
        }
        
        /**
        *@return le FastestPathTree construit via le Builder. 
        */
        public FastestPathTree build()
        {
            return new FastestPathTree(startingStop, arrivalTime, predecessor);
        }
    }
}
