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

import java.awt.event.ActionEvent;
import java.util.Collection;
import javax.swing.Action;
import org.openide.util.ContextAwareAction;
import org.openide.util.Lookup;
import org.openide.util.LookupEvent;
import org.openide.util.LookupListener;
import org.openide.util.Mutex;

/**Inner stub action class which delegates to the parent action's methods.
* Used both for context aware instances, and for internal state for
* ContextAction instances
* @author Tim Boudreau
*/
class ActionStub<T> extends NbAction implements LookupListener, ContextAwareAction {

    private final Lookup.Result<T> lkpResult;
    private final Lookup context;
    protected final ContextAction<T> parent;
    protected boolean enabled;

    ActionStub(Lookup context, ContextAction<T> parent) {
        assert context != null;
        this.context = context;
        this.parent = parent;
        lkpResult = context.lookupResult(parent.type);
        lkpResult.addLookupListener(this);
        if (getClass() == ActionStub.class) {
            //avoid superclass call to Retained.collection() before
            //it has initialized
            enabled = isEnabled();
        }
    }

    @Override
    public Object getValue(String key) {
        Object result = _getValue(key);
        if (result == null) {
            result = parent.getValue(key);
        }
        return result;
    }

    Object _getValue(String key) {
        if (!attached()) {
            //Make sure any code that updates the name runs - we are not
            //listening to the lookup
            resultChanged(null);
        }
        return pairs.get(key);
    }

    Collection<? extends T> collection() {
        return lkpResult.allInstances();
    }
    
    public boolean isEnabled() {
        Collection<? extends T> targets = collection();
        assert targets != null;
        assert parent != null;
        return targets.isEmpty() ? false : parent.checkQuantity(targets) &&
                parent.isEnabled(targets);
    }

    public void actionPerformed(ActionEvent e) {
        assert isEnabled() : "Not enabled: " + this + " collection " +
                collection() + " of " + parent.type.getName();
        Collection<? extends T> targets = collection();
        parent.actionPerformed(targets);
    }

    void enabledChanged(final boolean enabled) {
        Mutex.EVENT.readAccess(new Runnable() {
            public void run() {
                firePropertyChange(PROP_ENABLED, !enabled, enabled); //NOI18N
                if (ContextAction.unitTest) {
                    synchronized (parent) {
                        parent.notifyAll();
                    }
                    synchronized (this) {
                        this.notifyAll();
                    }
                }
            }
        });
    }

    public void resultChanged(LookupEvent ev) {
        if (ContextAction.unitTest) {
            synchronized (parent) {
                parent.notifyAll();
            }
        }
        synchronized(parent.lock()) {
            parent.change (collection(), this == parent.stub ? parent : this);
        }
        boolean old = enabled;
        enabled = isEnabled();
        if (old != enabled) {
            enabledChanged(enabled);
        }
    }

    @Override
    public String toString() {
        return super.toString() + "[name=" + getValue(NAME) + //NOI18N
                "delegating={" + parent + "} context=" + //NOI18N
                context + "]"; //NOI18N
    }

    @Override
    protected NbAction internalCreateContextAwareInstance(Lookup actionContext) {
        return parent.createStub(actionContext);
    }

    @Override
    public boolean equals(Object o) {
        return o != null && o.getClass() == getClass() && parent.equals(((ActionStub<?>) o).parent);
    }

    @Override
    public int hashCode() {
        return parent.hashCode() * 37;
    }
}
