/******************************************************************************* 
 * Copyright (c) 2011 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 
 * 
 * Contributors: 
 * Red Hat, Inc. - initial API and implementation 
 ******************************************************************************/ 
package org.kie.eclipse.navigator.view;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;

import org.eclipse.core.resources.IFolder;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.IResourceVisitor;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.jobs.Job;
import org.eclipse.jface.viewers.ITreeContentProvider;
import org.eclipse.jface.viewers.TreeViewer;
import org.eclipse.jface.viewers.Viewer;
import org.eclipse.wst.server.core.IServer;
import org.kie.eclipse.navigator.KieNavigatorContentRoot;
import org.kie.eclipse.navigator.view.content.IContainerNode;
import org.kie.eclipse.navigator.view.content.IContentNode;
import org.kie.eclipse.navigator.view.content.ServerNode;

/**
 * Content provider implementation for server management details.
 */
public class KieNavigatorContentProvider implements ITreeContentProvider {

    /** Represents a pending request in the tree. */
    static final Object PENDING = new Object();
    private ConcurrentMap<IContainerNode<?>, Object> pendingUpdates = new ConcurrentHashMap<IContainerNode<?>, Object>();
    private transient TreeViewer viewer;

    /**
     * Loads content for specified nodes, then refreshes the content in the
     * tree.
     */
    private Job loadElementJob = new Job("Refresh Server") {
        @Override
		public boolean shouldRun() {
            return pendingUpdates.size() > 0;
        }

        @Override
		protected IStatus run(final IProgressMonitor monitor) {
            monitor.beginTask("Loading Server Content", IProgressMonitor.UNKNOWN);
            try {
                final List<IContainerNode<?>> updatedNodes = new ArrayList<IContainerNode<?>>(pendingUpdates.size());
                for (IContainerNode<?> node : pendingUpdates.keySet()) {
                    try {
                        node.load();
                        updatedNodes.add(node);
                    } catch (Exception e) {
                    }
                    if (monitor.isCanceled()) {
                        pendingUpdates.keySet().removeAll(updatedNodes);
                        return Status.CANCEL_STATUS;
                    }
                }
                final TreeViewer viewer = KieNavigatorContentProvider.this.viewer;
                if (viewer == null) {
                    pendingUpdates.keySet().clear();
                } else {
                    viewer.getTree().getDisplay().asyncExec(new Runnable() {
                        @Override
						public void run() {
                            if (viewer.getTree().isDisposed()) {
                                return;
                            }
                            for (IContainerNode<?> node : updatedNodes) {
                                pendingUpdates.remove(node);
                                viewer.refresh(node);
                            }
                        }
                    });
                }
            } finally {
                monitor.done();
            }
            return Status.OK_STATUS;
        }
    };

    @Override
	public void dispose() {
        viewer = null;
        loadElementJob.cancel();
        pendingUpdates.clear();
    }
    
    @Override
	public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
        if (viewer instanceof TreeViewer) {
            this.viewer = (TreeViewer) viewer;
        } else {
            viewer = null;
        }
    }

    @Override
	public Object[] getElements(Object inputElement) {
        return getChildren(inputElement);
    }

    List<ServerNode> rootElements;
    
	@Override
	public Object[] getChildren(Object parentElement) {
		final List<Object> results = new ArrayList<Object>();
		if (parentElement instanceof KieNavigatorContentRoot) {
			List<? extends Object> children = ((KieNavigatorContentRoot) parentElement).getChildren();
			results.addAll(children);
		}
		else if (parentElement instanceof IContainerNode) {
			IContainerNode<?> container = (IContainerNode<?>) parentElement;
			if (pendingUpdates.containsKey(container)) {
				return new Object[] { PENDING };
			}
			List<? extends Object> children = container.getChildren();
			if (children == null) {
				pendingUpdates.putIfAbsent(container, PENDING);
				loadElementJob.schedule();
				return new Object[] { PENDING };
			}
			for (Object node : children) {
				if (node instanceof IContentNode) {
					// Resolve the content of this node: this may or
					// may not be the node itself. This is currently
					// used only by IProjectNode, which may return
					// the workspace IProject itself. From the IProject
					// level down, we will delegate to the Project View
					// for node content and labels.
					node = ((IContentNode) node).resolveContent();
				}
				results.add(node);
			}
		}
		else if (parentElement instanceof IProject) {
			// TODO: support GoInto actions for Projects
			final IProject project = (IProject) parentElement;
			if (project.isAccessible()) {
				try {
					project.accept(new IResourceVisitor() {
						@Override
						public boolean visit(IResource resource) throws CoreException {
							if (resource!=project)
								results.add(resource);
							return !(resource instanceof IFolder) || resource==project;
						}
					});
				}
				catch (CoreException e) {
					e.printStackTrace();
				}
			}			
		}
		return results.toArray();
	}

    @Override
	public Object getParent(Object element) {
        if (element instanceof IContentNode) {
            Object parent = ((IContentNode<?>) element).getParent();
            if (parent == null) {
                parent = ((IContentNode<?>) element).getServer();
            }
            return parent;
        }
        return null;
    }

    @Override
	public boolean hasChildren(Object element) {
        if (element instanceof IServer) {
            return true;
        } else if (element instanceof IContainerNode) {
            return ((IContainerNode)element).hasChildren();
        }
        return false;
    }

}
