package vermilion;

import java.util.ArrayList;
import JSSPF.*;

public class SystemPluginManager implements Runnable
{
    // The system preference name we use to find the main system modules.
    public static final String STARTUP_PLUGINS_PREF = "Startup Plugins";

    // Stores the list of system plugin instances created at startup.
    private ArrayList<PluginClassVermilion> system = new ArrayList<PluginClassVermilion>();

    private PluginClassManager pluginSource;

    private ArrayList<String> messageQueue = new ArrayList<String>();
    private ArrayList<Object> msgObjQueue = new ArrayList<Object>();

    public SystemPluginManager()
    {
        pluginSource = new PluginClassManager("plugins/", false);
    }

    public PluginClassVermilion getPlugin(String pluginName)
    {
        PluginClassBase base = pluginSource.getPlugin(pluginName);
        if (base instanceof PluginClassVermilion) return (PluginClassVermilion) base;
        return null;
    }

    public ArrayList<PluginClassDef> getPluginList()
    {
        return pluginSource.getPluginList();
    }

    /**
     * Creates instances of plugins listed as startup plugins and initialises
     * them, allowing them to build GUIs, connect to system components etc.
     */
    public synchronized void loadSystem()
    {
        // Load startup plugins and call initialise on them.
        UsefulFileObjectList startupPlugins = (UsefulFileObjectList) Main.settings.getValue(STARTUP_PLUGINS_PREF);
        if (startupPlugins != null)
        {
            for (int x = 0; x < startupPlugins.size(); x++)
            {
                UsefulFileObjectSimple obj = (UsefulFileObjectSimple) startupPlugins.get(x);
                String str = obj.strValue;
                PluginClassVermilion oBase = (PluginClassVermilion) getPlugin(str);
                if (oBase != null)
                {
                    try
                    {
                    oBase.initialise();
                    system.add(oBase);
                    }
                    catch (Exception ex)
                    {
                        System.out.println("Exception initialising plugin '" + oBase.getName() + "': " + ex.toString());
                    }
                }
            }
        }
    }

    /**
     * Called during reinitialisation or closing, unloads each system plugin
     * loaded at startup.
     */
    public synchronized void unloadSystem()
    {
        // Load startup plugins and call initialise on them.
        for (PluginClassVermilion oBase : system)
        {
            oBase.deinitialise();
        }

        system.clear();
    }

    /**
     * Calls documentLoaded on all system plugins, so they can
     * read from the composition and create runtime data.
     */
    public synchronized void documentLoaded()
    {
        for (PluginClassVermilion oBase : system)
        {
            oBase.documentLoaded();
        }
    }

    /**
     * Calls documentSaving on all system plugins, so they can
     * write a representation of runtime data into the composition.
     */
    public synchronized void documentSaving()
    {
        for (PluginClassVermilion oBase : system)
        {
            oBase.documentSaving();
        }
    }

    /**
     * Sends a message to all system plugins, allowing communication between
     * system plugins for situations where information must be passed or a
     * document update requires systems to refresh their data. The message is
     * enqueued and posted next time the main window timer runs, to ensure
     * all operations stay on the GUI thread.
     * @param message A string indicating what has happened.
     */
    public synchronized void sendMessage(String message, Object msgVal)
    {
        System.out.println("Queueing message '" + message + "'");
        messageQueue.add(message);
        msgObjQueue.add(msgVal);
    }

    protected synchronized void pumpMessages()
    {
        while (messageQueue.size() > 0)
        {
            String str = messageQueue.get(0);
            Object obj = msgObjQueue.get(0);

            System.out.println("Sending message '" + str + "' to " + system.size() + " plugins");

            for (PluginClassVermilion oBase : system)
            {
                try
                {
                    oBase.processMessage(str, obj);
                }
                catch (Exception ex)
                {
                    System.out.println(ex.toString());
                }
            }

            messageQueue.remove(0);
            msgObjQueue.remove(0);
        }
    }

    @Override
    public void run()
    {
        pumpMessages();
    }
}
