package de.renew.plugin;

import java.util.ArrayList;
import java.util.Collection;


/**
 * Instances of this class represent a service tracker for plug-ins requiring a certain service.<br>
 * To get notified of tracked plug-ins, classes extending this class must override <br>
 * {@code bindService(IPlugin application)} and {@code unbindService(IPlugin application)}.
 *
 * @author Eva Mueller
 * @date Nov 14, 2010
 * @version 0.1
 */
public class ApplicationTracker implements IPluginManagerListener {
    /**
     * Logger for logging purposes as specified by Apache Log4j
     */
    public static final org.apache.log4j.Logger LOGGER =
        org.apache.log4j.Logger.getLogger(ApplicationTracker.class);

    /**
     * The required service to listen for.
     **/
    protected String _requiredService;


    /**
     * List of plug-ins requiring the tracker's <b>requiredService</b>.
     */
    protected Collection<IPlugin> _applicationList;

    /**
     * The plugin manager this tracker refers to.
     **/
    private PluginManager _mgr;

    /**
     * Instantiates a new <b>ApplicationTracker</b> tracking plug-ins<br>
     * which require the given <b>requiredService</b>.<br>
     * <br>
     * To start tracking the method {@code open()} must be called.
     *
     * @param requiredService [String] The service to track for
     */
    public ApplicationTracker(String requiredService) {
        this._requiredService = requiredService;
        this._applicationList = new ArrayList<IPlugin>();
        this._mgr = PluginManager.getInstance();
    }


    /**
     * Start tracking of plug-ins which require the tracker's <b>requiredService</b>.<br>
     * <br>
     * <span style="color: red;">Note</span> : Plug-ins which are already added to the {@link PluginManager}<br>
     * and require the tracker's <b>requiredService</b> are tracked as well.
     */
    public void open() {
        _mgr.addPluginManagerListener(this);
        bindServices(_mgr.getPluginsRequiring(_requiredService));
    }

    /**
     * Stop tracking of plug-ins which require the tracker's <b>requiredService</b>.
     */
    public synchronized void close() {
        _mgr.removePluginManagerListener(this);
        unbindServices(_mgr.getPluginsRequiring(_requiredService));
    }

    /**
     * Bind given <b>applicationList</b> to the tracker.
     *
     * @param applicationList [Collection&lt;{@link IPlugin}&gt;] List of {@link IPlugin}'s
     *         requiring the tracker's <b>requiredService</b>
     */
    private void bindServices(Collection<IPlugin> applicationList) {
        if (applicationList != null) {
            for (IPlugin provider : applicationList) {
                bindService(provider);
            }
        }
    }

    /**
     * Unbind given <b>applicationList</b> from the tracker.
     *
     * @param applicationList [Collection&lt;{@link IPlugin}&gt;] List of {@link IPlugin}'s
     *         requiring the tracker's <b>requiredService</b>
     */
    private void unbindServices(Collection<IPlugin> applicationList) {
        for (IPlugin provider : applicationList) {
            unbindService(provider);
        }
        assert this._applicationList.isEmpty() : "There is a difference between the \n"
            + "list of plugins requiring " + _requiredService + " of the plugin manager and "
            + "the application service tracker (" + this + ").\n" + "[Tracked required service: "
            + _requiredService + "]." + "\nRemaining plugins : " + this._applicationList + ".";
    }

    /**
     * Bind given <b>application</b> to the tracker.
     *
     * @param application [{@link IPlugin}] The plug-in requiring the tracker's <b>requiredService</b>
     */
    protected void bindService(IPlugin application) {
        if (application != null
            && application.getProperties().getRequirements().contains(_requiredService)) {
            this._applicationList.add(application);
        }
    }

    /**
     * Unbind given <b>application</b> from the tracker.
     *
     * @param application [{@link IPlugin}] The plug-in requiring the tracker's <b>requiredService</b>
     */
    protected void unbindService(IPlugin application) {
        if (application != null && this._applicationList.contains(application)) {
            this._applicationList.remove(application);
        }
    }

    /**
     * <span style="color: red;">To be called by the plugin manager only.</span>
     *
     * @param service [String] {@code UNUSED}
     * @param application [{@link IPlugin}] The newly added plug-in
     */
    @Override
    public synchronized void serviceAdded(final String service, IPlugin application) {
        if (application != null) {
            bindService(application);
        }
    }

    /**
     * <span style="color: red;">To be called by the plugin manager only.</span>
     *
     * @param service [String] {@code UNUSED}
     * @param application [{@link IPlugin}] The newly removed plug-in
     */
    @Override
    public synchronized void serviceRemoved(String service, IPlugin application) {
        if (application != null) {
            unbindService(application);
        }
    }
}