/* CVS: $Id: ResourceKnowledgeCatalog.java,v 1.30 2001/03/18 20:54:12 gvijf Exp $ */

package evolution.resources;

import java.util.*;
import java.io.*;

import evolution.KnowledgeCatalog;
import evolution.SystemMessage;
import evolution.InfoList;
import evolution.actions.ActionKnowledgeCatalog;
import evolution.constructions.ConstructionKnowledgeCatalog;

/**
 * The Resource KnowledgeCatalog.
 * @stereotype singleton
 */
public class ResourceKnowledgeCatalog extends KnowledgeCatalog {

    /**
     * Create a new KnowledgeCatalog from the given file.
     */
    protected ResourceKnowledgeCatalog(String fileName) throws IOException, FileNotFoundException {
        super(fileName);
        parseResources(getProperty("Resources"));
    }

    /**
     * Return the instance of this singleton.
     */
    public static ResourceKnowledgeCatalog getInst() {
        if(instance == null)
            throw new RuntimeException("ResourceKnowledgeCatalog was not initialized");
        return instance;
    }

    /**
     * Initialize this singleton from the given file.
     */
    public static void initialize(String fileName) throws FileNotFoundException, IOException {
        instance = new ResourceKnowledgeCatalog(fileName);
    }

    /**
     * Generate an info list of all available resources.
     */
    public InfoList getResourcesInfo() {
        InfoList l = new InfoList();
        Iterator it = getResourcesNames().iterator();
        while(it.hasNext()) {
            String resName = (String) it.next();
            Resource res = (Resource) getResource(resName);
            l.add(resName, InfoList.VALUE, res.getValue());
        }
        return l;
    }

    /**
     * An map of all resources which are connected to the current world.
     * @return A Map of (String, Resource).
     */
    public Map getResourceInstances() {
        return resourcesMap;
    }

    /**
     * Return the resource with the given name.
     */
    public Resource getResource(String resName) {
        return (Resource) resourcesMap.get(resName);
    }

    /**
     * Parse the resources string.
     */
    protected void parseResources(String resourcesString) {
        List r = split(" ", resourcesString);
        resourcesMap = new HashMap();
        Resource energy = new Energy();
        resourcesMap.put("Energy", energy);
        resources.add("Energy");
        resourcesMap.put("Max Energy", new SimpleResource("Max Energy", 0, 0, Double.MAX_VALUE, Resource.LINEAR));
        resources.add("Max Energy");
        Iterator it = r.iterator();
        while(it.hasNext()) {
            String part = (String) it.next();
            String resourceName = extractName(part);
            if(resourcesMap.containsKey(resourceName))
                throw new RuntimeException(resourceName + " is a reserved resource name or already in use, sorry dude");
            Double value = new Double(getProperty(resourceName + ".init", "0"));
            Double min = new Double(getProperty(resourceName + ".min", "0"));
            Double max = new Double(getProperty(resourceName + ".max", Double.MAX_VALUE + ""));
            String curve = getProperty(resourceName + ".curve", "LINEAR");
            Integer function = new Integer(Resource.LINEAR);
            if(curve.equals("SLOPE")) function = new Integer(Resource.SLOPE);
            else if(!curve.equals("LINEAR"))
                throw new RuntimeException("Unknow resource curve " + curve);
            try {
                // A resource with a real class underneath
	            Class cl = dlClassForName("resources", resourceName);
                Resource res = (Resource) cl.
                    getConstructor(new Class[] {
                    	double.class,
                        double.class,
                        double.class,
                        int.class}).
                    newInstance(new Object[] {
                    	value,
                        min,
                        max,
                		function});
                resourcesMap.put(resourceName, res);
                resources.add(resourceName);
            } catch(ClassNotFoundException e) {
                // a SimpleResource case
                Resource res = new SimpleResource(resourceName,
                    value.doubleValue(), min.doubleValue(), max.doubleValue(),
                    function.intValue());
                resourcesMap.put(resourceName, res);
                resources.add(resourceName);
            } catch(Exception e) {
                throw new RuntimeException("No proper constructor for " + resourceName);
            }
        }
    }

    /**
     * Increase or decrease the named resource with the given value.
     */
    public void modResource(String resourceName, double v) throws NotEnoughResourcesException {
        Resource res = (Resource) resourcesMap.get(resourceName);
        if(res == null) {
            System.err.println("Trying to modify unknown resource " + resourceName);
            return;
        }
        res.modValue(v);
    }

    /**
     * Increase or decrease the named resource with the given value. The amount of
     * increase or decrease will also depend on the resources that influence the
     * named action.
     */
    public void modResource(String resourceName, String actionName, double v) throws NotEnoughResourcesException {
        double influencedValue = influencedByResources(actionName, v);
        Resource res = (Resource) resourcesMap.get(resourceName);
        if(res == null) {
            System.err.println("Trying to modify unknown resource " + resourceName);
            return;
        }
        res.modValue(influencedValue);
    }

    /**
     * Influence a value by all resources that influence the given action.
     */
    public double influencedByResources(String actionName, double v) {
        List influencedBy = ActionKnowledgeCatalog.getInst().influencedBy(actionName);
        Iterator it = influencedBy.iterator();
        double influencedValue = v;
        while (it.hasNext()) {
            String influenceName = (String) it.next();
	    Resource influenceRes = (Resource) resourcesMap.get(influenceName);
        	if (influenceRes == null)
            	System.err.println("Action " + actionName + "is influenced by an unknown resource " + influenceName);
	        else influencedValue = influenceRes.influence(influencedValue);
        }
        return influencedValue;
    }

    /**
     * See from which resources we can fullfill the need for this one.
     * Returns a List of Object[]{Resource, Double}.
     */
    public List transformsFrom(String resourceName) {
        List result = new ArrayList();
        List l = split(" ", getProperty(resourceName + ".from", ""));
        Iterator it = l.iterator();
        while(it.hasNext()) {
            String e = (String) it.next();
            String resName = extractName(e);
            Resource res = (Resource) resourcesMap.get(resName);
            Double ratio = extractValue(e);
            Object[] o = new Object[]{res, ratio};
            result.add(o);
        }
        return result;
    }

    /**
     * Is the named resource a temporary one?
     */
    public boolean isTemporary(String resourceName) {
        return (new Boolean(getProperty(resourceName + ".temporary", "false"))).booleanValue();
    }

    /**
     * Reset all temporary resources to their minimum.
     */
    public void resetTemporaries() {
        Iterator it = getResourcesNames().iterator();
        while(it.hasNext()) {
            String resName = (String) it.next();
            if(isTemporary(resName)) {
                Resource r = getResource(resName);
                try {
                    r.setValue(r.getMin());
                } catch(NotEnoughResourcesException e) {
                }
            }
        }
    }

    /**
     * Set all the temporary resources.
     * @param max A (String, Double) Map, where the String is the name
     * of the resource and the Double is the maximum producable.
     * @param percentage The percentage we have to set of the maximum
     * for each temporary resource.
     */
    public void setTemporaries(Map max, double percentage) {
        Iterator it = getResourcesNames().iterator();
        while(it.hasNext()) {
            String resName = (String) it.next();
            if(isTemporary(resName)) {
                Resource r = getResource(resName);
                try {
                    r.setValue(((Double) max.get(resName)).doubleValue() *
                        percentage);
                } catch(NotEnoughResourcesException e) {
                }
            }
        }
    }

    /**
     * Get the current world resources as a (String, Double) Map.
     */
    public Map getResourcesStringDoubleMap() {
        Map result = new HashMap();
        Iterator it = getResourcesNames().iterator();
        while(it.hasNext()) {
            String resName = (String) it.next();
            result.put(resName, new Double(getResource(resName).getValue()));
        }
        return result;
    }

    /**
     * Set the worldresources.
     * @param resMap A Map of (String, Double).
     */
    public void setResources(Map resMap) {
        Iterator it = resMap.keySet().iterator();
        while(it.hasNext()) {
            String name = (String) it.next();
            Double value = (Double) resMap.get(name);
            try {
                getResource(name).setValue(value.doubleValue());
            } catch(NotEnoughResourcesException e) {
            }
        }
    }

    /**
     * Add a percentage of a maximum to the world resources.
     * @param resMap A Map of (String, Double).
     * @param percentage The percentage to add.
     */
    public void addResources(Map resMap, double percentage) {
        Iterator it = resMap.keySet().iterator();
        while(it.hasNext()) {
            String name = (String) it.next();
            Double value = (Double) resMap.get(name);
            Resource r = getResource(name);
            try {
                r.setValue(r.getValue() + value.doubleValue()*percentage);
            } catch(NotEnoughResourcesException e) {
            }
        }
    }

    /**
     * The names of the worldresources.
     * A List of Strings.
     */
    public List getResourcesNames() {
        return resources;
    }

    /**
     * The names of the worldresources.
     * This is kept separate to keep the order of the worldresources.
     * A List of Strings.
     */
    private List resources = new ArrayList();

    /**
     * The worldresources.
     * A Map of (String, Resource).
     */
    private Map resourcesMap;

    /**
     * Instance of the singleton.
     */
    protected static ResourceKnowledgeCatalog instance = null;
}
