/*
 * Scone - The Web Enhancement Framework
 * Copyright (C) 2009 Harald Weinreich, Volkert Buchmann, Frank Wollenweber, Torsten Haß
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 */
package  scone.util;


import java.io.FileReader;
import java.io.FileWriter;
import java.util.Enumeration;
import java.util.Vector;

import org.w3c.dom.Document;
import org.w3c.dom.Element;


/**
 * a repository of Scone plugins. The plugins' states (enabled/disabled)
 * are mirrored in a configuration file to make them persistent.
 */
public class PluginRepository {
    // the xml document representing the plugins and their states
    private Document dom = null;
    // the name of the xml file that contains that document
    private final static String configFileName = "config/scone/plugins.xml";
    // the entries
    private Vector entries = new Vector();
    // the requirements complied from the initially enabled plugins
    private int requirements = 0;

    /**
     * creates a new repository. The repository definition is read from
     * the file. For each plugin element, a PluginRepositoryEntry is created.
     * This constructor also compiles the plugins' requirements.
     */
    public PluginRepository() {
        try {
            dom = XMLHelper.parseReader(new FileReader(configFileName));
            NodeEnumeration nodes = new NodeEnumeration(dom.getFirstChild(), "plugin");
            Element el = null;
            PluginRepositoryEntry entry = null;

            while (nodes.hasMoreNodes()) {
                try {
                    el = (Element) nodes.nextNode();
                    entry = new PluginRepositoryEntry(el);
                    entries.addElement(entry);
                    requirements |= entry.getRequirements();
                } catch (Exception exc) {
                    ErrorLog.log(this, "<init>", "", exc);
                }
            }
        } catch (Exception ex) {
            ErrorLog.log("", "PluginRepository <init>", "Could not read " + configFileName,
                    ex);
            System.out.println("Could not read " + configFileName);
            System.exit(0);
        }
    }

    /**
     * terminates the plugins 
     * does not disable them
     */
    public void terminate() {
        for (Enumeration en = getEntries(); en.hasMoreElements();) {
            try {
                ((PluginRepositoryEntry) en.nextElement()).terminate();
            } catch (Exception x) {}
         	
        }		
    }
	
    /**
     * returns the entry for the named plugin.
     * @param name the full classname of the plugin
     * @return the entry for that plugin or null if not found
     */
    public PluginRepositoryEntry getEntryByName(String name) {
        Enumeration en = getEntries();

        while (en.hasMoreElements()) {
            PluginRepositoryEntry entry = (PluginRepositoryEntry) en.nextElement();

            if (entry.getName().equals(name)) {
                return  entry;
            }
        }
        return  null;
    }

    /**
     * returns the entry at the given position
     * @param pos the position of the entry
     * @return the entry
     */
    public PluginRepositoryEntry getEntryByNumber(int pos) {
        return  (PluginRepositoryEntry) entries.elementAt(pos);
    }

    /**
     * registers a new plugin. If a plugin with the given name exists,
     * a new entry for that plugin will be generated and added to the repository.
     * There cannot be two plugins with the same name in the repository. A
     * RuntimeException will be thrown in case this method is used to register a registered plugin.
     * Please not that Scone should be restarted to ensure that the plugin's 
     * requirements are met.
     * @param name the full classname of the plugin
     * @return the entry for the plugin if successful
     */
    public PluginRepositoryEntry register(String name) {
        try {
            // is it already registered?
            if (getEntryByName(name) != null) { 
                throw new RuntimeException("The plugin " + name + " has already been registered!");
            }
            // does it exist?
            Class.forName(name).newInstance();
            // is it already registered?
            PluginRepositoryEntry entry = getEntryByName(name);

            if (entry != null) {
                return  entry;
            }
            Element node = dom.createElement("plugin");

            node.setAttribute("class", name);
            node.setAttribute("enabled", "yes");
            dom.getFirstChild().appendChild(node);
            entry = new PluginRepositoryEntry(node);
            entries.addElement(entry);
            requirements |= entry.getRequirements();
            return  entry;
        } catch (ClassNotFoundException cnfe) {
            ErrorLog.log(this, "register()", "Class not found: " + name, cnfe);
        } catch (Exception e) {
            ErrorLog.log(this, "register()", "", e);
        }
        return  null;
    }

    /**
     * stores the XML definition of the repository to the file.
     */
    public void store() {
        try {
            DOMWriter writer = new DOMWriter(new FileWriter(configFileName), false);

            writer.print(dom);
        } catch (Exception exc) {
            ErrorLog.log(this, "store()", "Could not store " + configFileName, exc);
        }
    }

    /**
     * returns an enumeration of the entries.
     * @return the entries
     */
    public Enumeration getEntries() {
        return  entries.elements();
    }

    /**
     * returns the requirements of the enabled registered plugins.
     * @return the requirements
     */
    public int getRequirements() {
        return  requirements;
    }
}
