/*******************************************************************************
 * Copyright (c) 2011, 2013 Wind River Systems and others.
 *
 * 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:
 *     Wind River Systems - initial API and implementation
 *     IBM Corporation - bug fixing
 *******************************************************************************/
package org.eclipse.debug.internal.ui.viewers.model.provisional;

import java.util.HashSet;
import java.util.Set;

import org.eclipse.swt.SWT;


/**
 * Tree of virtual items that is analogous to SWT's tree control.  The tree is used
 * by the {@link VirtualTreeModelViewer}.
 *
 * @see VirtualTreeModelViewer
 * @since 3.8
 */
public class VirtualTree extends VirtualItem {

    /**
     * Lazy virtual tree does not retrieve elements or labels,
     * except for the selected elements.
     */
    private boolean fLazy;

    private IVirtualItemValidator fValidator;

    private class SelectedItemValidator implements IVirtualItemValidator {
        @Override
		public boolean isItemVisible(VirtualItem item) {
            // visible items.  For now only mark the selected items as visible.
            for (int i = 0; i < fSelection.length; i++) {
                VirtualItem selectionItem = fSelection[i];
                while (selectionItem != null) {
                    if (item.equals(selectionItem)) {
                        return true;
                    }
                    selectionItem = selectionItem.getParent();
                }
            }
            return false;
        }

        @Override
		public void showItem(VirtualItem item) {
        }
    }

    /**
     * Set of listeners of the virtual tree.
     */
	private Set<IVirtualItemListener> fVirtualItemListeners = new HashSet<>(1);

    /**
     * The currently selected items.  This array contains only
     * the leaf items which are selected.
     */
    private VirtualItem[] fSelection = new VirtualItem[0];

    /**
     * Constructs the virtual tree with the given style and validator.
     *
     * @param style The style flag.  Only SWT.VIRTUAL flag is used.
     * @param validator Item validator used to determine item visibility.
     */
    public VirtualTree(int style, IVirtualItemValidator validator) {
        super(null, new VirtualItem.Index(0));
        fLazy = (style & SWT.VIRTUAL) != 0;
        if (fLazy && validator == null) {
            fValidator = new SelectedItemValidator();
        } else {
            fValidator = validator;
        }
        clearNeedsLabelUpdate();
        clearNeedsDataUpdate();
    }

    /**
     * Disposes the virtual tree.
     */
    @Override
	public void dispose() {
        super.dispose();
        fVirtualItemListeners.clear();
    }

    @Override
	public void setNeedsCountUpdate() {
        super.setNeedsCountUpdate();
        clearNeedsLabelUpdate();
        clearNeedsDataUpdate();
    }

    @Override
	public void setNeedsLabelUpdate() {
        // no-op
    }

    @Override
	public void setData(String key, Object data) {
        super.setData(key, data);
        if (data == null) {
            clearNeedsDataUpdate();
        }
    }

    /**
     * Adds a listener for when virtual items are revealed in the view.
     * @param listener Listener to add to list of listeners.
     */
    public void addItemListener(IVirtualItemListener listener) {
        fVirtualItemListeners.add(listener);
    }

    public void removeItemListener(IVirtualItemListener listener) {
        fVirtualItemListeners.remove(listener);
    }

    public VirtualItem[] getSelection() {
        return fSelection;
    }

    public void setSelection(VirtualItem[] items) {
        fSelection = items;
    }

    public void showItem(VirtualItem item) {
        if (fValidator != null) {
            fValidator.showItem(item);
        }
    }

    public void fireItemDisposed(VirtualItem item) {
		for (IVirtualItemListener listener : fVirtualItemListeners) {
			listener.disposed(item);
        }
    }

    public void fireItemRevealed(VirtualItem item) {
		for (IVirtualItemListener listener : fVirtualItemListeners) {
			listener.revealed(item);
        }
    }

    @Override
	public void setData(Object data) {
        super.setData(data);
        // The root item always has children as long as the input is non-null,
        // so that it should be expanded.
        setHasItems(data != null);
    }

    @Override
	public void setHasItems(boolean hasChildren) {
        super.setHasItems(hasChildren);
        // The root item is always expanded as long as it has children.
        if (hasChildren) {
            setExpanded(true);
        }
    }

    /**
     * Returns whether the given item is considered visible by the tree as
     * determined by its virtual item validator.
     *
     * @param item Item to check.
     * @return true if items is vislble.
     * @see IVirtualItemValidator
     */
    public boolean isItemVisible(VirtualItem item) {
        if (fLazy) {
            return fValidator.isItemVisible(item);
        }
        return true;
    }

    /**
     * Validates the entire tree.
     */
    public void validate() {
        validate(VirtualTree.this);
    }

    /**
     * Validates the item and its children, identifying children which were
     * revealed and need to be updated.
     *
     * @param item The item which to validate.
     */
    public void validate(VirtualItem item) {
        if (item.needsDataUpdate()) {
            if (isItemVisible(item)) {
                fireItemRevealed(item);
            }
        } else if (item.getData() != null) {
            if ( item.needsLabelUpdate() || (item.needsCountUpdate() && item.hasItems() && item.getExpanded()) ) {
                if (isItemVisible(item)) {
                    fireItemRevealed(item);
                }
            }

            if (item.getData() != null && item.getItemCount() > 0 && item.getExpanded()) {
                for (int i = 0; i < item.getItemCount(); i++) {
                    validate(item.getItem(new Index(i)));
                }
            }
        }
    }
}
