<commit_before>/*******************************************************************************
 * Copyright (c) 2004 - 2006 University Of British Columbia and others.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *     University Of British Columbia - initial API and implementation
 *******************************************************************************/
/*
 * Created on Dec 26, 2004
 */
package org.eclipse.mylar.tasks.ui;

import java.io.File;
import java.net.URLDecoder;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.ConcurrentHashMap;

import org.eclipse.core.runtime.Platform;
import org.eclipse.jface.util.IPropertyChangeListener;
import org.eclipse.jface.util.PropertyChangeEvent;
import org.eclipse.mylar.context.core.ContextCorePlugin;
import org.eclipse.mylar.context.core.IMylarContext;
import org.eclipse.mylar.context.core.IMylarContextListener;
import org.eclipse.mylar.context.core.IMylarElement;
import org.eclipse.mylar.core.MylarStatusHandler;
import org.eclipse.mylar.internal.context.core.MylarContextManager;
import org.eclipse.mylar.internal.tasks.core.RepositoryTaskHandleUtil;
import org.eclipse.mylar.internal.tasks.core.WebTask;
import org.eclipse.mylar.internal.tasks.ui.TaskListPreferenceConstants;
import org.eclipse.mylar.internal.tasks.ui.WorkspaceAwareContextStore;
import org.eclipse.mylar.internal.tasks.ui.util.TaskListSaveManager;
import org.eclipse.mylar.internal.tasks.ui.util.TaskListWriter;
import org.eclipse.mylar.internal.tasks.ui.views.TaskActivationHistory;
import org.eclipse.mylar.monitor.core.InteractionEvent;
import org.eclipse.mylar.tasks.core.AbstractQueryHit;
import org.eclipse.mylar.tasks.core.AbstractRepositoryTask;
import org.eclipse.mylar.tasks.core.AbstractTaskContainer;
import org.eclipse.mylar.tasks.core.DateRangeActivityDelegate;
import org.eclipse.mylar.tasks.core.DateRangeContainer;
import org.eclipse.mylar.tasks.core.ITask;
import org.eclipse.mylar.tasks.core.ITaskActivityListener;
import org.eclipse.mylar.tasks.core.ITaskListChangeListener;
import org.eclipse.mylar.tasks.core.ITaskListElement;
import org.eclipse.mylar.tasks.core.TaskList;
import org.eclipse.mylar.tasks.core.TaskRepository;
import org.eclipse.mylar.tasks.core.TaskRepositoryManager;

/**
 * TODO: pull task activity management out into new TaskActivityManager
 * 
 * @author <NAME>
 * @author <NAME> (task activity)
 */
public class TaskListManager implements IPropertyChangeListener {

	// Did have preference for this so should re-enable
	private static final int START_HOUR = 8;

	private static final long SECOND = 1000;

	private static final long MINUTE = 60 * SECOND;

	private static final long ROLLOVER_DELAY = 30 * MINUTE;

	private static final int NUM_WEEKS_PREVIOUS = -1;

	private static final int NUM_WEEKS_NEXT = 1;

	private static final int NUM_WEEKS_FUTURE_START = 2;

	private static final int NUM_WEEKS_FUTURE_END = 8;

	private static final int NUM_WEEKS_PAST_START = -8;

	private static final int NUM_WEEKS_PAST_END = -2;

	public static final String ARCHIVE_CATEGORY_DESCRIPTION = "Archive";

	private static final String DESCRIPTION_THIS_WEEK = "This Week";

	private static final String DESCRIPTION_PREVIOUS_WEEK = "Previous Week";

	private static final String DESCRIPTION_NEXT_WEEK = "Next Week";

	private static final String DESCRIPTION_FUTURE = "Future";

	private static final String DESCRIPTION_PAST = "Past";

	public static final String[] ESTIMATE_TIMES = new String[] { "0 Hours", "1 Hours", "2 Hours", "3 Hours", "4 Hours",
			"5 Hours", "6 Hours", "7 Hours", "8 Hours", "9 Hours", "10 Hours" };

	private DateRangeContainer activityPreviousWeek;

	private DateRangeContainer activityThisWeek;

	private List<DateRangeContainer> activityWeekDays = new ArrayList<DateRangeContainer>();

	private DateRangeContainer activityNextWeek;

	private DateRangeContainer activityFuture;

	private DateRangeContainer activityPast;

	private ArrayList<DateRangeContainer> dateRangeContainers = new ArrayList<DateRangeContainer>();

	private Set<ITask> tasksWithReminders = new HashSet<ITask>();

	private Set<ITask> tasksWithDueDates = new HashSet<ITask>();

	private ITask currentTask = null;

	private String currentHandle = "";

	private Calendar currentTaskStart = null;

	private Calendar currentTaskEnd = null;

	private Map<ITask, Long> taskElapsedTimeMap = new ConcurrentHashMap<ITask, Long>();

	private List<ITaskActivityListener> activityListeners = new ArrayList<ITaskActivityListener>();

	private TaskListWriter taskListWriter;

	private File taskListFile;

	private TaskListSaveManager taskListSaveManager;

	// TODO: guard against overwriting the single instance?
	private TaskList taskList = new TaskList();

	private TaskActivationHistory taskActivityHistory = new TaskActivationHistory();

	private boolean taskListInitialized = false;

	private boolean taskActivityHistoryInitialized = false;

	private int startDay;

	private int endDay;

	private int scheduledEndHour;

	private Timer timer;

	/** public for testing */
	public Date startTime = new Date();

	private final IMylarContextListener CONTEXT_LISTENER = new IMylarContextListener() {

		public void contextActivated(IMylarContext context) {
			// ignore
		}

		public void contextDeactivated(IMylarContext context) {
			// ignore
		}

		public void presentationSettingsChanging(UpdateKind kind) {
			// ignore
		}

		public void presentationSettingsChanged(UpdateKind kind) {
			// ignore
		}

		public void interestChanged(List<IMylarElement> elements) {
			List<InteractionEvent> events = ContextCorePlugin.getContextManager().getActivityHistoryMetaContext()
					.getInteractionHistory();
			InteractionEvent event = events.get(events.size() - 1);
			parseInteractionEvent(event);

		}

		public void elementDeleted(IMylarElement element) {
			// ignore
		}

		public void landmarkAdded(IMylarElement element) {
			// ignore
		}

		public void landmarkRemoved(IMylarElement element) {
			// ignore
		}

		public void relationsChanged(IMylarElement element) {
			// ignore
		}
	};

	private final ITaskListChangeListener CHANGE_LISTENER = new ITaskListChangeListener() {

		public void containerAdded(AbstractTaskContainer container) {
		}

		public void containerDeleted(AbstractTaskContainer container) {
		}

		public void containerInfoChanged(AbstractTaskContainer container) {
		}

		public void localInfoChanged(ITask task) {
		}

		public void repositoryInfoChanged(ITask task) {
		}

		public void taskAdded(ITask task) {
		}

		public void taskDeleted(ITask task) {
			TaskListManager.this.resetAndRollOver();
		}

		public void taskMoved(ITask task, AbstractTaskContainer fromContainer, AbstractTaskContainer toContainer) {
		}

	};

	private int timeTicks = 0;

	public TaskListManager(TaskListWriter taskListWriter, File file) {
		this.taskListFile = file;
		this.taskListWriter = taskListWriter;

		timer = new Timer();
		timer.schedule(new RolloverCheck(), ROLLOVER_DELAY, ROLLOVER_DELAY);
		taskList.addChangeListener(CHANGE_LISTENER);
	}

	public void init() {
		ContextCorePlugin.getContextManager().addActivityMetaContextListener(CONTEXT_LISTENER);
	}

	public void dispose() {
		ContextCorePlugin.getContextManager().removeActivityMetaContextListener(CONTEXT_LISTENER);
	}

	public TaskList resetTaskList() {
		resetActivity();
		taskList.reset();
		taskListInitialized = true;
		return taskList;
	}

	private void resetActivity() {
		taskElapsedTimeMap.clear();
		dateRangeContainers.clear();
		setupCalendarRanges();
	}

	/**
	 * Warning: if called twice task times will be wrong
	 */
	private void parseTaskActivityInteractionHistory() {
		if (!TasksUiPlugin.getTaskListManager().isTaskListInitialized()) {
			return;
		}
		List<InteractionEvent> events = ContextCorePlugin.getContextManager().getActivityHistoryMetaContext()
				.getInteractionHistory();
		for (InteractionEvent event : events) {
			parseInteractionEvent(event);
		}
		taskActivityHistoryInitialized = true;
		parseFutureReminders();
	}

	public void parseFutureReminders() {
		activityFuture.clear();
		activityNextWeek.clear();

		for (DateRangeContainer day : activityWeekDays) {
			day.clear();
		}
		HashSet<ITask> toRemove = new HashSet<ITask>();
		toRemove.addAll(activityThisWeek.getDateRangeDelegates());
		for (ITask activity : toRemove) {
			DateRangeActivityDelegate delegate = (DateRangeActivityDelegate) activity;
			Calendar calendar = GregorianCalendar.getInstance();

			Date schedDate = delegate.getDueDate();

			if (schedDate == null) {
				schedDate = delegate.getScheduledForDate();
				// schedDate = delegate.getDueDate();
			}

			if (schedDate != null) {
				calendar.setTime(schedDate);
				if (!activityThisWeek.includes(calendar) && activityThisWeek.getElapsed(delegate) == 0) {
					activityThisWeek.remove(delegate);
				}
			} else {
				if (activityThisWeek.getElapsed(delegate) == 0) {
					activityThisWeek.remove(delegate);
				}
			}
		}
		GregorianCalendar tempCalendar = new GregorianCalendar();
		tempCalendar.setFirstDayOfWeek(startDay);

		Set<ITask> allScheduledTasks = new HashSet<ITask>();
		allScheduledTasks.addAll(tasksWithReminders);
		allScheduledTasks.addAll(tasksWithDueDates);

		for (ITask task : allScheduledTasks) {
			if (task instanceof DateRangeActivityDelegate) {
				task = ((DateRangeActivityDelegate) task).getCorrespondingTask();
			}

			Date schedDate = task.getDueDate();
			if (schedDate == null) {
				schedDate = task.getScheduledForDate();
			}

			if (schedDate != null) {
				tempCalendar.setTime(schedDate);
				if (activityNextWeek.includes(tempCalendar)) {
					activityNextWeek.addTask(new DateRangeActivityDelegate(activityNextWeek, task, tempCalendar,
							tempCalendar));
				} else if (activityFuture.includes(tempCalendar)) {
					activityFuture.addTask(new DateRangeActivityDelegate(activityFuture, task, tempCalendar,
							tempCalendar));
				} else if (activityThisWeek.includes(tempCalendar) && !activityThisWeek.getChildren().contains(task)) {
					activityThisWeek.addTask(new DateRangeActivityDelegate(activityThisWeek, task, tempCalendar,
							tempCalendar));
				} else if (activityPreviousWeek.includes(tempCalendar)
						&& !activityPreviousWeek.getChildren().contains(task)) {
					activityPreviousWeek.addTask(new DateRangeActivityDelegate(activityPreviousWeek, task,
							tempCalendar, tempCalendar));
				} else if (activityPast.includes(tempCalendar) && !activityPast.getChildren().contains(task)) {
					activityPast.addTask(new DateRangeActivityDelegate(activityPast, task, tempCalendar, tempCalendar));
				}

				for (DateRangeContainer day : activityWeekDays) {
					if (day.includes(tempCalendar) && !day.getChildren().contains(task)) {
						day.addTask(new DateRangeActivityDelegate(day, task, tempCalendar, tempCalendar, this
								.getElapsedTime(task)));
					}
				}
			}
		}
	}

	/** public for testing * */
	public void parseInteractionEvent(InteractionEvent event) {

		if (event.getDelta().equals(MylarContextManager.ACTIVITY_DELTA_ACTIVATED)) {
			if (!event.getStructureHandle().equals(MylarContextManager.ACTIVITY_HANDLE_ATTENTION)) {

				ITask activatedTask = TasksUiPlugin.getTaskListManager().getTaskList().getTask(
						event.getStructureHandle());

				if (currentTask != null && activatedTask != null) {
					if (!currentTask.equals(activatedTask)) {

						GregorianCalendar calendarEnd = new GregorianCalendar();
						calendarEnd.setFirstDayOfWeek(startDay);
						calendarEnd.setTime(event.getDate());
						calendarEnd.getTime();
						// Activation of different task before deactivation of
						// previous, log was inconsistent,
						// finish what we started
						taskDeactivated(calendarEnd);
					} else {
						// skip re-activations of same task
						return;
					}
				}

				currentTask = activatedTask;
				if (currentTask != null) {
					GregorianCalendar calendar = new GregorianCalendar();
					calendar.setFirstDayOfWeek(startDay);
					calendar.setTime(event.getDate());
					currentTaskStart = calendar;
					currentHandle = event.getStructureHandle();
				}
			} else if (event.getStructureHandle().equals(MylarContextManager.ACTIVITY_HANDLE_ATTENTION)) {
				if (!currentHandle.equals("")) {
					long active = event.getEndDate().getTime() - event.getDate().getTime();

					// add to running total
					if (taskElapsedTimeMap.containsKey(currentTask)) {
						long pastTime = taskElapsedTimeMap.get(currentTask);
						taskElapsedTimeMap.put(currentTask, pastTime + active);
						timeTicks++;
						if (taskActivityHistoryInitialized && timeTicks > 3) {
							// Save incase of system failure.
							// TODO: request asynchronous save
							ContextCorePlugin.getContextManager().saveActivityHistoryContext();
							timeTicks = 0;
						}

					} else {
						taskElapsedTimeMap.put(currentTask, active);
					}

				}

			}
		} else if (event.getDelta().equals(MylarContextManager.ACTIVITY_DELTA_DEACTIVATED)) {
			if (!event.getStructureHandle().equals(MylarContextManager.ACTIVITY_HANDLE_ATTENTION)
					&& currentHandle.equals(event.getStructureHandle())) {
				GregorianCalendar calendarEnd = new GregorianCalendar();
				calendarEnd.setFirstDayOfWeek(startDay);
				calendarEnd.setTime(event.getDate());
				calendarEnd.getTime();
				taskDeactivated(calendarEnd);
			} else if (event.getStructureHandle().equals(MylarContextManager.ACTIVITY_HANDLE_ATTENTION)) {
				// Deactivated attention events not currently used (ignored)
			}
		}
	}

	private void taskDeactivated(GregorianCalendar calendarEnd) {
		currentTaskEnd = calendarEnd;

		Set<DateRangeContainer> rangeSet = new HashSet<DateRangeContainer>();
		rangeSet.addAll(dateRangeContainers);
		rangeSet.add(activityThisWeek);

		for (DateRangeContainer week : rangeSet) {
			if (week.includes(currentTaskStart) && (!isWeekDay(week) && !week.isFuture())) {
				if (currentTask != null) {
					// add to date range 'bin'
					Long activeTime = taskElapsedTimeMap.get(currentTask);
					if (activeTime == null) {
						activeTime = new Long(0);
					}
					DateRangeActivityDelegate delegate = new DateRangeActivityDelegate(week, currentTask,
							currentTaskStart, currentTaskEnd, activeTime);
					week.addTask(delegate);
					if (taskActivityHistoryInitialized) {
						for (ITaskActivityListener listener : activityListeners) {
							listener.activityChanged(week);
						}
					}
				}
			}
		}

		currentTask = null;
		currentHandle = "";
	}

	/** public for testing * */
	public DateRangeContainer getActivityThisWeek() {
		return activityThisWeek;
	}

	/** public for testing * */
	public DateRangeContainer getActivityPast() {
		return activityPast;
	}

	/** public for testing * */
	public DateRangeContainer getActivityFuture() {
		return activityFuture;
	}

	/** public for testing * */
	public DateRangeContainer getActivityNextWeek() {
		return activityNextWeek;
	}

	/** public for testing * */
	public DateRangeContainer getActivityPrevious() {
		return activityPreviousWeek;
	}

	/** total elapsed time based on activation history */
	public long getElapsedTime(ITask task) {
		if (taskElapsedTimeMap.containsKey(task)) {
			return taskElapsedTimeMap.get(task);
		} else {
			return 0;
		}
	}

	private void setupCalendarRanges() {
		// MylarTaskListPlugin.getMylarCorePrefs().getInt(TaskListPreferenceConstants.PLANNING_STARTDAY);
		startDay = Calendar.MONDAY;
		// MylarTaskListPlugin.getMylarCorePrefs().getInt(TaskListPreferenceConstants.PLANNING_ENDDAY);
		endDay = Calendar.SUNDAY;
		// scheduledStartHour =
		// TasksUiPlugin.getDefault().getPreferenceStore().getInt(
		// TaskListPreferenceConstants.PLANNING_STARTHOUR);
		scheduledEndHour = TasksUiPlugin.getDefault().getPreferenceStore().getInt(
				TaskListPreferenceConstants.PLANNING_ENDHOUR);

		activityWeekDays.clear();
		for (int x = startDay; x < (startDay + 7); x++) {
			GregorianCalendar dayStart = new GregorianCalendar();
			GregorianCalendar dayEnd = new GregorianCalendar();
			dayStart.setFirstDayOfWeek(startDay);
			dayEnd.setFirstDayOfWeek(startDay);
			if (x > 7) {
				dayStart.set(Calendar.DAY_OF_WEEK, x % 7);
				dayEnd.set(Calendar.DAY_OF_WEEK, x % 7);
			} else {
				dayStart.set(Calendar.DAY_OF_WEEK, x);
				dayEnd.set(Calendar.DAY_OF_WEEK, x);
			}

			dayStart.set(Calendar.HOUR_OF_DAY, 0);
			dayStart.set(Calendar.MINUTE, 0);
			dayStart.set(Calendar.SECOND, 0);
			dayStart.set(Calendar.MILLISECOND, 0);
			dayStart.getTime();

			dayEnd.set(Calendar.HOUR_OF_DAY, dayEnd.getMaximum(Calendar.HOUR_OF_DAY));
			dayEnd.set(Calendar.MINUTE, dayEnd.getMaximum(Calendar.MINUTE));
			dayEnd.set(Calendar.SECOND, dayEnd.getMaximum(Calendar.SECOND));
			dayEnd.set(Calendar.MILLISECOND, dayEnd.getMaximum(Calendar.MILLISECOND));
			dayEnd.getTime();

			String summary = "<unknown>";
			switch (dayStart.get(Calendar.DAY_OF_WEEK)) {
			case Calendar.MONDAY:
				summary = "Monday";
				break;
			case Calendar.TUESDAY:
				summary = "Tuesday";
				break;
			case Calendar.WEDNESDAY:
				summary = "Wednesday";
				break;
			case Calendar.THURSDAY:
				summary = "Thursday";
				break;
			case Calendar.FRIDAY:
				summary = "Friday";
				break;
			case Calendar.SATURDAY:
				summary = "Saturday";
				break;
			case Calendar.SUNDAY:
				summary = "Sunday";
				break;
			}
			DateRangeContainer day = new DateRangeContainer(dayStart, dayEnd, summary, taskList);
			activityWeekDays.add(day);
			dateRangeContainers.add(day);
		}

		GregorianCalendar currentBegin = new GregorianCalendar();
		currentBegin.setFirstDayOfWeek(startDay);
		currentBegin.setTime(startTime);
		snapToStartOfWeek(currentBegin);
		GregorianCalendar currentEnd = new GregorianCalendar();
		currentEnd.setFirstDayOfWeek(startDay);
		currentEnd.setTime(startTime);
		snapToEndOfWeek(currentEnd);
		activityThisWeek = new DateRangeContainer(currentBegin, currentEnd, DESCRIPTION_THIS_WEEK, taskList);
		// dateRangeContainers.add(activityThisWeek);

		GregorianCalendar previousStart = new GregorianCalendar();
		previousStart.setFirstDayOfWeek(startDay);
		previousStart.setTime(startTime);
		previousStart.add(Calendar.WEEK_OF_YEAR, NUM_WEEKS_PREVIOUS);
		snapToStartOfWeek(previousStart);
		GregorianCalendar previousEnd = new GregorianCalendar();
		previousEnd.setFirstDayOfWeek(startDay);
		previousEnd.setTime(startTime);
		previousEnd.add(Calendar.WEEK_OF_YEAR, NUM_WEEKS_PREVIOUS);
		snapToEndOfWeek(previousEnd);
		activityPreviousWeek = new DateRangeContainer(previousStart.getTime(), previousEnd.getTime(),
				DESCRIPTION_PREVIOUS_WEEK, taskList);
		dateRangeContainers.add(activityPreviousWeek);

		GregorianCalendar nextStart = new GregorianCalendar();
		nextStart.setFirstDayOfWeek(startDay);
		nextStart.setTime(startTime);
		nextStart.add(Calendar.WEEK_OF_YEAR, NUM_WEEKS_NEXT);
		snapToStartOfWeek(nextStart);
		GregorianCalendar nextEnd = new GregorianCalendar();
		nextEnd.setFirstDayOfWeek(startDay);
		nextEnd.setTime(startTime);
		nextEnd.add(Calendar.WEEK_OF_YEAR, NUM_WEEKS_NEXT);
		snapToEndOfWeek(nextEnd);
		activityNextWeek = new DateRangeContainer(nextStart.getTime(), nextEnd.getTime(), DESCRIPTION_NEXT_WEEK,
				taskList);
		dateRangeContainers.add(activityNextWeek);

		GregorianCalendar futureStart = new GregorianCalendar();
		futureStart.setFirstDayOfWeek(startDay);
		futureStart.setTime(startTime);
		futureStart.add(Calendar.WEEK_OF_YEAR, NUM_WEEKS_FUTURE_START);
		snapToStartOfWeek(futureStart);
		GregorianCalendar futureEnd = new GregorianCalendar();
		futureEnd.setFirstDayOfWeek(startDay);
		futureEnd.setTime(startTime);
		futureEnd.add(Calendar.WEEK_OF_YEAR, NUM_WEEKS_FUTURE_END);
		snapToEndOfWeek(futureEnd);
		activityFuture = new DateRangeContainer(futureStart.getTime(), futureEnd.getTime(), DESCRIPTION_FUTURE,
				taskList);
		dateRangeContainers.add(activityFuture);

		GregorianCalendar pastStart = new GregorianCalendar();
		pastStart.setFirstDayOfWeek(startDay);
		pastStart.setTime(startTime);
		pastStart.add(Calendar.WEEK_OF_YEAR, NUM_WEEKS_PAST_START);
		snapToStartOfWeek(pastStart);
		GregorianCalendar pastEnd = new GregorianCalendar();
		pastEnd.setFirstDayOfWeek(startDay);
		pastEnd.setTime(startTime);
		pastEnd.add(Calendar.WEEK_OF_YEAR, NUM_WEEKS_PAST_END);
		snapToEndOfWeek(pastEnd);
		activityPast = new DateRangeContainer(pastStart.getTime(), pastEnd.getTime(), DESCRIPTION_PAST, taskList);
		dateRangeContainers.add(activityPast);
	}

	public void snapToNextDay(Calendar cal) {
		cal.add(Calendar.DAY_OF_MONTH, 1);
		cal.set(Calendar.HOUR_OF_DAY, 0);
		cal.set(Calendar.MINUTE, 0);
		cal.set(Calendar.SECOND, 0);
		cal.set(Calendar.MILLISECOND, 0);
		cal.getTime();
	}

	private void snapToStartOfWeek(Calendar cal) {
		cal.getTime();
		cal.set(Calendar.DAY_OF_WEEK, startDay);
		cal.set(Calendar.HOUR_OF_DAY, 0);
		cal.set(Calendar.MINUTE, 0);
		cal.set(Calendar.SECOND, 0);
		cal.set(Calendar.MILLISECOND, 0);
		cal.getTime();
	}

	private void snapToEndOfWeek(Calendar cal) {
		cal.getTime();
		cal.set(Calendar.DAY_OF_WEEK, endDay);
		cal.set(Calendar.HOUR_OF_DAY, cal.getMaximum(Calendar.HOUR_OF_DAY));
		cal.set(Calendar.MINUTE, cal.getMaximum(Calendar.MINUTE));
		cal.set(Calendar.SECOND, cal.getMaximum(Calendar.SECOND));
		cal.set(Calendar.MILLISECOND, cal.getMaximum(Calendar.MILLISECOND));
		cal.getTime();
	}

	public Calendar setSecheduledIn(Calendar calendar, int days) {
		calendar.add(Calendar.DAY_OF_MONTH, days);
		calendar.set(Calendar.HOUR_OF_DAY, scheduledEndHour);
		calendar.set(Calendar.MINUTE, 0);
		calendar.set(Calendar.SECOND, 0);
		calendar.set(Calendar.MILLISECOND, 0);
		return calendar;
	}

	/**
	 * Will schedule for today if past work-day's end.
	 */
	public Calendar setScheduledEndOfDay(Calendar calendar) {
		// Calendar now = Calendar.getInstance();
		// if (now.get(Calendar.HOUR_OF_DAY) >= scheduledEndHour) {
		// setSecheduledIn(calendar, 1);
		// }
		calendar.set(Calendar.HOUR_OF_DAY, scheduledEndHour);
		calendar.set(Calendar.MINUTE, 0);
		calendar.set(Calendar.SECOND, 0);
		calendar.set(Calendar.MILLISECOND, 0);
		return calendar;
	}

	public void setScheduledNextWeek(Calendar calendar) {
		calendar.setTimeInMillis(TasksUiPlugin.getTaskListManager().getActivityNextWeek().getStart().getTimeInMillis());
		setScheduledEndOfDay(calendar);
	}

	public List<DateRangeContainer> getDateRanges() {
		// parseFutureReminders();
		// return dateRangeContainers.toArray(new
		// ITaskListElement[dateRangeContainers.size()]);
		return dateRangeContainers;
	}

	/**
	 * Every call to this method generates a unique handle, subsequent calls
	 * will have incremented task numbers
	 */
	public String genUniqueTaskHandle() {
		return TaskRepositoryManager.PREFIX_LOCAL + taskList.getNextTaskNum();
	}

	public void refactorRepositoryUrl(String oldUrl, String newUrl) {
		if (oldUrl == null || newUrl == null || oldUrl.equals(newUrl)) {
			return;
		}
		List<ITask> activeTasks = taskList.getActiveTasks();
		for (ITask task : new ArrayList<ITask>(activeTasks)) {
			deactivateTask(task);
		}
		taskList.refactorRepositoryUrl(oldUrl, newUrl);

		File dataDir = new File(TasksUiPlugin.getDefault().getDataDirectory(),
				WorkspaceAwareContextStore.CONTEXTS_DIRECTORY);
		if (dataDir.exists() && dataDir.isDirectory()) {
			for (File file : dataDir.listFiles()) {
				int dotIndex = file.getName().lastIndexOf(".xml");
				if (dotIndex != -1) {
					String storedHandle;
					try {
						storedHandle = URLDecoder.decode(file.getName().substring(0, dotIndex),
								MylarContextManager.CONTEXT_FILENAME_ENCODING);
						int delimIndex = storedHandle.lastIndexOf(RepositoryTaskHandleUtil.HANDLE_DELIM);
						if (delimIndex != -1) {
							String storedUrl = storedHandle.substring(0, delimIndex);
							if (oldUrl.equals(storedUrl)) {
								String id = RepositoryTaskHandleUtil.getTaskId(storedHandle);
								String newHandle = RepositoryTaskHandleUtil.getHandle(newUrl, id);
								File newFile = ContextCorePlugin.getContextManager().getFileForContext(newHandle);
								file.renameTo(newFile);
							}
						}
					} catch (Exception e) {
						MylarStatusHandler.fail(e, "Could not move context file: " + file.getName(), false);
					}
				}
			}
		}
		saveTaskList();
	}

	public boolean readExistingOrCreateNewList() {
		try {
			if (taskListFile.exists()) {
				taskListWriter.readTaskList(taskList, taskListFile, TasksUiPlugin.getDefault().getTaskDataManager());
			} else {
				resetTaskList();
			}

			for (ITask task : taskList.getAllTasks()) {
				if (task.getScheduledForDate() != null) {
					tasksWithReminders.add(task);
				}
				if (task.getDueDate() != null) {
					tasksWithDueDates.add(task);
				}
			}

			resetActivity();
			// parseFutureReminders();
			taskListInitialized = true;
			for (ITaskActivityListener listener : new ArrayList<ITaskActivityListener>(activityListeners)) {
				listener.taskListRead();
			}
		} catch (Exception e) {
			MylarStatusHandler.log(e, "Could not read task list");
			return false;
		}
		return true;
	}

	/**
	 * Only to be called upon initial startup by plugin.
	 */
	public void initActivityHistory() {
		resetAndRollOver();
		taskActivityHistory.loadPersistentHistory();
	}

	/**
	 * Will not save an empty task list to avoid losing data on bad startup.
	 */
	public synchronized void saveTaskList() {
		try {
			if (taskListInitialized) {
				taskListSaveManager.saveTaskList(true, false);
				// TasksUiPlugin.getDefault().getPreferenceStore().setValue(TaskListPreferenceConstants.TASK_ID,
				// nextLocalTaskId);
			} else {
				MylarStatusHandler.log("task list save attempted before initialization", this);
			}
		} catch (Exception e) {
			MylarStatusHandler.fail(e, "Could not save task list", true);
		}
	}

	public TaskList getTaskList() {
		return taskList;
	}

	public void addActivityListener(ITaskActivityListener listener) {
		activityListeners.add(listener);
	}

	public void removeActivityListener(ITaskActivityListener listener) {
		activityListeners.remove(listener);
	}

	public void activateTask(ITask task) {
		if (!TasksUiPlugin.getDefault().isMultipleActiveTasksMode()) {
			deactivateAllTasks();
		}

		try {
			taskList.setActive(task, true);
			for (ITaskActivityListener listener : new ArrayList<ITaskActivityListener>(activityListeners)) {
				try {
					listener.taskActivated(task);
				} catch (Throwable t) {
					MylarStatusHandler.fail(t, "task activity listener failed: " + listener, false);
				}
			}
		} catch (Throwable t) {
			MylarStatusHandler.fail(t, "could not activate task", false);
		}
	}

	public void deactivateAllTasks() {
		// Make a copy to avoid modification on list being traversed; can result
		// in a ConcurrentModificationException
		List<ITask> activeTasks = taskList.getActiveTasks();
		for (ITask task : activeTasks) {
			deactivateTask(task);
		}
	}

	public void deactivateTask(ITask task) {
		if (task == null) {
			return;
		}

		if (task.isActive()) {
			taskList.setActive(task, false);
			for (ITaskActivityListener listener : new ArrayList<ITaskActivityListener>(activityListeners)) {
				try {
					listener.taskDeactivated(task);
				} catch (Throwable t) {
					MylarStatusHandler.fail(t, "notification failed for: " + listener, false);
				}
			}
		}
	}

	public void setTaskListFile(File file) {
		this.taskListFile = file;
	}

	public void copyDataDirContentsTo(String newDataDir) {
		taskListSaveManager.copyDataDirContentsTo(newDataDir);
	}

	public boolean isTaskListInitialized() {
		return taskListInitialized;
	}

	public TaskListWriter getTaskListWriter() {
		return taskListWriter;
	}

	public File getTaskListFile() {
		return taskListFile;
	}

	public boolean isActiveThisWeek(ITask task) {
		for (ITask activityDelegateTask : activityThisWeek.getChildren()) {
			if (activityDelegateTask.getHandleIdentifier().equals(task.getHandleIdentifier())) {
				return true;
			}
		}
		return false;
	}

	/**
	 * @return if a repository task, will only return true if the user is a
	 */
	public boolean isCompletedToday(ITask task) {
		if (task != null) {

			if (task instanceof AbstractRepositoryTask && !(task instanceof WebTask)) {
				AbstractRepositoryTask repositoryTask = (AbstractRepositoryTask) task;
				TaskRepository repository = TasksUiPlugin.getRepositoryManager().getRepository(
						repositoryTask.getRepositoryKind(), repositoryTask.getRepositoryUrl());

				if (repository != null && repositoryTask.getOwner() != null
						&& !repositoryTask.getOwner().equals(repository.getUserName())) {
					return false;
				}
			}

			Date completionDate = task.getCompletionDate();
			if (completionDate != null) {
				Calendar tomorrow = Calendar.getInstance();
				snapToNextDay(tomorrow);
				Calendar yesterday = Calendar.getInstance();
				yesterday.set(Calendar.HOUR_OF_DAY, 0);
				yesterday.set(Calendar.MINUTE, 0);
				yesterday.set(Calendar.SECOND, 0);
				yesterday.set(Calendar.MILLISECOND, 0);

				return completionDate.compareTo(yesterday.getTime()) == 1
						&& completionDate.compareTo(tomorrow.getTime()) == -1;
			}
		}
		return false;
	}

	public boolean isScheduledAfterThisWeek(ITask task) {
		if (task != null) {
			Date reminder = task.getScheduledForDate();
			if (reminder != null) {
				return reminder.compareTo(activityNextWeek.getStart().getTime()) > -1;
			}
		}
		return false;
	}

	public boolean isScheduledForLater(ITask task) {
		if (task != null) {
			Date reminder = task.getScheduledForDate();
			if (reminder != null) {
				return reminder.compareTo(activityFuture.getStart().getTime()) > -1;
			}
		}
		return false;
	}

	public boolean isScheduledForThisWeek(ITask task) {
		if (task != null) {
			Date reminder = task.getScheduledForDate();
			if (reminder != null) {
				Calendar weekStart = Calendar.getInstance();
				snapToStartOfWeek(weekStart);
				return (reminder.compareTo(weekStart.getTime()) >= 0 && reminder.compareTo(activityThisWeek.getEnd()
						.getTime()) <= 0);
			}
		}
		return false;
	}

	public boolean isScheduledForToday(ITask task) {
		if (task != null) {
			Date reminder = task.getScheduledForDate();
			if (reminder != null) {
				Calendar dayStart = Calendar.getInstance();
				dayStart.set(Calendar.HOUR_OF_DAY, 0);
				dayStart.set(Calendar.MINUTE, 0);
				dayStart.set(Calendar.SECOND, 0);
				dayStart.set(Calendar.MILLISECOND, 0);
				Calendar midnight = GregorianCalendar.getInstance();
				snapToNextDay(midnight);
				return (reminder.compareTo(dayStart.getTime()) >= 0 && reminder.compareTo(midnight.getTime()) == -1);
			}
		}
		return false;
	}

	/**
	 * TODO: Need to migrate to use of this method for setting of reminders
	 */
	public void setScheduledFor(ITask task, Date reminderDate) {
		if (task == null)
			return;
		task.setScheduledForDate(reminderDate);
		task.setReminded(false);
		if (reminderDate == null) {
			tasksWithReminders.remove(task);
		} else {
			tasksWithReminders.remove(task);
			tasksWithReminders.add(task);
		}
		parseFutureReminders();
		taskList.notifyLocalInfoChanged(task);
	}

	public void setDueDate(ITask task, Date dueDate) {
		task.setDueDate(dueDate);
		if (dueDate == null) {
			tasksWithDueDates.remove(task);
		} else {
			tasksWithDueDates.remove(task);
			tasksWithDueDates.add(task);
		}
		parseFutureReminders();
		taskList.notifyLocalInfoChanged(task);
	}

	/**
	 * @return true if task due date != null and has past
	 */
	public boolean isOverdue(ITask task) {
		return (!task.isCompleted() && task.getDueDate() != null && new Date().after(task.getDueDate()));
	}

	public void propertyChange(PropertyChangeEvent event) {
		if (event.getProperty().equals(TaskListPreferenceConstants.PLANNING_STARTHOUR)
				|| event.getProperty().equals(TaskListPreferenceConstants.PLANNING_ENDHOUR)) {
			// event.getProperty().equals(TaskListPreferenceConstants.PLANNING_STARTDAY)
			// scheduledStartHour =
			// TasksUiPlugin.getDefault().getPreferenceStore().getInt(
			// TaskListPreferenceConstants.PLANNING_STARTHOUR);
			scheduledEndHour = TasksUiPlugin.getDefault().getPreferenceStore().getInt(
					TaskListPreferenceConstants.PLANNING_ENDHOUR);
		}
	}

	/** public for testing */
	public void resetAndRollOver() {
		taskActivityHistoryInitialized = false;
		tasksWithReminders.clear();
		tasksWithDueDates.clear();
		for (ITask task : taskList.getAllTasks()) {
			if (task.getScheduledForDate() != null) {
				tasksWithReminders.add(task);
			}
			if (task.getDueDate() != null) {
				tasksWithDueDates.add(task);
			}
		}
		resetActivity();
		parseTaskActivityInteractionHistory();
		for (ITaskActivityListener listener : activityListeners) {
			listener.calendarChanged();
		}
	}

	private class RolloverCheck extends TimerTask {

		@Override
		public void run() {
			if (!Platform.isRunning() || ContextCorePlugin.getDefault() == null) {
				return;
			} else {
				Calendar now = GregorianCalendar.getInstance();
				DateRangeContainer thisWeek = getActivityThisWeek();
				if (!thisWeek.includes(now)) {
					startTime = new Date();
					resetAndRollOver();
				}
			}
		}
	}

	public TaskActivationHistory getTaskActivationHistory() {
		return taskActivityHistory;
	}

	public Set<ITask> getScheduledForThisWeek() {
		Set<ITask> tasksScheduled = new HashSet<ITask>();
		for (ITask task : getActivityThisWeek().getChildren()) {
			if (isScheduledForThisWeek(task)) {
				tasksScheduled.add(task);
			}
		}
		return tasksScheduled;
	}

	/**
	 * @param element
	 *            tasklist element to retrieve a task for currently will work
	 *            for (ITask, AbstractQueryHit)
	 * @param force -
	 *            if a query hit is passed you can either force construction of
	 *            the task or not (if not and no task, null is returned) TODO:
	 *            Move into TaskList?
	 */
	public ITask getTaskForElement(ITaskListElement element, boolean force) {
		ITask task = null;
		if (element instanceof AbstractQueryHit) {
			if (force) {
				task = ((AbstractQueryHit) element).getOrCreateCorrespondingTask();
			} else {
				task = ((AbstractQueryHit) element).getCorrespondingTask();
			}
		} else if (element instanceof ITask) {
			task = (ITask) element;
		}
		return task;
	}

	protected void setTaskListSaveManager(TaskListSaveManager taskListSaveManager) {
		this.taskListSaveManager = taskListSaveManager;
		this.taskList.addChangeListener(taskListSaveManager);
	}

	public List<DateRangeContainer> getActivityWeekDays() {
		return activityWeekDays;
	}

	public boolean isWeekDay(DateRangeContainer dateRangeTaskContainer) {
		return activityWeekDays.contains(dateRangeTaskContainer);
	}

	public int getStartHour() {
		return START_HOUR;
	}
}
<commit_msg>NEW - bug 179620: Use bugzilla time tracking due date as task due date when available https://bugs.eclipse.org/bugs/show_bug.cgi?id=179620<commit_after>/*******************************************************************************
 * Copyright (c) 2004 - 2006 University Of British Columbia and others.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *     University Of British Columbia - initial API and implementation
 *******************************************************************************/
/*
 * Created on Dec 26, 2004
 */
package org.eclipse.mylar.tasks.ui;

import java.io.File;
import java.net.URLDecoder;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.ConcurrentHashMap;

import org.eclipse.core.runtime.Platform;
import org.eclipse.jface.util.IPropertyChangeListener;
import org.eclipse.jface.util.PropertyChangeEvent;
import org.eclipse.mylar.context.core.ContextCorePlugin;
import org.eclipse.mylar.context.core.IMylarContext;
import org.eclipse.mylar.context.core.IMylarContextListener;
import org.eclipse.mylar.context.core.IMylarElement;
import org.eclipse.mylar.core.MylarStatusHandler;
import org.eclipse.mylar.internal.context.core.MylarContextManager;
import org.eclipse.mylar.internal.tasks.core.RepositoryTaskHandleUtil;
import org.eclipse.mylar.internal.tasks.core.WebTask;
import org.eclipse.mylar.internal.tasks.ui.TaskListPreferenceConstants;
import org.eclipse.mylar.internal.tasks.ui.WorkspaceAwareContextStore;
import org.eclipse.mylar.internal.tasks.ui.util.TaskListSaveManager;
import org.eclipse.mylar.internal.tasks.ui.util.TaskListWriter;
import org.eclipse.mylar.internal.tasks.ui.views.TaskActivationHistory;
import org.eclipse.mylar.monitor.core.InteractionEvent;
import org.eclipse.mylar.tasks.core.AbstractQueryHit;
import org.eclipse.mylar.tasks.core.AbstractRepositoryTask;
import org.eclipse.mylar.tasks.core.AbstractTaskContainer;
import org.eclipse.mylar.tasks.core.DateRangeActivityDelegate;
import org.eclipse.mylar.tasks.core.DateRangeContainer;
import org.eclipse.mylar.tasks.core.ITask;
import org.eclipse.mylar.tasks.core.ITaskActivityListener;
import org.eclipse.mylar.tasks.core.ITaskListChangeListener;
import org.eclipse.mylar.tasks.core.ITaskListElement;
import org.eclipse.mylar.tasks.core.TaskList;
import org.eclipse.mylar.tasks.core.TaskRepository;
import org.eclipse.mylar.tasks.core.TaskRepositoryManager;

/**
 * TODO: pull task activity management out into new TaskActivityManager
 * 
 * @author <NAME>
 * @author <NAME> (task activity)
 */
public class TaskListManager implements IPropertyChangeListener {

	// Did have preference for this so should re-enable
	private static final int START_HOUR = 8;

	private static final long SECOND = 1000;

	private static final long MINUTE = 60 * SECOND;

	private static final long ROLLOVER_DELAY = 30 * MINUTE;

	private static final int NUM_WEEKS_PREVIOUS = -1;

	private static final int NUM_WEEKS_NEXT = 1;

	private static final int NUM_WEEKS_FUTURE_START = 2;

	private static final int NUM_WEEKS_FUTURE_END = 8;

	private static final int NUM_WEEKS_PAST_START = -8;

	private static final int NUM_WEEKS_PAST_END = -2;

	public static final String ARCHIVE_CATEGORY_DESCRIPTION = "Archive";

	private static final String DESCRIPTION_THIS_WEEK = "This Week";

	private static final String DESCRIPTION_PREVIOUS_WEEK = "Previous Week";

	private static final String DESCRIPTION_NEXT_WEEK = "Next Week";

	private static final String DESCRIPTION_FUTURE = "Future";

	private static final String DESCRIPTION_PAST = "Past";

	public static final String[] ESTIMATE_TIMES = new String[] { "0 Hours", "1 Hours", "2 Hours", "3 Hours", "4 Hours",
			"5 Hours", "6 Hours", "7 Hours", "8 Hours", "9 Hours", "10 Hours" };

	private DateRangeContainer activityPreviousWeek;

	private DateRangeContainer activityThisWeek;

	private List<DateRangeContainer> activityWeekDays = new ArrayList<DateRangeContainer>();

	private DateRangeContainer activityNextWeek;

	private DateRangeContainer activityFuture;

	private DateRangeContainer activityPast;

	private ArrayList<DateRangeContainer> dateRangeContainers = new ArrayList<DateRangeContainer>();

	private Set<ITask> tasksWithReminders = new HashSet<ITask>();

	private Set<ITask> tasksWithDueDates = new HashSet<ITask>();

	private ITask currentTask = null;

	private String currentHandle = "";

	private Calendar currentTaskStart = null;

	private Calendar currentTaskEnd = null;

	private Map<ITask, Long> taskElapsedTimeMap = new ConcurrentHashMap<ITask, Long>();

	private List<ITaskActivityListener> activityListeners = new ArrayList<ITaskActivityListener>();

	private TaskListWriter taskListWriter;

	private File taskListFile;

	private TaskListSaveManager taskListSaveManager;

	// TODO: guard against overwriting the single instance?
	private TaskList taskList = new TaskList();

	private TaskActivationHistory taskActivityHistory = new TaskActivationHistory();

	private boolean taskListInitialized = false;

	private boolean taskActivityHistoryInitialized = false;

	private int startDay;

	private int endDay;

	private int scheduledEndHour;

	private Timer timer;

	/** public for testing */
	public Date startTime = new Date();

	private final IMylarContextListener CONTEXT_LISTENER = new IMylarContextListener() {

		public void contextActivated(IMylarContext context) {
			// ignore
		}

		public void contextDeactivated(IMylarContext context) {
			// ignore
		}

		public void presentationSettingsChanging(UpdateKind kind) {
			// ignore
		}

		public void presentationSettingsChanged(UpdateKind kind) {
			// ignore
		}

		public void interestChanged(List<IMylarElement> elements) {
			List<InteractionEvent> events = ContextCorePlugin.getContextManager().getActivityHistoryMetaContext()
					.getInteractionHistory();
			InteractionEvent event = events.get(events.size() - 1);
			parseInteractionEvent(event);

		}

		public void elementDeleted(IMylarElement element) {
			// ignore
		}

		public void landmarkAdded(IMylarElement element) {
			// ignore
		}

		public void landmarkRemoved(IMylarElement element) {
			// ignore
		}

		public void relationsChanged(IMylarElement element) {
			// ignore
		}
	};

	private final ITaskListChangeListener CHANGE_LISTENER = new ITaskListChangeListener() {

		public void containerAdded(AbstractTaskContainer container) {
		}

		public void containerDeleted(AbstractTaskContainer container) {
		}

		public void containerInfoChanged(AbstractTaskContainer container) {
		}

		public void localInfoChanged(ITask task) {
		}

		public void repositoryInfoChanged(ITask task) {
		}

		public void taskAdded(ITask task) {
		}

		public void taskDeleted(ITask task) {
			TaskListManager.this.resetAndRollOver();
		}

		public void taskMoved(ITask task, AbstractTaskContainer fromContainer, AbstractTaskContainer toContainer) {
		}

	};

	private int timeTicks = 0;

	public TaskListManager(TaskListWriter taskListWriter, File file) {
		this.taskListFile = file;
		this.taskListWriter = taskListWriter;

		timer = new Timer();
		timer.schedule(new RolloverCheck(), ROLLOVER_DELAY, ROLLOVER_DELAY);
		taskList.addChangeListener(CHANGE_LISTENER);
	}

	public void init() {
		ContextCorePlugin.getContextManager().addActivityMetaContextListener(CONTEXT_LISTENER);
	}

	public void dispose() {
		ContextCorePlugin.getContextManager().removeActivityMetaContextListener(CONTEXT_LISTENER);
	}

	public TaskList resetTaskList() {
		resetActivity();
		taskList.reset();
		taskListInitialized = true;
		return taskList;
	}

	private void resetActivity() {
		taskElapsedTimeMap.clear();
		dateRangeContainers.clear();
		setupCalendarRanges();
	}

	/**
	 * Warning: if called twice task times will be wrong
	 */
	private void parseTaskActivityInteractionHistory() {
		if (!TasksUiPlugin.getTaskListManager().isTaskListInitialized()) {
			return;
		}
		List<InteractionEvent> events = ContextCorePlugin.getContextManager().getActivityHistoryMetaContext()
				.getInteractionHistory();
		for (InteractionEvent event : events) {
			parseInteractionEvent(event);
		}
		taskActivityHistoryInitialized = true;
		parseFutureReminders();
	}

	public void parseFutureReminders() {
		activityFuture.clear();
		activityNextWeek.clear();

		for (DateRangeContainer day : activityWeekDays) {
			day.clear();
		}
		HashSet<ITask> toRemove = new HashSet<ITask>();
		toRemove.addAll(activityThisWeek.getDateRangeDelegates());
		for (ITask activity : toRemove) {
			DateRangeActivityDelegate delegate = (DateRangeActivityDelegate) activity;
			Calendar calendar = GregorianCalendar.getInstance();

			Date schedDate = delegate.getScheduledForDate();
			if (schedDate == null) {
				schedDate = delegate.getDueDate();
			}

			if (schedDate != null) {
				calendar.setTime(schedDate);
				if (!activityThisWeek.includes(calendar) && activityThisWeek.getElapsed(delegate) == 0) {
					activityThisWeek.remove(delegate);
				}
			} else {
				if (activityThisWeek.getElapsed(delegate) == 0) {
					activityThisWeek.remove(delegate);
				}
			}
		}
		GregorianCalendar tempCalendar = new GregorianCalendar();
		tempCalendar.setFirstDayOfWeek(startDay);

		Set<ITask> allScheduledTasks = new HashSet<ITask>();
		allScheduledTasks.addAll(tasksWithReminders);
		allScheduledTasks.addAll(tasksWithDueDates);

		for (ITask task : allScheduledTasks) {
			if (task instanceof DateRangeActivityDelegate) {
				task = ((DateRangeActivityDelegate) task).getCorrespondingTask();
			}

			Date schedDate = task.getScheduledForDate();
			if (schedDate == null || isOverdue(task)) {
				schedDate = task.getDueDate();
			}

			if (schedDate != null) {
				tempCalendar.setTime(schedDate);
				if (activityNextWeek.includes(tempCalendar)) {
					activityNextWeek.addTask(new DateRangeActivityDelegate(activityNextWeek, task, tempCalendar,
							tempCalendar));
				} else if (activityFuture.includes(tempCalendar)) {
					activityFuture.addTask(new DateRangeActivityDelegate(activityFuture, task, tempCalendar,
							tempCalendar));
				} else if (activityThisWeek.includes(tempCalendar) && !activityThisWeek.getChildren().contains(task)) {
					activityThisWeek.addTask(new DateRangeActivityDelegate(activityThisWeek, task, tempCalendar,
							tempCalendar));
				} else if (activityPreviousWeek.includes(tempCalendar)
						&& !activityPreviousWeek.getChildren().contains(task)) {
					activityPreviousWeek.addTask(new DateRangeActivityDelegate(activityPreviousWeek, task,
							tempCalendar, tempCalendar));
				} else if (activityPast.includes(tempCalendar) && !activityPast.getChildren().contains(task)) {
					activityPast.addTask(new DateRangeActivityDelegate(activityPast, task, tempCalendar, tempCalendar));
				}

				for (DateRangeContainer day : activityWeekDays) {
					if (day.includes(tempCalendar) && !day.getChildren().contains(task)) {
						day.addTask(new DateRangeActivityDelegate(day, task, tempCalendar, tempCalendar, this
								.getElapsedTime(task)));
					}
				}
			}
		}
	}

	/** public for testing * */
	public void parseInteractionEvent(InteractionEvent event) {

		if (event.getDelta().equals(MylarContextManager.ACTIVITY_DELTA_ACTIVATED)) {
			if (!event.getStructureHandle().equals(MylarContextManager.ACTIVITY_HANDLE_ATTENTION)) {

				ITask activatedTask = TasksUiPlugin.getTaskListManager().getTaskList().getTask(
						event.getStructureHandle());

				if (currentTask != null && activatedTask != null) {
					if (!currentTask.equals(activatedTask)) {

						GregorianCalendar calendarEnd = new GregorianCalendar();
						calendarEnd.setFirstDayOfWeek(startDay);
						calendarEnd.setTime(event.getDate());
						calendarEnd.getTime();
						// Activation of different task before deactivation of
						// previous, log was inconsistent,
						// finish what we started
						taskDeactivated(calendarEnd);
					} else {
						// skip re-activations of same task
						return;
					}
				}

				currentTask = activatedTask;
				if (currentTask != null) {
					GregorianCalendar calendar = new GregorianCalendar();
					calendar.setFirstDayOfWeek(startDay);
					calendar.setTime(event.getDate());
					currentTaskStart = calendar;
					currentHandle = event.getStructureHandle();
				}
			} else if (event.getStructureHandle().equals(MylarContextManager.ACTIVITY_HANDLE_ATTENTION)) {
				if (!currentHandle.equals("")) {
					long active = event.getEndDate().getTime() - event.getDate().getTime();

					// add to running total
					if (taskElapsedTimeMap.containsKey(currentTask)) {
						long pastTime = taskElapsedTimeMap.get(currentTask);
						taskElapsedTimeMap.put(currentTask, pastTime + active);
						timeTicks++;
						if (taskActivityHistoryInitialized && timeTicks > 3) {
							// Save incase of system failure.
							// TODO: request asynchronous save
							ContextCorePlugin.getContextManager().saveActivityHistoryContext();
							timeTicks = 0;
						}

					} else {
						taskElapsedTimeMap.put(currentTask, active);
					}

				}

			}
		} else if (event.getDelta().equals(MylarContextManager.ACTIVITY_DELTA_DEACTIVATED)) {
			if (!event.getStructureHandle().equals(MylarContextManager.ACTIVITY_HANDLE_ATTENTION)
					&& currentHandle.equals(event.getStructureHandle())) {
				GregorianCalendar calendarEnd = new GregorianCalendar();
				calendarEnd.setFirstDayOfWeek(startDay);
				calendarEnd.setTime(event.getDate());
				calendarEnd.getTime();
				taskDeactivated(calendarEnd);
			} else if (event.getStructureHandle().equals(MylarContextManager.ACTIVITY_HANDLE_ATTENTION)) {
				// Deactivated attention events not currently used (ignored)
			}
		}
	}

	private void taskDeactivated(GregorianCalendar calendarEnd) {
		currentTaskEnd = calendarEnd;

		Set<DateRangeContainer> rangeSet = new HashSet<DateRangeContainer>();
		rangeSet.addAll(dateRangeContainers);
		rangeSet.add(activityThisWeek);

		for (DateRangeContainer week : rangeSet) {
			if (week.includes(currentTaskStart) && (!isWeekDay(week) && !week.isFuture())) {
				if (currentTask != null) {
					// add to date range 'bin'
					Long activeTime = taskElapsedTimeMap.get(currentTask);
					if (activeTime == null) {
						activeTime = new Long(0);
					}
					DateRangeActivityDelegate delegate = new DateRangeActivityDelegate(week, currentTask,
							currentTaskStart, currentTaskEnd, activeTime);
					week.addTask(delegate);
					if (taskActivityHistoryInitialized) {
						for (ITaskActivityListener listener : activityListeners) {
							listener.activityChanged(week);
						}
					}
				}
			}
		}

		currentTask = null;
		currentHandle = "";
	}

	/** public for testing * */
	public DateRangeContainer getActivityThisWeek() {
		return activityThisWeek;
	}

	/** public for testing * */
	public DateRangeContainer getActivityPast() {
		return activityPast;
	}

	/** public for testing * */
	public DateRangeContainer getActivityFuture() {
		return activityFuture;
	}

	/** public for testing * */
	public DateRangeContainer getActivityNextWeek() {
		return activityNextWeek;
	}

	/** public for testing * */
	public DateRangeContainer getActivityPrevious() {
		return activityPreviousWeek;
	}

	/** total elapsed time based on activation history */
	public long getElapsedTime(ITask task) {
		if (taskElapsedTimeMap.containsKey(task)) {
			return taskElapsedTimeMap.get(task);
		} else {
			return 0;
		}
	}

	private void setupCalendarRanges() {
		// MylarTaskListPlugin.getMylarCorePrefs().getInt(TaskListPreferenceConstants.PLANNING_STARTDAY);
		startDay = Calendar.MONDAY;
		// MylarTaskListPlugin.getMylarCorePrefs().getInt(TaskListPreferenceConstants.PLANNING_ENDDAY);
		endDay = Calendar.SUNDAY;
		// scheduledStartHour =
		// TasksUiPlugin.getDefault().getPreferenceStore().getInt(
		// TaskListPreferenceConstants.PLANNING_STARTHOUR);
		scheduledEndHour = TasksUiPlugin.getDefault().getPreferenceStore().getInt(
				TaskListPreferenceConstants.PLANNING_ENDHOUR);

		activityWeekDays.clear();
		for (int x = startDay; x < (startDay + 7); x++) {
			GregorianCalendar dayStart = new GregorianCalendar();
			GregorianCalendar dayEnd = new GregorianCalendar();
			dayStart.setFirstDayOfWeek(startDay);
			dayEnd.setFirstDayOfWeek(startDay);
			if (x > 7) {
				dayStart.set(Calendar.DAY_OF_WEEK, x % 7);
				dayEnd.set(Calendar.DAY_OF_WEEK, x % 7);
			} else {
				dayStart.set(Calendar.DAY_OF_WEEK, x);
				dayEnd.set(Calendar.DAY_OF_WEEK, x);
			}

			dayStart.set(Calendar.HOUR_OF_DAY, 0);
			dayStart.set(Calendar.MINUTE, 0);
			dayStart.set(Calendar.SECOND, 0);
			dayStart.set(Calendar.MILLISECOND, 0);
			dayStart.getTime();

			dayEnd.set(Calendar.HOUR_OF_DAY, dayEnd.getMaximum(Calendar.HOUR_OF_DAY));
			dayEnd.set(Calendar.MINUTE, dayEnd.getMaximum(Calendar.MINUTE));
			dayEnd.set(Calendar.SECOND, dayEnd.getMaximum(Calendar.SECOND));
			dayEnd.set(Calendar.MILLISECOND, dayEnd.getMaximum(Calendar.MILLISECOND));
			dayEnd.getTime();

			String summary = "<unknown>";
			switch (dayStart.get(Calendar.DAY_OF_WEEK)) {
			case Calendar.MONDAY:
				summary = "Monday";
				break;
			case Calendar.TUESDAY:
				summary = "Tuesday";
				break;
			case Calendar.WEDNESDAY:
				summary = "Wednesday";
				break;
			case Calendar.THURSDAY:
				summary = "Thursday";
				break;
			case Calendar.FRIDAY:
				summary = "Friday";
				break;
			case Calendar.SATURDAY:
				summary = "Saturday";
				break;
			case Calendar.SUNDAY:
				summary = "Sunday";
				break;
			}
			DateRangeContainer day = new DateRangeContainer(dayStart, dayEnd, summary, taskList);
			activityWeekDays.add(day);
			dateRangeContainers.add(day);
		}

		GregorianCalendar currentBegin = new GregorianCalendar();
		currentBegin.setFirstDayOfWeek(startDay);
		currentBegin.setTime(startTime);
		snapToStartOfWeek(currentBegin);
		GregorianCalendar currentEnd = new GregorianCalendar();
		currentEnd.setFirstDayOfWeek(startDay);
		currentEnd.setTime(startTime);
		snapToEndOfWeek(currentEnd);
		activityThisWeek = new DateRangeContainer(currentBegin, currentEnd, DESCRIPTION_THIS_WEEK, taskList);
		// dateRangeContainers.add(activityThisWeek);

		GregorianCalendar previousStart = new GregorianCalendar();
		previousStart.setFirstDayOfWeek(startDay);
		previousStart.setTime(startTime);
		previousStart.add(Calendar.WEEK_OF_YEAR, NUM_WEEKS_PREVIOUS);
		snapToStartOfWeek(previousStart);
		GregorianCalendar previousEnd = new GregorianCalendar();
		previousEnd.setFirstDayOfWeek(startDay);
		previousEnd.setTime(startTime);
		previousEnd.add(Calendar.WEEK_OF_YEAR, NUM_WEEKS_PREVIOUS);
		snapToEndOfWeek(previousEnd);
		activityPreviousWeek = new DateRangeContainer(previousStart.getTime(), previousEnd.getTime(),
				DESCRIPTION_PREVIOUS_WEEK, taskList);
		dateRangeContainers.add(activityPreviousWeek);

		GregorianCalendar nextStart = new GregorianCalendar();
		nextStart.setFirstDayOfWeek(startDay);
		nextStart.setTime(startTime);
		nextStart.add(Calendar.WEEK_OF_YEAR, NUM_WEEKS_NEXT);
		snapToStartOfWeek(nextStart);
		GregorianCalendar nextEnd = new GregorianCalendar();
		nextEnd.setFirstDayOfWeek(startDay);
		nextEnd.setTime(startTime);
		nextEnd.add(Calendar.WEEK_OF_YEAR, NUM_WEEKS_NEXT);
		snapToEndOfWeek(nextEnd);
		activityNextWeek = new DateRangeContainer(nextStart.getTime(), nextEnd.getTime(), DESCRIPTION_NEXT_WEEK,
				taskList);
		dateRangeContainers.add(activityNextWeek);

		GregorianCalendar futureStart = new GregorianCalendar();
		futureStart.setFirstDayOfWeek(startDay);
		futureStart.setTime(startTime);
		futureStart.add(Calendar.WEEK_OF_YEAR, NUM_WEEKS_FUTURE_START);
		snapToStartOfWeek(futureStart);
		GregorianCalendar futureEnd = new GregorianCalendar();
		futureEnd.setFirstDayOfWeek(startDay);
		futureEnd.setTime(startTime);
		futureEnd.add(Calendar.WEEK_OF_YEAR, NUM_WEEKS_FUTURE_END);
		snapToEndOfWeek(futureEnd);
		activityFuture = new DateRangeContainer(futureStart.getTime(), futureEnd.getTime(), DESCRIPTION_FUTURE,
				taskList);
		dateRangeContainers.add(activityFuture);

		GregorianCalendar pastStart = new GregorianCalendar();
		pastStart.setFirstDayOfWeek(startDay);
		pastStart.setTime(startTime);
		pastStart.add(Calendar.WEEK_OF_YEAR, NUM_WEEKS_PAST_START);
		snapToStartOfWeek(pastStart);
		GregorianCalendar pastEnd = new GregorianCalendar();
		pastEnd.setFirstDayOfWeek(startDay);
		pastEnd.setTime(startTime);
		pastEnd.add(Calendar.WEEK_OF_YEAR, NUM_WEEKS_PAST_END);
		snapToEndOfWeek(pastEnd);
		activityPast = new DateRangeContainer(pastStart.getTime(), pastEnd.getTime(), DESCRIPTION_PAST, taskList);
		dateRangeContainers.add(activityPast);
	}

	public void snapToNextDay(Calendar cal) {
		cal.add(Calendar.DAY_OF_MONTH, 1);
		cal.set(Calendar.HOUR_OF_DAY, 0);
		cal.set(Calendar.MINUTE, 0);
		cal.set(Calendar.SECOND, 0);
		cal.set(Calendar.MILLISECOND, 0);
		cal.getTime();
	}

	private void snapToStartOfWeek(Calendar cal) {
		cal.getTime();
		cal.set(Calendar.DAY_OF_WEEK, startDay);
		cal.set(Calendar.HOUR_OF_DAY, 0);
		cal.set(Calendar.MINUTE, 0);
		cal.set(Calendar.SECOND, 0);
		cal.set(Calendar.MILLISECOND, 0);
		cal.getTime();
	}

	private void snapToEndOfWeek(Calendar cal) {
		cal.getTime();
		cal.set(Calendar.DAY_OF_WEEK, endDay);
		cal.set(Calendar.HOUR_OF_DAY, cal.getMaximum(Calendar.HOUR_OF_DAY));
		cal.set(Calendar.MINUTE, cal.getMaximum(Calendar.MINUTE));
		cal.set(Calendar.SECOND, cal.getMaximum(Calendar.SECOND));
		cal.set(Calendar.MILLISECOND, cal.getMaximum(Calendar.MILLISECOND));
		cal.getTime();
	}

	public Calendar setSecheduledIn(Calendar calendar, int days) {
		calendar.add(Calendar.DAY_OF_MONTH, days);
		calendar.set(Calendar.HOUR_OF_DAY, scheduledEndHour);
		calendar.set(Calendar.MINUTE, 0);
		calendar.set(Calendar.SECOND, 0);
		calendar.set(Calendar.MILLISECOND, 0);
		return calendar;
	}

	/**
	 * Will schedule for today if past work-day's end.
	 */
	public Calendar setScheduledEndOfDay(Calendar calendar) {
		// Calendar now = Calendar.getInstance();
		// if (now.get(Calendar.HOUR_OF_DAY) >= scheduledEndHour) {
		// setSecheduledIn(calendar, 1);
		// }
		calendar.set(Calendar.HOUR_OF_DAY, scheduledEndHour);
		calendar.set(Calendar.MINUTE, 0);
		calendar.set(Calendar.SECOND, 0);
		calendar.set(Calendar.MILLISECOND, 0);
		return calendar;
	}

	public void setScheduledNextWeek(Calendar calendar) {
		calendar.setTimeInMillis(TasksUiPlugin.getTaskListManager().getActivityNextWeek().getStart().getTimeInMillis());
		setScheduledEndOfDay(calendar);
	}

	public List<DateRangeContainer> getDateRanges() {
		// parseFutureReminders();
		// return dateRangeContainers.toArray(new
		// ITaskListElement[dateRangeContainers.size()]);
		return dateRangeContainers;
	}

	/**
	 * Every call to this method generates a unique handle, subsequent calls
	 * will have incremented task numbers
	 */
	public String genUniqueTaskHandle() {
		return TaskRepositoryManager.PREFIX_LOCAL + taskList.getNextTaskNum();
	}

	public void refactorRepositoryUrl(String oldUrl, String newUrl) {
		if (oldUrl == null || newUrl == null || oldUrl.equals(newUrl)) {
			return;
		}
		List<ITask> activeTasks = taskList.getActiveTasks();
		for (ITask task : new ArrayList<ITask>(activeTasks)) {
			deactivateTask(task);
		}
		taskList.refactorRepositoryUrl(oldUrl, newUrl);

		File dataDir = new File(TasksUiPlugin.getDefault().getDataDirectory(),
				WorkspaceAwareContextStore.CONTEXTS_DIRECTORY);
		if (dataDir.exists() && dataDir.isDirectory()) {
			for (File file : dataDir.listFiles()) {
				int dotIndex = file.getName().lastIndexOf(".xml");
				if (dotIndex != -1) {
					String storedHandle;
					try {
						storedHandle = URLDecoder.decode(file.getName().substring(0, dotIndex),
								MylarContextManager.CONTEXT_FILENAME_ENCODING);
						int delimIndex = storedHandle.lastIndexOf(RepositoryTaskHandleUtil.HANDLE_DELIM);
						if (delimIndex != -1) {
							String storedUrl = storedHandle.substring(0, delimIndex);
							if (oldUrl.equals(storedUrl)) {
								String id = RepositoryTaskHandleUtil.getTaskId(storedHandle);
								String newHandle = RepositoryTaskHandleUtil.getHandle(newUrl, id);
								File newFile = ContextCorePlugin.getContextManager().getFileForContext(newHandle);
								file.renameTo(newFile);
							}
						}
					} catch (Exception e) {
						MylarStatusHandler.fail(e, "Could not move context file: " + file.getName(), false);
					}
				}
			}
		}
		saveTaskList();
	}

	public boolean readExistingOrCreateNewList() {
		try {
			if (taskListFile.exists()) {
				taskListWriter.readTaskList(taskList, taskListFile, TasksUiPlugin.getDefault().getTaskDataManager());
			} else {
				resetTaskList();
			}

			for (ITask task : taskList.getAllTasks()) {
				if (task.getScheduledForDate() != null) {
					tasksWithReminders.add(task);
				}
				if (task.getDueDate() != null) {
					tasksWithDueDates.add(task);
				}
			}

			resetActivity();
			// parseFutureReminders();
			taskListInitialized = true;
			for (ITaskActivityListener listener : new ArrayList<ITaskActivityListener>(activityListeners)) {
				listener.taskListRead();
			}
		} catch (Exception e) {
			MylarStatusHandler.log(e, "Could not read task list");
			return false;
		}
		return true;
	}

	/**
	 * Only to be called upon initial startup by plugin.
	 */
	public void initActivityHistory() {
		resetAndRollOver();
		taskActivityHistory.loadPersistentHistory();
	}

	/**
	 * Will not save an empty task list to avoid losing data on bad startup.
	 */
	public synchronized void saveTaskList() {
		try {
			if (taskListInitialized) {
				taskListSaveManager.saveTaskList(true, false);
				// TasksUiPlugin.getDefault().getPreferenceStore().setValue(TaskListPreferenceConstants.TASK_ID,
				// nextLocalTaskId);
			} else {
				MylarStatusHandler.log("task list save attempted before initialization", this);
			}
		} catch (Exception e) {
			MylarStatusHandler.fail(e, "Could not save task list", true);
		}
	}

	public TaskList getTaskList() {
		return taskList;
	}

	public void addActivityListener(ITaskActivityListener listener) {
		activityListeners.add(listener);
	}

	public void removeActivityListener(ITaskActivityListener listener) {
		activityListeners.remove(listener);
	}

	public void activateTask(ITask task) {
		if (!TasksUiPlugin.getDefault().isMultipleActiveTasksMode()) {
			deactivateAllTasks();
		}

		try {
			taskList.setActive(task, true);
			for (ITaskActivityListener listener : new ArrayList<ITaskActivityListener>(activityListeners)) {
				try {
					listener.taskActivated(task);
				} catch (Throwable t) {
					MylarStatusHandler.fail(t, "task activity listener failed: " + listener, false);
				}
			}
		} catch (Throwable t) {
			MylarStatusHandler.fail(t, "could not activate task", false);
		}
	}

	public void deactivateAllTasks() {
		// Make a copy to avoid modification on list being traversed; can result
		// in a ConcurrentModificationException
		List<ITask> activeTasks = taskList.getActiveTasks();
		for (ITask task : activeTasks) {
			deactivateTask(task);
		}
	}

	public void deactivateTask(ITask task) {
		if (task == null) {
			return;
		}

		if (task.isActive()) {
			taskList.setActive(task, false);
			for (ITaskActivityListener listener : new ArrayList<ITaskActivityListener>(activityListeners)) {
				try {
					listener.taskDeactivated(task);
				} catch (Throwable t) {
					MylarStatusHandler.fail(t, "notification failed for: " + listener, false);
				}
			}
		}
	}

	public void setTaskListFile(File file) {
		this.taskListFile = file;
	}

	public void copyDataDirContentsTo(String newDataDir) {
		taskListSaveManager.copyDataDirContentsTo(newDataDir);
	}

	public boolean isTaskListInitialized() {
		return taskListInitialized;
	}

	public TaskListWriter getTaskListWriter() {
		return taskListWriter;
	}

	public File getTaskListFile() {
		return taskListFile;
	}

	public boolean isActiveThisWeek(ITask task) {
		for (ITask activityDelegateTask : activityThisWeek.getChildren()) {
			if (activityDelegateTask.getHandleIdentifier().equals(task.getHandleIdentifier())) {
				return true;
			}
		}
		return false;
	}

	/**
	 * @return if a repository task, will only return true if the user is a
	 */
	public boolean isCompletedToday(ITask task) {
		if (task != null) {

			if (task instanceof AbstractRepositoryTask && !(task instanceof WebTask)) {
				AbstractRepositoryTask repositoryTask = (AbstractRepositoryTask) task;
				TaskRepository repository = TasksUiPlugin.getRepositoryManager().getRepository(
						repositoryTask.getRepositoryKind(), repositoryTask.getRepositoryUrl());

				if (repository != null && repositoryTask.getOwner() != null
						&& !repositoryTask.getOwner().equals(repository.getUserName())) {
					return false;
				}
			}

			Date completionDate = task.getCompletionDate();
			if (completionDate != null) {
				Calendar tomorrow = Calendar.getInstance();
				snapToNextDay(tomorrow);
				Calendar yesterday = Calendar.getInstance();
				yesterday.set(Calendar.HOUR_OF_DAY, 0);
				yesterday.set(Calendar.MINUTE, 0);
				yesterday.set(Calendar.SECOND, 0);
				yesterday.set(Calendar.MILLISECOND, 0);

				return completionDate.compareTo(yesterday.getTime()) == 1
						&& completionDate.compareTo(tomorrow.getTime()) == -1;
			}
		}
		return false;
	}

	public boolean isScheduledAfterThisWeek(ITask task) {
		if (task != null) {
			Date reminder = task.getScheduledForDate();
			if (reminder != null) {
				return reminder.compareTo(activityNextWeek.getStart().getTime()) > -1;
			}
		}
		return false;
	}

	public boolean isScheduledForLater(ITask task) {
		if (task != null) {
			Date reminder = task.getScheduledForDate();
			if (reminder != null) {
				return reminder.compareTo(activityFuture.getStart().getTime()) > -1;
			}
		}
		return false;
	}

	public boolean isScheduledForThisWeek(ITask task) {
		if (task != null) {
			Date reminder = task.getScheduledForDate();
			if (reminder != null) {
				Calendar weekStart = Calendar.getInstance();
				snapToStartOfWeek(weekStart);
				return (reminder.compareTo(weekStart.getTime()) >= 0 && reminder.compareTo(activityThisWeek.getEnd()
						.getTime()) <= 0);
			}
		}
		return false;
	}

	public boolean isScheduledForToday(ITask task) {
		if (task != null) {
			Date reminder = task.getScheduledForDate();
			if (reminder != null) {
				Calendar dayStart = Calendar.getInstance();
				dayStart.set(Calendar.HOUR_OF_DAY, 0);
				dayStart.set(Calendar.MINUTE, 0);
				dayStart.set(Calendar.SECOND, 0);
				dayStart.set(Calendar.MILLISECOND, 0);
				Calendar midnight = GregorianCalendar.getInstance();
				snapToNextDay(midnight);
				return (reminder.compareTo(dayStart.getTime()) >= 0 && reminder.compareTo(midnight.getTime()) == -1);
			}
		}
		return false;
	}

	/**
	 * TODO: Need to migrate to use of this method for setting of reminders
	 */
	public void setScheduledFor(ITask task, Date reminderDate) {
		if (task == null)
			return;
		task.setScheduledForDate(reminderDate);
		task.setReminded(false);
		if (reminderDate == null) {
			tasksWithReminders.remove(task);
		} else {
			tasksWithReminders.remove(task);
			tasksWithReminders.add(task);
		}
		parseFutureReminders();
		taskList.notifyLocalInfoChanged(task);
	}

	public void setDueDate(ITask task, Date dueDate) {
		task.setDueDate(dueDate);
		if (dueDate == null) {
			tasksWithDueDates.remove(task);
		} else {
			tasksWithDueDates.remove(task);
			tasksWithDueDates.add(task);
		}
		parseFutureReminders();
		taskList.notifyLocalInfoChanged(task);
	}

	/**
	 * @return true if task due date != null and has past
	 */
	public boolean isOverdue(ITask task) {
		return (!task.isCompleted() && task.getDueDate() != null && new Date().after(task.getDueDate()));
	}

	public void propertyChange(PropertyChangeEvent event) {
		if (event.getProperty().equals(TaskListPreferenceConstants.PLANNING_STARTHOUR)
				|| event.getProperty().equals(TaskListPreferenceConstants.PLANNING_ENDHOUR)) {
			// event.getProperty().equals(TaskListPreferenceConstants.PLANNING_STARTDAY)
			// scheduledStartHour =
			// TasksUiPlugin.getDefault().getPreferenceStore().getInt(
			// TaskListPreferenceConstants.PLANNING_STARTHOUR);
			scheduledEndHour = TasksUiPlugin.getDefault().getPreferenceStore().getInt(
					TaskListPreferenceConstants.PLANNING_ENDHOUR);
		}
	}

	/** public for testing */
	public void resetAndRollOver() {
		taskActivityHistoryInitialized = false;
		tasksWithReminders.clear();
		tasksWithDueDates.clear();
		for (ITask task : taskList.getAllTasks()) {
			if (task.getScheduledForDate() != null) {
				tasksWithReminders.add(task);
			}
			if (task.getDueDate() != null) {
				tasksWithDueDates.add(task);
			}
		}
		resetActivity();
		parseTaskActivityInteractionHistory();
		for (ITaskActivityListener listener : activityListeners) {
			listener.calendarChanged();
		}
	}

	private class RolloverCheck extends TimerTask {

		@Override
		public void run() {
			if (!Platform.isRunning() || ContextCorePlugin.getDefault() == null) {
				return;
			} else {
				Calendar now = GregorianCalendar.getInstance();
				DateRangeContainer thisWeek = getActivityThisWeek();
				if (!thisWeek.includes(now)) {
					startTime = new Date();
					resetAndRollOver();
				}
			}
		}
	}

	public TaskActivationHistory getTaskActivationHistory() {
		return taskActivityHistory;
	}

	public Set<ITask> getScheduledForThisWeek() {
		Set<ITask> tasksScheduled = new HashSet<ITask>();
		for (ITask task : getActivityThisWeek().getChildren()) {
			if (isScheduledForThisWeek(task)) {
				tasksScheduled.add(task);
			}
		}
		return tasksScheduled;
	}

	/**
	 * @param element
	 *            tasklist element to retrieve a task for currently will work
	 *            for (ITask, AbstractQueryHit)
	 * @param force -
	 *            if a query hit is passed you can either force construction of
	 *            the task or not (if not and no task, null is returned) TODO:
	 *            Move into TaskList?
	 */
	public ITask getTaskForElement(ITaskListElement element, boolean force) {
		ITask task = null;
		if (element instanceof AbstractQueryHit) {
			if (force) {
				task = ((AbstractQueryHit) element).getOrCreateCorrespondingTask();
			} else {
				task = ((AbstractQueryHit) element).getCorrespondingTask();
			}
		} else if (element instanceof ITask) {
			task = (ITask) element;
		}
		return task;
	}

	protected void setTaskListSaveManager(TaskListSaveManager taskListSaveManager) {
		this.taskListSaveManager = taskListSaveManager;
		this.taskList.addChangeListener(taskListSaveManager);
	}

	public List<DateRangeContainer> getActivityWeekDays() {
		return activityWeekDays;
	}

	public boolean isWeekDay(DateRangeContainer dateRangeTaskContainer) {
		return activityWeekDays.contains(dateRangeTaskContainer);
	}

	public int getStartHour() {
		return START_HOUR;
	}
}

{'fraction_non_alphanumeric': 0.11163407641949424, 'fraction_numerical': 0.0029381141870979703, 'mean_word_length': 4.634814755284095, 'pattern_counts': {'":': 0, '<': 77, '<?xml version=': 0, '>': 87, 'https://': 1, 'lorem ipsum': 0, 'www.': 2, 'xml': 2}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2656624', 'n_tokens_mistral': 24809, 'n_tokens_neox': 21156, 'n_words': 4222}