package ulcrs.data;

import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;
import com.google.gson.stream.JsonReader;
import ulcrs.models.course.Course;
import ulcrs.models.shift.Shift;
import ulcrs.models.tutor.Tutor;

import java.io.InputStream;
import java.io.InputStreamReader;
import java.time.LocalDateTime;
import java.util.List;

public class DataStore {

    private List<Tutor> tutors;
    private List<Course> courses;
    private List<Shift> shifts;
    private LocalDateTime timeFetched = LocalDateTime.MIN;

    private static DataStore dataStore;

    public static DataStore getInstance() {
        if (dataStore == null) {
            dataStore = new DataStore();
        }
        return dataStore;
    }


    /**
     * Get the tutor of the specified id
     * @param id - id of the tutor
     * @return Tutor - the Tutor with the specified id, if it exists. Otherwise, null.
     */
    public static Tutor getTutor(int id) {
    	if (requiresFetch(getInstance().tutors)) {
    		fetch();
    	}
    	
        return getInstance().tutors.stream()
                .filter(tutor -> tutor.getId() == id)
                .findFirst()
                .orElse(null);
    }

    /**
     * Get the course of the specified id
     * @param id - id of the course
     * @return Course - the Course with the specified id, if it exists. Otherwise, null.
     */
    public static Course getCourse(int id) {
        if (requiresFetch(getInstance().courses)) {
        	fetch();
        }
        
        return getInstance().courses.stream()
                .filter(course -> course.getId() == id)
                .findFirst()
                .orElse(null);
    }

    /**
     * Get the shift of the specified id
     * @param id - id of the shift
     * @return Shift - the shift with the specified id, if it exists. Otherwise, null.
     */
    public static Shift getShift(int id) {
    	if (requiresFetch(getInstance().shifts)) {
        	fetch();
        }
    	
    	return getInstance().shifts.stream()
                .filter(s -> s.getId() == id)
                .findFirst()
                .orElse(null);
    }

    /**
     * Get all tutors, fetching from ULC if necessary.
     * @return List<Tutor> - list of all tutors
     */
    public static List<Tutor> getTutors() {
        if (requiresFetch(getInstance().tutors)) {
        	fetch();
        }

        return getInstance().tutors;
    }

    /**
     * Get all courses, fetching from ULC if necessary.
     * @return List<Course> - list of all courses
     */
    public static List<Course> getCourses() {
        if (requiresFetch(getInstance().courses)) {
        	fetch();
        }

        return getInstance().courses;
    }
    
    public static List<Shift> getShifts() {
    	// TODO implement - requires getting shifts from course, tutor data
    	if (requiresFetch(getInstance().courses)) {
        	fetch();
        }
    	
    	return getInstance().shifts;
    }

    private static <T> boolean requiresFetch(T reference) {
        return reference == null;
    }
    
    private static void fetch() {
    	// Fetch from ULC - TODO
    	// For now, just get data from mock data    	
    	
    	// Tutor
        InputStream is = DataStore.class.getClassLoader().getResourceAsStream("mockTutorsFull.json");
        JsonReader reader = new JsonReader(new InputStreamReader(is));
        getInstance().tutors = new Gson().fromJson(reader, new TypeToken<List<Tutor>>() {}.getType());
        
    	// Course
        is = DataStore.class.getClassLoader().getResourceAsStream("mockCoursesFull.json");
        reader = new JsonReader(new InputStreamReader(is));
        getInstance().courses = new Gson().fromJson(reader, new TypeToken<List<Course>>() {}.getType());
        
        // TODO using tutors and courses, get all shift information and save into getInstance().shifts
        
        getInstance().timeFetched = LocalDateTime.now();
    }  
    
}

@@ -998,33 +998,31 @@
 %7B%0D%0A    %09
-if (requiresFetch
+fetchIfRequired
 (getInst
@@ -1039,42 +1039,9 @@
 ors)
-) %7B%0D%0A    %09%09fetch();%0D%0A    %09%7D%0D%0A    %09
+;
 %0D%0A  
@@ -1448,37 +1448,32 @@
  %7B%0D%0A    
-    if (requiresFetch
+%09fetchIfRequired
 (getInst
@@ -1491,51 +1491,9 @@
 ses)
-) %7B%0D%0A        %09fetch();%0D%0A        %7D%0D%0A        
+;
 %0D%0A  
@@ -1898,33 +1898,31 @@
 %7B%0D%0A    %09
-if (requiresFetch
+fetchIfRequired
 (getInst
@@ -1939,48 +1939,9 @@
 fts)
-) %7B%0D%0A        %09fetch();%0D%0A        %7D%0D%0A    %09
+;
 %0D%0A  
@@ -2271,37 +2271,32 @@
  %7B%0D%0A    
-    if (requiresFetch
+%09fetchIfRequired
 (getInst
@@ -2313,43 +2313,9 @@
 ors)
-) %7B%0D%0A        %09fetch();%0D%0A        %7D%0D%0A
+;
 %0D%0A  
@@ -2540,29 +2540,24 @@
     
-    if (requiresFetch
+%09fetchIfRequired
 (get
@@ -2579,43 +2579,9 @@
 ses)
-) %7B%0D%0A        %09fetch();%0D%0A        %7D%0D%0A
+;
 %0D%0A  
@@ -2758,25 +2758,23 @@
    %09
-if (requiresFetch
+fetchIfRequired
 (get
@@ -2796,92 +2796,184 @@
 ses)
-) %7B
+;
 %0D%0A    
- 
-   %09fetch()
+%09return getInstance().shifts
 ;%0D%0A    
-  
-  %7D
+%7D%0D%0A
 %0D%0A    
-%09%0D%0A    %09return getInstance().shifts
+private static %3CT%3E void fetchIfRequired(T reference) %7B%0D%0A    %09if (!isCached(reference)) %7B%0D%0A    %09%09fetch()
 ;%0D%0A    
+%09
 %7D%0D%0A
+    %7D%0D%0A    
 %0D%0A  
@@ -3005,21 +3005,16 @@
 ean 
-requiresFetch
+isCached
 (T r
@@ -3051,17 +3051,17 @@
 ference 
-=
+!
 = null;%0D

Add fetchIfRequired method for clarity in DataStore