/*******************************************************************************
 * Copyright (c) 2001, 2018 IBM Corporation and others.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License 2.0
 * which accompanies this distribution, and is available at
 * https://www.eclipse.org/legal/epl-2.0/
 *
 * SPDX-License-Identifier: EPL-2.0
 *
 * Contributors:
 *     IBM Corporation - initial API and implementation
 *     
 *******************************************************************************/
package org.eclipse.wst.sse.core.internal.tasks;

import java.util.ArrayList;
import java.util.List;

import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.IResourceChangeEvent;
import org.eclipse.core.resources.IResourceChangeListener;
import org.eclipse.core.resources.IResourceDelta;
import org.eclipse.core.resources.IResourceDeltaVisitor;
import org.eclipse.core.resources.IWorkspaceRoot;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.wst.sse.core.internal.Logger;

public class TaskScanningScheduler {
	class ListenerVisitor implements IResourceChangeListener, IResourceDeltaVisitor {
		public void resourceChanged(IResourceChangeEvent event) {
			IResourceDelta delta = event.getDelta();
			if (delta.getResource() != null) {
				int resourceType = delta.getResource().getType();
				if (resourceType == IResource.PROJECT || resourceType == IResource.ROOT) {
					try {
						delta.accept(this);
					}
					catch (CoreException e) {
						Logger.logException("Exception handling resource change", e); //$NON-NLS-1$
					}
				}
			}
		}

		public boolean visit(IResourceDelta delta) throws CoreException {
			if ((delta.getKind() & IResourceDelta.MARKERS) > 0 || (delta.getKind() & IResourceDelta.ENCODING) > 0 || (delta.getKind() & IResourceDelta.NO_CHANGE) > 0)
				return false;

			IResource resource = delta.getResource();
			if (resource != null) {
				if (resource.getType() == IResource.ROOT)
					return true;
				else if (resource.getType() == IResource.PROJECT) {
					fJob.addProjectDelta(delta);
					return false;
				}
			}
			return false;
		}

	}

	private static TaskScanningScheduler scheduler;

	public static void refresh() {
		TaskScanningJob.setScannedProjects(new String[0]);
		scheduler.enqueue(ResourcesPlugin.getWorkspace().getRoot());
	}

	public static void refresh(IProject project) {
		String[] projectNames = TaskScanningJob.getScannedProjects();
		List<String> freshProjectList = new ArrayList<>();
		for (int i = 0; i < projectNames.length; i++) {
			if (!projectNames[i].equals(project.getName())) {
				freshProjectList.add(projectNames[i]);
			}
		}
		TaskScanningJob.setScannedProjects(freshProjectList.toArray(new String[freshProjectList.size()]));

		scheduler.enqueue(project);
	}


	/**
	 * Only for use by SSECorePlugin class
	 */
	public static void shutdown() {
		if (scheduler != null) {
			ResourcesPlugin.getWorkspace().removeResourceChangeListener(scheduler.visitor);
			scheduler.fJob.cancel();
			try {
				scheduler.fJob.join();
			}
			catch (InterruptedException e) {
				Logger.logException(e);
			}
		}
	}

	/**
	 * Only for use by SSEUIPlugin class, UI by nature of its output being meant for the user
	 */
	public static void startup() {
		scheduler = new TaskScanningScheduler();

		/*
		 * According to
		 * http://www.eclipse.org/eclipse/development/performance/bloopers.html,
		 * POST_CHANGE listeners add a trivial performance cost.
		 * 
		 * Always add listener since we might be enabled per-project even if disabled workspace-wide.
		 */
		ResourcesPlugin.getWorkspace().addResourceChangeListener(scheduler.visitor, IResourceChangeEvent.POST_CHANGE);

		scheduler.enqueue(ResourcesPlugin.getWorkspace().getRoot());
	}

	TaskScanningJob fJob = null;

	ListenerVisitor visitor = null;

	private TaskScanningScheduler() {
		super();
		fJob = new TaskScanningJob();
		visitor = new ListenerVisitor();
	}

	void enqueue(IProject project) {
		fJob.addProject(project);
	}

	void enqueue(IWorkspaceRoot root) {
		IProject[] allProjects = root.getProjects();
		for (int i = 0; i < allProjects.length; i++) {
			fJob.addProject(allProjects[i]);
		}
	}
}
