/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */
package org.netbeans.modules.maven.nodes;

import java.awt.Image;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import org.openide.ErrorManager;
import org.openide.filesystems.*;
import org.openide.nodes.AbstractNode;
import org.openide.nodes.Children;
import org.openide.util.Lookup;
import org.openide.util.RequestProcessor;


/** 
 * A node that shows filesystem annotations on icons.
 *
 * @author Milos Kleint
 */
public abstract class AnnotatedAbstractNode extends AbstractNode implements FileStatusListener, Runnable {

    private Set<FileObject> files;
    private Map<FileSystem, FileStatusListener> fileSystemListeners;
    private RequestProcessor.Task task;
    private final Object privateLock = new Object();
    private boolean iconChange;
    private boolean nameChange;
    private static final RequestProcessor RP = new RequestProcessor(AnnotatedAbstractNode.class);

    public AnnotatedAbstractNode(Children childs, Lookup lookup) {
        super(childs, lookup);
    }

    //----------------------------------------------------
// icon annotation change related, copied from j2se project.
// eventually this should end up in a sort of SPI and be shared across project types

    protected final void setFiles(Set<FileObject> files) {
        if (fileSystemListeners != null) {
            for (Map.Entry<FileSystem, FileStatusListener> e : fileSystemListeners.entrySet()) {
                e.getKey().removeFileStatusListener(e.getValue());
            }
        }

        fileSystemListeners = new HashMap<FileSystem, FileStatusListener>();
        synchronized (privateLock) {
            this.files = files;
            if (files == null) {
                return;
            }

            Set<FileSystem> hookedFileSystems = new HashSet<FileSystem>();
            for (FileObject fo : files) {
                try {
                    FileSystem fs = fo.getFileSystem();
                    if (hookedFileSystems.contains(fs)) {
                        continue;
                    }
                    hookedFileSystems.add(fs);
                    FileStatusListener fsl = FileUtil.weakFileStatusListener(this, fs);
                    fs.addFileStatusListener(fsl);
                    fileSystemListeners.put(fs, fsl);
                } catch (FileStateInvalidException e) {
                    ErrorManager err = ErrorManager.getDefault();
                    err.annotate(e, ErrorManager.UNKNOWN, "Cannot get " + fo + " filesystem, ignoring...", null, null, null); // NO18N
                    err.notify(ErrorManager.INFORMATIONAL, e);
                }
            }
        }
        fireIconChange();
        fireOpenedIconChange();
    }

    @Override
    public void run() {
        boolean fireIcon;
        boolean fireName;
        synchronized (privateLock) {
            fireIcon = iconChange;
            fireName = nameChange;
            iconChange = false;
            nameChange = false;
        }
        if (fireIcon) {
            fireIconChange();
            fireOpenedIconChange();
        }
        if (fireName) {
            fireDisplayNameChange(null, null);
        }
    }

    @Override
    public void annotationChanged(FileStatusEvent event) {
        if (task == null) {
            task = RP.create(this);
        }

        synchronized (privateLock) {
            if ((iconChange == false && event.isIconChange()) || (nameChange == false && event.isNameChange())) {
                for (FileObject fo : files) {
                    if (event.hasChanged(fo)) {
                        iconChange |= event.isIconChange();
                        nameChange |= event.isNameChange();
                    }
                }
            }
        }

        task.schedule(50); // batch by 50 ms
    }
    //----------------------------------------------------

    protected abstract Image getIconImpl(int param);

    protected abstract Image getOpenedIconImpl(int param);

    @Override
    public final Image getIcon(int param) {
        Image img = getIconImpl(param);
        return annotateImpl(img, param);
    }

    @Override
    public final Image getOpenedIcon(int param) {
        Image img = getOpenedIconImpl(param);
        return annotateImpl(img, param);
    }
    
    private Image annotateImpl(Image img, int param) {
        synchronized (privateLock) {
            if (files != null && files.size() > 0) {
                try {
                    Iterator<FileObject> it = files.iterator();
                    assert it.hasNext();
                    FileObject fo = it.next();
                    assert fo != null;
                    FileSystem fs = fo.getFileSystem();
                    assert fs != null;
                    return FileUIUtils.getImageDecorator(fs).annotateIcon(img, param, files);
                } catch (FileStateInvalidException e) {
                    ErrorManager.getDefault().notify(ErrorManager.INFORMATIONAL, e);
                }
            }
        }
        return img;
    }
}
