package gov.va.vinci.dart.json.builder;

import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.Set;

import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import gov.va.vinci.dart.biz.Event;
import gov.va.vinci.dart.biz.EventType;
import gov.va.vinci.dart.biz.Group;
import gov.va.vinci.dart.biz.Request;
import gov.va.vinci.dart.biz.RequestStatus;
import gov.va.vinci.dart.biz.RequestWorkflow;
import gov.va.vinci.dart.biz.Review;
import gov.va.vinci.dart.json.ReviewStatusView;

public class ReviewStatusViewBuilder {
    private static Log log = LogFactory.getLog(ReviewStatusViewBuilder.class);

    /**
     * Status Visualization
     * 
     * @param request
     * @return
     */
    public static ReviewStatusView populateInitialNDSReviewStatus(final RequestWorkflow workflow, final Request request) {

        ReviewStatusView view = new ReviewStatusView();

        if (request != null) {

            Group.initialize();
            final boolean isInitialReview = true;

            view.setId(1);

            // group name
            final String groupName = Review.INITIAL_NDS_GROUP + " Review";
            view.setGroupName(groupName);
            view.setGroupNameNoSpaces(getGroupNameWithoutSpaces(groupName));

            // status
            String status = request.getNDSReviewStatus(workflow, Group.NDS, isInitialReview);
            view.setStatus(status);
            view.setStatusClass(getStatusClassName(status)); // style info

            // days elapsed (reviewer)
            double reviewElapsedTime = getReviewElapsedDaysForNDS(workflow, request, Group.NDS, isInitialReview);
            view.setDaysElapsed(ReviewStatusView.formatTo2DecimalPlaces(reviewElapsedTime));

            // elapsed time (requestor)
            double requestorElapsedTime = getRequestorElapsedDaysForNDS(workflow, request, Group.NDS, isInitialReview);
            view.setRequestorDaysElapsed(ReviewStatusView.formatTo2DecimalPlaces(requestorElapsedTime));

            view.setSortOrder(ReviewStatusView.getSortOrderForGroup(Group.NDS, isInitialReview)); // initial NDS review
        } // end if

        return view;
    }

    public static ReviewStatusView populateFinalNDSReviewStatus(final RequestWorkflow workflow, final Request request) {

        ReviewStatusView view = new ReviewStatusView();

        if (request != null) {

            Group.initialize();

            view.setId(2);

            // group name
            final String groupName = Review.FINAL_NDS_GROUP + " Review";
            view.setGroupName(groupName);
            view.setGroupNameNoSpaces(getGroupNameWithoutSpaces(groupName));

            // The Final NDS approval should not indicate waiting for review until it actually goes into their To Do List
            // Once all the other approvals are granted, then it would indicate waiting for review.
            if (request.allReviewsApproved(workflow)) {

                // status
                String status = request.getNDSReviewStatus(workflow, Group.NDS, false);
                view.setStatus(status);
                view.setStatusClass(getStatusClassName(status)); // style info

                // days elapsed (reviewer)
                double reviewElapsedTime = getReviewElapsedDaysForNDS(workflow, request, Group.NDS, false);
                view.setDaysElapsed(ReviewStatusView.formatTo2DecimalPlaces(reviewElapsedTime));

                // elapsed time (requestor)
                double requestorElapsedTime = getRequestorElapsedDaysForNDS(workflow, request, Group.NDS, false);
                view.setRequestorDaysElapsed(ReviewStatusView.formatTo2DecimalPlaces(requestorElapsedTime));
            } // end if -- all intermediate reviews approved

            view.setSortOrder(ReviewStatusView.getSortOrderForGroup(Group.NDS, false)); // final NDS review
        } // end if

        return view;
    }

    // intermediate review
    public static ReviewStatusView populateReviewStatus(final RequestWorkflow workflow, final Request request,
            final Review review) {

        ReviewStatusView view = new ReviewStatusView();

        if (request != null && review != null) {
            view.setId(review.getId());

            // group name
            Group group = review.getReviewer();
            String groupShortName = "";
            if (group != null) {
                groupShortName = group.getShortName();
                view.setGroupName(groupShortName);
                view.setGroupNameNoSpaces(getGroupNameWithoutSpaces(groupShortName));
            }

            // review status
            String reviewStatus = review.getReviewStatus(); // get the review status
            view.setStatus(reviewStatus);
            view.setStatusClass(getStatusClassName(reviewStatus));

            // elapsed time (reviewer)
            double reviewElapsedTime = getReviewElapsedDays(review, workflow, request, group, false);
            view.setDaysElapsed(ReviewStatusView.formatTo2DecimalPlaces(reviewElapsedTime));

            // elapsed time (requestor)
            double requestorElapsedTime = getRequestorElapsedDays(review, workflow, request, group, false);
            view.setRequestorDaysElapsed(ReviewStatusView.formatTo2DecimalPlaces(requestorElapsedTime));

            // sort order: Privacy, Security, ORD, others
            view.setSortOrder(ReviewStatusView.getSortOrderForGroup(group, false));
        }

        return view;
    }

    /**
     * Returns the # of days elapsed between Reviewer opening and closing events: get the opening and closing event pair (for
     * this request, for this group) 1) sent to this group 2) decision or now (if no decision) add this event pair (difference)
     * to the running total
     * 
     * @param request
     * @param group
     * @param initNDSReview
     * @return
     */
    @SuppressWarnings("unchecked")
    public static double getReviewElapsedDaysForNDS(final RequestWorkflow workflow, final Request request, final Group group,
            final boolean initNDSReview) {

        double reviewElapsedTime = 0;

        if (group != null && request != null && request.getActivity() != null) {

            String groupShortName = "";
            List<Event> eventListForGroup = new ArrayList<Event>();

            // TODO: find the event whose description matches what we're looking for (group and action) and return the date of
            // that event (for now, finding all events for this request and this group)
            if (group.getId() == Group.NDS.getId()) { // NDS review

                if (initNDSReview)
                    groupShortName = Review.INITIAL_NDS_GROUP;
                else
                    groupShortName = Review.FINAL_NDS_GROUP;

                // get the list of events for this request and for this group
                eventListForGroup = Event.listByRequestIdAndGroupOrder(request.getId(), group.getId(), initNDSReview);

            }

            if (eventListForGroup == null || eventListForGroup.size() == 0) // no events to step through
                return reviewElapsedTime;

            // sort the list by createdOn date
            Collections.sort(eventListForGroup, Event.getComparator());

            int endIndex = 0;
            int startIndex = 0;

            for (startIndex = 0; startIndex < eventListForGroup.size();) { // start at the earliest event

                double diff = 0; // elapsed time for this event pair

                int numEventsLookedAt = 1; // now adding the group to the initiated event (looking at an event)
                Event startEvent = eventListForGroup.get(startIndex);

                if (startEvent.isStartReviewEvent()) { // start event

                    boolean foundClose = false;
                    for (endIndex = startIndex + 1; endIndex < eventListForGroup.size(); endIndex++) { // step through the
                                                                                                       // remaining events

                        numEventsLookedAt++; // looking at an event
                        Event endEvent = eventListForGroup.get(endIndex);

                        // find the matching end event
                        if (endEvent.isEndReviewEvent()) { // end event

                            foundClose = true;

                            diff = calculatePartialDayDifference(startEvent.getCreatedOn(), endEvent.getCreatedOn());

                            break;
                        }
                    }

                    // no end event, so look for another final event
                    if (!foundClose) {

                        // has another review group denied this request?
                        Event denyEvent = findDenyEvent(workflow, request);
                        if (denyEvent != null) {
                            diff = calculatePartialDayDifference(startEvent.getCreatedOn(), denyEvent.getCreatedOn());
                            foundClose = true;
                        }

                    }

                    if (!foundClose) {

                        // has someone closed this request?
                        Event closeEvent = findCloseEvent(workflow, request);
                        if (closeEvent != null) {
                            diff = calculatePartialDayDifference(startEvent.getCreatedOn(), closeEvent.getCreatedOn());
                            foundClose = true;
                        }
                    }

                    if (!foundClose) {

                        // has this workflow been closed?
                        Event closeEvent = findWorkflowClosedEvent(workflow, request);
                        if (closeEvent != null) {
                            diff = calculatePartialDayDifference(startEvent.getCreatedOn(), closeEvent.getCreatedOn());
                            foundClose = true;
                        }

                    }
                    // no end event, so look for another final event

                    // no end event, so use the current date
                    if (!foundClose) {

                        // diff = calculateDifference( startEvent.getCreatedOn(), new Date() );
                        diff = calculatePartialDayDifference(startEvent.getCreatedOn(), new Date());

                    }

                    reviewElapsedTime += diff;

                }

                startIndex += numEventsLookedAt; // move past the end event that we just used (don't repeat events)
            } // end for -- start event

            log.debug("**TOTAL reviewer time so far (" + groupShortName + ") = " + reviewElapsedTime);
        } // end if

        return reviewElapsedTime;
    }

    /**
     * Returns the # of days elapsed between Reviewer opening and closing events: get the opening and closing event pair (for
     * this request, for this group) 1) sent to this group 2) decision or now (if no decision) add this event pair (difference)
     * to the running total
     * 
     * @param request
     * @param group
     * @param initNDSReview
     * @return
     */
    public static double getReviewElapsedDays(final Review review, final RequestWorkflow workflow, final Request request,
            final Group group, final boolean initNDSReview) {

        double reviewElapsedTime = 0;

        if (group != null && request != null && request.getActivity() != null) {

            if (group.getId() == Group.NDS.getId()) {

                reviewElapsedTime = getReviewElapsedDaysForNDS(workflow, request, group, initNDSReview);

            } else { // intermediate review group

                reviewElapsedTime = getReviewElapsedDaysForIntermediateGroups(workflow, request, group, review);
            }
        }

        return reviewElapsedTime;
    }

    @SuppressWarnings("unchecked")
    private static double getReviewElapsedDaysForIntermediateGroups(final RequestWorkflow workflow, Request request, Group group, Review review) {

        double reviewElapsedTime = 0;
        List<Event> events = null;
        if (request != null && group != null && review != null) {
            //events will have review id after select/deselect reviews epic is implemented.
            events = Event.listByRequestIdAndGroupIdAndReviewId(request.getId(), group.getId(), review.getId());
        }
        //if we can not find any events by review id then search for events prior to select and deseelect
        if (CollectionUtils.isEmpty(events)){
            events = Event.listByRequestIdAndGroupId(request.getId(), group.getId());
        }

        if (CollectionUtils.isNotEmpty(events)) {
            Collections.sort(events, Event.getComparator());
            Event startEvent = null;
            for (Event event : events) {
                if (event.isStartReviewEvent()) {
                    startEvent = event;
                } else {
                    if (startEvent != null) {
                        if (event.isEndReviewEvent()){
                            reviewElapsedTime += calculatePartialDayDifference( startEvent.getCreatedOn(), event.getCreatedOn() ); 
                            startEvent = null;
                            continue;
                        }
                        Event closeEvent = findCloseEvent(workflow, request);
                        if ( closeEvent != null){
                            reviewElapsedTime += calculatePartialDayDifference( startEvent.getCreatedOn(), closeEvent.getCreatedOn() ); 
                            startEvent = null;
                            continue;
                        }
                        Event denyEvent = findDenyEvent(workflow, request);
                        if (denyEvent != null){
                            reviewElapsedTime += calculatePartialDayDifference( startEvent.getCreatedOn(), denyEvent.getCreatedOn() ); 
                            startEvent = null;
                            continue;
                        }
                        Event workflowClosedEvent = findWorkflowClosedEvent(workflow, request);
                        if (workflowClosedEvent != null){
                            reviewElapsedTime += calculatePartialDayDifference( startEvent.getCreatedOn(), workflowClosedEvent.getCreatedOn() ); 
                            startEvent = null;
                            continue;
                        }     
                    } 
                }
            }
            if (startEvent != null){
                reviewElapsedTime += calculatePartialDayDifference( startEvent.getCreatedOn(), new Date() ); 
            }
        }

        return reviewElapsedTime;

    }

    private static Event findDenyEvent(final RequestWorkflow workflow, final Request request) {

        // TODO: if the request was closed or denied, use that as the final event for ANY group
        EventType.initialize();

        List<Event> denyEventList = Event.listByEventTypeAndRequestId(EventType.DENY_REVIEW.getId(), request.getId());
        if (denyEventList != null) {

            for (Event denyEvent : denyEventList) {
                if (denyEvent != null) {

                    Set<Group> denyGroupSet = denyEvent.getGroups();
                    if (denyGroupSet != null) {

                        for (Group denyGroup : denyGroupSet) {
                            if (denyGroup != null) {

                                // TODO: find the review for this group and this workflow (if there is a workflow)
                                for (Review review : request.getReviews(workflow)) {
                                    if (review != null && review.getReviewer() != null
                                            && review.getReviewer().getId() == denyGroup.getId()) {

                                        return denyEvent;

                                    }
                                }
                            }
                        }
                    }
                }
            }
        }

        return null;
    }

    private static Event findCloseEvent(final RequestWorkflow workflow, final Request request) {

        // TODO: if the request was closed or denied, use that as the final event for ANY group
        EventType.initialize();

        List<Event> closeEventList = Event.listByEventTypeAndRequestId(EventType.CLOSE_REQUEST.getId(), request.getId());
        if (!CollectionUtils.isEmpty(closeEventList)){
            return closeEventList.get(0);
            
        }

        return null;
    }

    private static Event findWorkflowClosedEvent(final RequestWorkflow workflow, final Request request) {

        // TODO: if the request was closed or denied, use that as the final event for ANY group
        EventType.initialize();

        List<Event> closeEventList = Event.listByEventTypeAndRequestId(EventType.CLOSE_WORKFLOW.getId(), request.getId());
        if (closeEventList != null) {

            for (Event closeEvent : closeEventList) {
                if (closeEvent != null) {

                    Set<Group> closeGroupSet = closeEvent.getGroups();
                    if (closeGroupSet != null) {

                        for (Group closeGroup : closeGroupSet) {
                            if (closeGroup != null) {

                                // TODO: find the review for this group and this workflow (if there is a workflow)
                                for (Review review : request.getReviews(workflow)) {
                                    if (review != null && review.getReviewer() != null
                                            && review.getReviewer().getId() == closeGroup.getId()) {

                                        return closeEvent;

                                    }
                                }
                            }
                        }
                    }
                }
            }
        }

        return null;
    }

    /**
     * Returns the # of days elapsed between the requestor events (per group) Requestor: initial NDS if changes requested ->
     * submit changes OR close OR now intermediate review if changes requested -> submit changes OR close OR now final NDS if
     * changes requested -> submit changes OR close OR now
     * 
     * @param request
     * @param group
     * @param initNDSReview
     * @return
     */
    @SuppressWarnings("unchecked")
    public static double getRequestorElapsedDaysForNDS(final RequestWorkflow workflow, final Request request, final Group group,
            final boolean initNDSReview) {

        double requestElapsedTime = 0;

        if (group != null && request != null && request.getActivity() != null) {

            String groupShortName = "";

            List<Event> eventListForGroup = new ArrayList<Event>();

            if (group.getId() == Group.NDS.getId()) { // NDS review

                if (initNDSReview)
                    groupShortName = Review.INITIAL_NDS_GROUP;
                else
                    groupShortName = Review.FINAL_NDS_GROUP;

                // get the list of events for this request and for this group
                eventListForGroup = Event.listByRequestIdAndGroupOrder(request.getId(), group.getId(), initNDSReview);

            } else { // intermediate review group

                // get the list of events for this request and for this group
                groupShortName = group.getShortName();
                eventListForGroup = Event.listByRequestIdAndGroupId(request.getId(), group.getId());
            } // end else

            if (eventListForGroup == null || eventListForGroup.size() == 0) // no events to step through
                return requestElapsedTime;

            // sort the list by createdOn date
            Collections.sort(eventListForGroup, Event.getComparator());

            int startIndex = 0;
            int endIndex = 0;

            for (startIndex = 0; startIndex < eventListForGroup.size();) { // start at the earliest event

                double diff = 0; // elapsed time for this event pair

                int numEventsLookedAt = 1; // now adding the group to the initiated event (looking at an event)
                Event startEvent = eventListForGroup.get(startIndex);

                if (startEvent.isStartRequestorEvent(group, initNDSReview)) { // start event

                    boolean foundClose = false;
                    for (endIndex = startIndex + 1; endIndex < eventListForGroup.size(); endIndex++) { // step through the
                                                                                                       // remaining events

                        numEventsLookedAt++; // looking at an event
                        Event endEvent = eventListForGroup.get(endIndex);

                        // find the matching end event (if change request, should contain the group name)
                        if (endEvent.isEndRequestorEvent(group, initNDSReview)) { // end event

                            foundClose = true;

                            // diff = calculateDifference( startEvent.getCreatedOn(), endEvent.getCreatedOn() );
                            diff = calculatePartialDayDifference(startEvent.getCreatedOn(), endEvent.getCreatedOn());

                            // System.out.println("\ndiff = " + diff + ", " +
                            // EventController.SDF.format(startEvent.getCreatedOn()) + " to " +
                            // EventController.SDF.format(endEvent.getCreatedOn()));

                            break;
                        } // end if -- end event
                    } // end for -- end event

                    // no end event, so look for another final event (requestor)
                    if (!foundClose) {

                        // has someone closed this request?
                        Event closeEvent = findCloseEvent(workflow, request);
                        if (closeEvent != null) {
                            diff = calculatePartialDayDifference(startEvent.getCreatedOn(), closeEvent.getCreatedOn());
                            foundClose = true;
                        } // end if

                    } // end if

                    if (!foundClose) {

                        // has this workflow been closed?
                        Event closeEvent = findWorkflowClosedEvent(workflow, request);
                        if (closeEvent != null) {
                            diff = calculatePartialDayDifference(startEvent.getCreatedOn(), closeEvent.getCreatedOn());
                            foundClose = true;
                        } // end if

                    } // end if
                      // no end event, so look for another final event (requestor)

                    // no end event, so use the current date
                    if (!foundClose) {
                        // diff = calculateDifference( startEvent.getCreatedOn(), new Date() );
                        diff = calculatePartialDayDifference(startEvent.getCreatedOn(), new Date());

                        // System.out.println("\ndiff = " + diff + ", " + EventController.SDF.format(startEvent.getCreatedOn())
                        // + " to now");
                    } // end if

                    // keep a running total of the elapsed time
                    requestElapsedTime += diff;
                    // System.out.println("time so far = " + requestElapsedTime);
                }

                startIndex += numEventsLookedAt; // move past the end event that we just used (don't repeat events)
            } // end for -- start event

            // System.out.println("**TOTAL requestor time so far (" + groupShortName + ") = " + requestElapsedTime);
            log.debug("**TOTAL requestor time so far (" + groupShortName + ") = " + requestElapsedTime);
        } // end if

        return requestElapsedTime;
    }

    public static double getRequestorElapsedDays(final Review review, final RequestWorkflow workflow, final Request request,
            final Group group, final boolean initNDSReview) {

        double requestElapsedTime = 0;

        if (group != null && request != null && request.getActivity() != null) {

            if (group.getId() == Group.NDS.getId()) { // NDS review

                requestElapsedTime = getRequestorElapsedDaysForNDS(workflow, request, group, initNDSReview);
            } else { // intermediate review group

                requestElapsedTime = getRequestorElapsedDaysForIntermediateGroups(workflow, request, group, review);
            }
        }

        return requestElapsedTime;
    }

    @SuppressWarnings("unchecked")
    private static double getRequestorElapsedDaysForIntermediateGroups(final RequestWorkflow workflow, Request request, Group group, Review review) {
        double reviewElapsedTime = 0;
        List<Event> events = null;

        //if we can not find any events by review id then search for events prior to select and deselect
        if (CollectionUtils.isEmpty(events)){
            events = Event.listByRequestIdAndGroupId(request.getId(), group.getId());
        }

        if (CollectionUtils.isNotEmpty(events)) {
            Collections.sort(events, Event.getComparator());
            Event startEvent = null;
            for (Event event : events) {

                //if we have found a different review id then break out of the loop 
                if ((event.getReview() != null && event.getReview().getId() != review.getId())  )
                {
                    continue;
                }
                
                if (event.isStartRequestorEvent()) {
                    startEvent = event;
                } else {
                    if (startEvent != null) {
                        if (event.isEndRequestorEvent()){
                            reviewElapsedTime += calculatePartialDayDifference( startEvent.getCreatedOn(), event.getCreatedOn() ); 
                            startEvent = null;
                            continue;
                        }
                        Event closeEvent = findCloseEvent(workflow, request);
                        if ( closeEvent != null){
                            reviewElapsedTime += calculatePartialDayDifference( startEvent.getCreatedOn(), closeEvent.getCreatedOn() ); 
                            startEvent = null;
                            continue;
                        }
                        Event denyEvent = findDenyEvent(workflow, request);
                        if (denyEvent != null){
                            reviewElapsedTime += calculatePartialDayDifference( startEvent.getCreatedOn(), denyEvent.getCreatedOn() ); 
                            startEvent = null;
                            continue;
                        }
                        Event workflowClosedEvent = findWorkflowClosedEvent(workflow, request);
                        if (workflowClosedEvent != null){
                            reviewElapsedTime += calculatePartialDayDifference( startEvent.getCreatedOn(), workflowClosedEvent.getCreatedOn() ); 
                            startEvent = null;
                            continue;
                        }  
                    } 
                }
            }
            if (startEvent != null){
                reviewElapsedTime += calculatePartialDayDifference( startEvent.getCreatedOn(), new Date() ); 
            }
        }

        return reviewElapsedTime;
    }

    /**
     * Returns the given group name without any spaces in it. Used for the Median Wait Time UI controls.
     * 
     * @param groupName
     * @return
     */
    public static String getGroupNameWithoutSpaces(final String groupName) {

        if (groupName != null) {
            return (groupName.replaceAll("\\s", ""));
        }

        return groupName;
    }

    /**
     * Convert between the review status string and the UI status value (style info). Used by the Median Wait Time display.
     * 
     * @param status
     * @return
     */
    public static String getStatusClassName(final String status) {

        String statusClassName = "";

        // TODO: could update the JSON to return a boolean (for each status) and then move this to the template
        if (status.equalsIgnoreCase(RequestStatus.APPROVED.getName()))
            statusClassName = "approved";
        else if (status.equalsIgnoreCase(RequestStatus.DENIED.getName()))
            statusClassName = "denied";
        else if (status.equalsIgnoreCase(RequestStatus.CHANGE_REQUESTED.getName()))
            statusClassName = "change";
        else if (status.equalsIgnoreCase(Review.WAITING_FOR_REVIEW))
            statusClassName = "waiting";
        else if (status.equalsIgnoreCase(Review.WITHDRAWN))
            statusClassName = "withdrawn";

        return statusClassName;
    }

    public static int calculateDifference(Date a, Date b) {
        int tempDifference = 0;
        int difference = 0;
        Calendar earlier = Calendar.getInstance();
        Calendar later = Calendar.getInstance();

        if (a.compareTo(b) < 0) {
            earlier.setTime(a);
            later.setTime(b);
        } else {
            earlier.setTime(b);
            later.setTime(a);
        }

        while (earlier.get(Calendar.YEAR) != later.get(Calendar.YEAR)) {
            tempDifference = 365 * (later.get(Calendar.YEAR) - earlier.get(Calendar.YEAR));
            difference += tempDifference;

            earlier.add(Calendar.DAY_OF_YEAR, tempDifference);
        }

        if (earlier.get(Calendar.DAY_OF_YEAR) != later.get(Calendar.DAY_OF_YEAR)) {
            tempDifference = later.get(Calendar.DAY_OF_YEAR) - earlier.get(Calendar.DAY_OF_YEAR);
            difference += tempDifference;

            earlier.add(Calendar.DAY_OF_YEAR, tempDifference);
        }

        return difference;
    }

    public static double calculatePartialDayDifference(Date a, Date b) {
        int tempDifference = 0;
        double difference = 0;
        Calendar earlier = Calendar.getInstance();
        Calendar later = Calendar.getInstance();

        if (a.compareTo(b) < 0) {
            earlier.setTime(a);
            later.setTime(b);
        } else {
            earlier.setTime(b);
            later.setTime(a);
        }

        while (earlier.get(Calendar.YEAR) != later.get(Calendar.YEAR)) {
            tempDifference = 365 * (later.get(Calendar.YEAR) - earlier.get(Calendar.YEAR));
            difference += tempDifference;

            earlier.add(Calendar.DAY_OF_YEAR, tempDifference);
        }

        if (earlier.get(Calendar.DAY_OF_YEAR) != later.get(Calendar.DAY_OF_YEAR)) {
            tempDifference = later.get(Calendar.DAY_OF_YEAR) - earlier.get(Calendar.DAY_OF_YEAR);
            difference += tempDifference;

            earlier.add(Calendar.DAY_OF_YEAR, tempDifference);
        } else { // if this difference is less than one day, show a floating point value (partial day)
            long laterMillisec = later.getTimeInMillis();
            long earlierMillisec = earlier.getTimeInMillis();

            // TODO: might want to show this in 8-hour work day? (had planned to move this into the query)
            long tempDiffMillisec = laterMillisec - earlierMillisec;
            difference = tempDiffMillisec / (24.0 * 60 * 60 * 1000); // get the partial day amount
        }

        return difference;
    }

}
