package de.lcs.wep.calendar;

import com.google.api.client.googleapis.auth.oauth2.GoogleCredential;
import com.google.api.client.googleapis.javanet.GoogleNetHttpTransport;
import com.google.api.client.http.HttpTransport;
import com.google.api.client.json.JsonFactory;
import com.google.api.client.json.gson.GsonFactory;
import com.google.api.client.util.DateTime;
import com.google.api.services.calendar.Calendar;
import com.google.api.services.calendar.model.Events;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

import java.io.IOException;
import java.security.GeneralSecurityException;
import java.util.*;

/**
 * Created by Sönke Greve on 30.10.2014.
 */
public class CalendarReader {

    public static Logger logger = LogManager.getLogger(CalendarReader.class);

    public static CalendarReader fromProperties(Properties props) {
        Set<Resource> resources = Resource.fromProperties(props);
        Credential credential = Credential.fromProperties(props);
        GoogleCredential gc = credential.createGoogleCredential();
        CalendarReader calReader = new CalendarReader(gc, resources);
        String timeZone = props.getProperty(Constants.PROP_KEY_TIMEZONE, Constants.DEFAULT_TIMEZONE);
        String strMaxResult = props.getProperty(Constants.PROP_KEY_MAX_EVENTS, "5");
        int maxResults;
        try {
            maxResults = Integer.valueOf(strMaxResult);
        } catch (Exception e) {
            maxResults = Constants.DEFAULT_MAX_KEYS;
            logger.warn("Couldn't parse number of events from properties file. Setting " + maxResults + " events.");
        }
        calReader.setMaxResults(maxResults);
        calReader.setTimeZone(timeZone);
        return calReader;
    }

    private GoogleCredential gCred;
    private Set<Resource> resources;
    private int maxResults;
    private String timeZone;

    public CalendarReader(GoogleCredential gc, Set<Resource> resources) {
        this.gCred = gc;
        this.resources = resources;
    }

    public void update() throws GeneralSecurityException, IOException {
        HttpTransport transport = GoogleNetHttpTransport.newTrustedTransport();
        JsonFactory json = new GsonFactory();
        Calendar.Builder builder = new Calendar.Builder(transport, json, gCred);
        builder.setApplicationName(Constants.APPLICATION_NAME);
        Calendar calendar = builder.build();

        // writes directly into the resources member
        updateEvents(calendar);
    }

    private void updateEvents(Calendar service) {

        for (Resource resource : resources) {
            try {
                readCalendar(service, resource);
                logger.debug("Successfully read calendar: " + resource);
            } catch (Exception e) {
                logger.warn("Service was unable to read calendar with id: " + resource.getID());
            }
        }
    }

    private void readCalendar(Calendar service, Resource resource) throws IOException {
        Calendar.Calendars.Get calRequest = service.calendars().get(resource.getID());
        com.google.api.services.calendar.model.Calendar calendar = calRequest.execute();

        // set startTime date for request from resource
        DateTime startTime = new DateTime(new Date(), TimeZone.getDefault());

        // update the resource's name
        resource.setName(calendar.getSummary());

        // get the next globVar.NUMBER_EVENTS events from resource
        Calendar.Events.List eventListRequest = createEventListRequest(service, resource.getID(), startTime);
        Events response = eventListRequest.execute();

        if (response.getItems().isEmpty()) {
            return;
        } else {
            // write all event information for the result events into an arraylist
            List<Event> events = resource.getEvents();
            events.clear();
            for (com.google.api.services.calendar.model.Event gEvent : response.getItems()) {
                logger.debug("read event: " + gEvent);
                Event event = readEvent(gEvent);
                logger.debug("added event: " + event);
                events.add(event);
            }
        }
    }

    private Calendar.Events.List createEventListRequest(Calendar service, String resourceID, DateTime startTime) throws IOException {
        Calendar.Events.List eventListRequest = service.events().list(resourceID);
        eventListRequest.setMaxResults(maxResults);
        eventListRequest.setOrderBy(Constants.GOOGLE_CALENDER_ORDER_BY);
        eventListRequest.setSingleEvents(true);
        eventListRequest.setTimeMin(startTime);
        eventListRequest.setTimeZone(timeZone);
        return eventListRequest;
    }

    private Event readEvent(com.google.api.services.calendar.model.Event gEvent) {
        String id = gEvent.getId();
        String owner = gEvent.getCreator().getDisplayName();
        String subject = gEvent.getSummary();
        String visibility = gEvent.getVisibility();
        DateTime start = gEvent.getStart().getDateTime();
        DateTime end = gEvent.getEnd().getDateTime();
        return new Event(id, owner, visibility, subject, start, end);
    }

    public void setMaxResults(int maxResults) {
        this.maxResults = maxResults;
    }

    public String getTimeZone() {
        return timeZone;
    }

    public void setTimeZone(String timeZone) {
        this.timeZone = timeZone;
    }

    public Set<Resource> getResources() {
        return resources;
    }
}
