// Copyright 2008 by Basil Vandegriend.  All rights reserved.

package com.basilv.resumecalculator;

import java.util.*;

import org.joda.time.Duration;
import org.joda.time.Interval;

import com.basilv.core.Assert;

/**
 * Calculates the duration of selected entries of a resume. 
 */
public class ResumeDurationCalculator
{
	/**
	 * Calculate the total duration of the supplied resume and list of entry ids.
	 * @param resume Cannot be null.
	 * @param entryIds Cannot be null. Must correspond to ids within the resume.
	 * @return the duration. Will not be null.
	 */
	public Duration calculateDurationOfEntriesInResume(Resume resume, List<String> entryIds) {
		Assert.notNull("resume", resume);
		Assert.notNull("entryIds", entryIds);

		List<ResumeEntry> resumeEntries = new ArrayList<ResumeEntry>();
		for (String entryId : entryIds) {
			ResumeEntry entry = resume.getEntry(entryId);
			if (entry == null) {
				throw new IllegalArgumentException("Supplied entry id [" + entryId + "] does not exist in the supplied resume.");
			}
			resumeEntries.add(entry);
		}

		return calculateDurationOfEntries(resumeEntries);
	}
	
	/**
	 * Calculate the total combined duration of the supplied resume entries.
	 * @param resumeEntries Cannot be null. May be empty.
	 * @return the duration. Will not be null.
	 */
	Duration calculateDurationOfEntries(List<ResumeEntry> resumeEntries) {
		Assert.notNull("resumeEntries", resumeEntries);
		
		// Algorithm: given list of intervals, construct a list of non-overlapping intervals
		// so their durations can be directly summed.
		// Iterate through list, checking for overlap between current element and predecessors in list.
		// If there is overlap, remove predecessor, union the two intervals, and start over with the iteration.
		
		List<Interval> inputIntervals = new ArrayList<Interval>();
		for (ResumeEntry entry : resumeEntries) {
			inputIntervals.add(entry.getDateInterval());
		}
		
		List<Interval> nonOverlappingIntervals = new LinkedList<Interval>();

		for (Interval inputInterval : inputIntervals) {
			boolean doneWithInputInterval = true;
			do {
				doneWithInputInterval = true;
				for (ListIterator<Interval> nonOverlappingIterator = nonOverlappingIntervals.listIterator(); 
					nonOverlappingIterator.hasNext(); ) {
					
					Interval nonOverlappingInterval = nonOverlappingIterator.next();
					
					if (inputInterval.overlaps(nonOverlappingInterval)) {
						
						inputInterval = unionOverlappingIntervals(inputInterval, nonOverlappingInterval);
						
						// Remove existing overlap interval as it is contained in new input interval
						assert inputInterval.contains(nonOverlappingInterval);
						nonOverlappingIterator.remove();

						// Need to loop back through all the other non-overlapping intervals, 
						// so not done with current input interval and abort current loop. 
						doneWithInputInterval = false;

						break;
					} 
				}
				
				if (doneWithInputInterval) {
					// Had no overlaps, so add to list
					nonOverlappingIntervals.add(inputInterval);
				}
				
			} while (! doneWithInputInterval);
		}
		
		Duration duration = Duration.ZERO;
		for (Interval interval : nonOverlappingIntervals) {
			duration = duration.plus(interval.toDuration());	
		}
		
		return duration;
	}

	private Interval unionOverlappingIntervals(Interval inputInterval, Interval nonOverlappingInterval) {
		long start = Math.min(inputInterval.getStartMillis(), nonOverlappingInterval.getStartMillis());
		long end = Math.max(inputInterval.getEndMillis(), nonOverlappingInterval.getEndMillis());
		Interval unionInterval = new Interval(start, end);
		return unionInterval;
	}

}
