/******************************************************************************* 
 * 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.content;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;

import org.eclipse.wst.server.core.IServer;
import org.kie.eclipse.server.IKieResourceHandler;

public abstract class ContainerNode<T extends IContainerNode<?>> extends ContentNode<T> implements IContainerNode<T> {

    private IErrorNode error;
	protected List<? extends IKieResourceHandler> handlerChildren;
	protected List<? extends IContentNode<?>> children;

    protected ContainerNode(String name) {
        super(name);
    }

    protected ContainerNode(T parent, IKieResourceHandler handler) {
        super(parent, handler);
    }
    
	@Override
	public boolean hasChildren() {
		return isResolved();
	}

    @Override
	public List<? extends IContentNode<?>> getChildren() {
        if (error != null) {
            return Collections.singletonList(error);
        }
		if (handlerChildren!=null) {
			// The Java Content Provider, which is included as a <contextExtension>
			// in our Kie Navigator Viewer's content binding (see plugin.xml),
			// will periodically (every 10 seconds or so) force a refresh of the
			// entire viewer. This forces potentially many REST calls to the server
			// to rebuild the entire tree. In order to avoid this, we set a Part
			// Property on the Navigator to signal when a refresh was triggered
			// as a result of a real change in the tree caused by user actions.
			// The Part Property value is set to "true" in ContentNode.refresh()
			// just before refreshing the Navigator viewer, and then back to "false"
			// when done. This allows {@code #createChildren()} to be called only
			// when necessary.
			String internalRefresh = getNavigator().getProperty(INTERNAL_REFRESH_KEY);
			if (children==null || Boolean.getBoolean(internalRefresh)==true)
				children = updateChildren(children, createChildren());
			return children;
		}
		return null;
    }

    protected abstract List<? extends IContentNode<?>> createChildren();
    
    @Override
	public final void clearChildren() {
        clearError();
        clearHandlerChildren();
		if (children!=null) {
			for (IContentNode<?> n : children)
				n.dispose();
			children.clear();
			children = null;
		}
    }

    public final void clearHandlerChildren() {
		if (handlerChildren!=null) {
			for (IKieResourceHandler h : handlerChildren)
				h.dispose();
			handlerChildren.clear();
			handlerChildren = null;
		}
    }
    
    protected void setError(IErrorNode error) {
        clearError();
        this.error = error;
    }

    @Override
    public void dispose() {
        clearChildren();
        super.dispose();
    }

    @Override
	public final void load() {
        if (getServer().getServerState() != IServer.STATE_STARTED) {
            setError(new ErrorNode(this, "Not connected"));
            return;
        }
        try {
    		handlerChildren = getHandler().getChildren();
            clearError();
        } catch (Exception e) {
            setError(new ErrorNode(this, e));
        }
    }

	@SuppressWarnings("unchecked")
	public static List<? extends IContentNode<?>> updateChildren(List<? extends IContentNode<?>> children, List<? extends IContentNode<?>> newChildren) {
		if (children==null)
			children = newChildren;
		else {
			List<IContentNode<?>> removed = new ArrayList<IContentNode<?>>();
			Iterator<? extends IContentNode<?>> newIter = newChildren.iterator();
			while (newIter.hasNext()) {
				IContentNode<?> newChild = newIter.next();
				boolean found = false;
				Iterator<? extends IContentNode<?>> oldIter = children.iterator();
				while (oldIter.hasNext()) {
					IContentNode<?> oldChild = oldIter.next();
					if (oldChild.equals(newChild)) {
						newChild.dispose();
						found = true;
						break;
					}
				}
				if (!found) {
					((List<IContentNode<?>>)children).add(newChild);
				}
			}

			Iterator<? extends IContentNode<?>> oldIter = children.iterator();
			while (oldIter.hasNext()) {
				IContentNode<?> oldChild = oldIter.next();
				boolean found = false;
				Iterator<? extends IContentNode<?>> newIter2 = newChildren.iterator();
				while (newIter2.hasNext()) {
					IContentNode<?> newChild = newIter2.next();
					if (oldChild.equals(newChild)) {
						found = true;
						break;
					}
				}
				if (!found) {
					removed.add(oldChild);
					oldChild.dispose();
				}
			}
			children.removeAll(removed);
		}
		Collections.sort(children);
		return children;
	}

    private void clearError() {
        if (error != null) {
            error.dispose();
            error = null;
        }
    }
}
