/*******************************************************************************
 * Copyright (c) 2007 - 2012 Red Hat, Inc.
 * Distributed under license by Red Hat, Inc. All rights reserved.
 * This program is 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
 *
 * Contributor:
 *     Red Hat, Inc. - initial API and implementation
 ******************************************************************************/
package org.jboss.tools.common.model.project.ext;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.eclipse.core.resources.IProject;
import org.eclipse.core.runtime.IPath;
import org.jboss.tools.common.model.XModel;
import org.jboss.tools.common.model.filesystems.FileSystemsHelper;
import org.jboss.tools.common.model.filesystems.impl.Libs;
import org.jboss.tools.common.model.filesystems.impl.LibsListener;

/**
 * Monitors class path of project and loads seam components of it.
 *  
 * @author Viacheslav Kabanovich
 */
public abstract class AbstractClassPathMonitor<P> implements LibsListener {
	protected XModel model = null;
	protected P project;
	
	protected List<String> paths = new ArrayList<String>();
	private boolean loaded = false;

	protected Map<IPath, String> paths2 = new HashMap<IPath, String>();
	boolean libsModified = false;
	
	protected Set<String> processedPaths = new HashSet<String>();

	public AbstractClassPathMonitor() {
	}

	public P getProject() {
		return project;
	}

	/**
	 * Initialization of inner model.
	 */
	public void init() {
		if(model == null) return;
		Libs libs = FileSystemsHelper.getLibs(model);
		if(libs != null) libs.addListener(this);
	}

	public abstract IProject getProjectResource();
	
	/**
	 * Returns true if class path was up-to-date.
	 * Otherwise, updates inner model and disables class loader.
	 * @return
	 */
	public boolean update() {
		Libs libs = FileSystemsHelper.getLibs(model);
		if(libs == null) {
			return false;
		}
		libs.update();
		synchronized(this) {
			List<String> newPaths = libs.getPaths();
			boolean result = libsModified || !loaded;
			if(newPaths != null) {
				paths = newPaths;
				loaded = true;
			} else {
				paths = new ArrayList<String>();
				loaded = false;
			}
			if(result) {
				paths2.clear();
				paths2.putAll(libs.getPathsAsMap());
			}
			libsModified = false;
			return result;
		}
	}
	
	public void pathLoaded(IPath path) {
		String p = paths2.get(path);
		if(p != null) synchronized (this) {
			processedPaths.add(p);
		}
	}
	
	public boolean hasPath(IPath path) {
		return paths2.get(path) != null;
	}

	public void clean() {
		Libs libs = FileSystemsHelper.getLibs(model);
		if(libs != null) {
			libs.requestForUpdate();
		}
		paths = new ArrayList<String>();
		loaded = false;
		if(paths2 != null) paths2.clear();
		synchronized (this) {
			processedPaths.clear();
		}
	}

	public synchronized void reset() {
		processedPaths.clear();
	}

	protected List<String> syncProcessedPaths() {
		ArrayList<String> removed = new ArrayList<String>();
		synchronized (this) {
			Iterator<String> it = processedPaths.iterator();
			while(it.hasNext()) {
				String p = it.next();
				if(paths.contains(p)) continue;
				removed.add(p);
				it.remove();
			}
		}
		return removed;
	}

	protected boolean requestForLoad(String p) {
		synchronized (this) {
			return processedPaths.add(p);
		}
	}

	public void pathsChanged(List<String> paths) {
		synchronized (this) {
			libsModified = true;
		}
	}

	public void libraryChanged(String path) {
		synchronized (this) {
			libsModified = true;
			processedPaths.remove(path);
		}
	}

}
