package ch.epfl.isochrone.timetable;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;

import ch.epfl.isochrone.geo.PointWGS84;

/**
 * Classe servant à lire les tableau de TL et de les convertir au format codé précédemment. 
 * 
 * @author Charles Thiebaut (238935) 
 * @author Arnaud Delamare (234841)
 *
 */



public final class TimeTableReader {
    private String prefixe;

    /**
    *Constructeur permettant de définir le préfixe de l'aborescence des fichiers à lire. 
    *@param l'aborescence du fichier dans lequelle les fichiers à lire son stocker
    */ 
    public TimeTableReader(String baseResourceName)
    {
        this.prefixe=baseResourceName;
    }
    
    /**
    *@throw IOException
    *       en cas d'erreur d'entrée-sortie ou d'autres exceptions en cas d'erreur de format de données.
    *@return le tableau horaire remplis avec les donnés consernant les arrêts ainsi que les services.
    */
    public TimeTable readTimeTable() throws IOException 
    {
        TimeTable.Builder tb = readStops(new TimeTable.Builder());
        
        tb = readServices(tb);
        
        return tb.build();
    }
    
    /**
    *@throw IOException
    *       en cas d'erreur d'entrée-sortie ou d'autres exceptions en cas d'erreur de format de données.
    *@return un graph qui correspond aux informations entrées en paramètre et aux données fournies dans les .cvs
    */
    public Graph readGraphForServices(Set<Stop> stops, Set<Service> services, int walkingTime, double walkingSpeed) throws IOException
    {
        Graph.Builder gb = new Graph.Builder(stops);
        
        InputStream stopsStream = getClass().getResourceAsStream(prefixe + "stop_times.csv");
        
        BufferedReader reader = new BufferedReader( new InputStreamReader(stopsStream, StandardCharsets.UTF_8));
        String line;
        String champs[];
        Stop stop1 = null, stop2 = null;
        while ((line = reader.readLine()) != null) {
            champs = line.split(";");
            
            for (Service s : services)
            {
                if (s.name().equals(champs[0]))
                {
                    stop1=null;
                    stop2=null;
                    for (Stop st : stops)
                    {
                        if (st.name().equals(champs[1]))
                        {
                            stop1 = st;
                        }
                        if (st.name().equals(champs[3]))
                        {
                            stop2 = st;
                        }
                        
                    }
                    
                    if (stop1!=null && stop2!=null)
                        gb.addTripEdge(stop1, stop2, Integer.parseInt(champs[2]), Integer.parseInt(champs[4]));
                    
                }
            }
        }
        reader.close();
        
        gb.addAllWalkEdges(walkingTime, walkingSpeed);
        
        return gb.build(); 
    }
    /**
    *@throw IOException
    *       en cas d'erreur d'entrée-sortie ou d'autres exceptions en cas d'erreur de format de données.
    *@return un tableau horaire remplis avec les donnés consernant les arrêts.
    */
    private TimeTable.Builder readStops(TimeTable.Builder tb) throws IOException 
    {
        InputStream stopsStream = getClass().getResourceAsStream(prefixe + "stops.csv");
        
        BufferedReader reader = new BufferedReader( new InputStreamReader(stopsStream, StandardCharsets.UTF_8));
        String line;
        String champs[];
        while ((line = reader.readLine()) != null) {
            champs = line.split(";");
            tb.addStop(new Stop(champs[0], new PointWGS84(ch.epfl.isochrone.math.Math.degToRad(Double.parseDouble(champs[1])), ch.epfl.isochrone.math.Math.degToRad(Double.parseDouble(champs[2])))));
        }
        reader.close();
        
        return tb;
    }
    
    /**
    *@throw IOException
    *       en cas d'erreur d'entrée-sortie ou d'autres exceptions en cas d'erreur de format de données.
    *@return le tableau horaire remplis avec les donnés consernant les services.
    */
    private TimeTable.Builder readServices(TimeTable.Builder tb) throws IOException 
    {
        InputStream serviceStream = getClass().getResourceAsStream(prefixe + "calendar.csv");
        
        BufferedReader reader = new BufferedReader( new InputStreamReader(serviceStream, StandardCharsets.UTF_8));
        List<Service.Builder> sbListe = new ArrayList<Service.Builder>();
        Service.Builder sb;
        String line;
        String champs[];
        Date.DayOfWeek[] tab = Date.DayOfWeek.values();
        
        while ((line = reader.readLine()) != null) {
            champs = line.split(";");
            sb = new Service.Builder(champs[0], stringToDate(champs[8]), stringToDate(champs[9]));
            
            for (int i=1; i<=7; i++)
            {
                if (Integer.parseInt(champs[i])==1)
                {
                    sb.addOperatingDay(tab[i-1]);
                }
            }
            
            sbListe.add(sb);
        }
        reader.close();
        
        
        InputStream serviceStream2 = getClass().getResourceAsStream(prefixe + "calendar_dates.csv");
        
        BufferedReader reader2 = new BufferedReader( new InputStreamReader(serviceStream2, StandardCharsets.UTF_8));
        
        while ((line = reader2.readLine()) != null) {
            champs = line.split(";");
            
            for (Service.Builder sb2 : sbListe)
            {
                if (sb2.name().equals(champs[0]))
                {
                    if (Integer.parseInt(champs[2])==1)
                    {
                        sb2.addIncludedDate(stringToDate(champs[1]));
                    }
                    else
                    {
                        sb2.addExcludedDate(stringToDate(champs[1]));
                    }
                }
            }
        }
        reader2.close();
        
        for (Service.Builder sb2 : sbListe)
        {
            tb.addService(sb2.build());
        }
        
        return tb;
    }
    
    /**
    *@param s
    *       la chaine de caractère que l'on souhaite consertir au format Date. 
    *@return la chaine de caractère passé en argument au format Date.
    */
    private Date stringToDate(String s){
        return new Date(Integer.parseInt(s.substring(6, 8)), Integer.parseInt(s.substring(4, 6)), Integer.parseInt(s.substring(0, 4)));
    }

}
