/* CVS: $Id: LandKnowledgeCatalog.java,v 1.20 2001/03/18 19:16:33 gvijf Exp $ */

package evolution.lands;

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

import evolution.KnowledgeCatalog;
import evolution.actions.Action;
import evolution.SystemMessage;
import java.net.URL;

/**
 * @stereotype singleton
 * The LandKnowledgeCatalog 
 */
public class LandKnowledgeCatalog extends KnowledgeCatalog {

    /**
     * Create a new KnowledgeCatalog from the given file.
     */
    protected LandKnowledgeCatalog(String fileName) throws FileNotFoundException, IOException {
        super(fileName);
        parseLandTypes(getProperty("LandTypes"));
        parseInitLandTypes(getProperty("InitLandTypes"));
    }

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

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

    /**
     * Generate a hash of resource names and a reference to such a land resource,
     * populated for the given landType.
     * Returns a Map of (String, LandResource).
     * precondition: the landName must be valid.
     */
    public Map generateLandResources(String landName) {
        Map result = new HashMap();
        List lt = split(" ", getProperty(landName + ".contains", ""));
        List res = new ArrayList();
        Iterator it = lt.iterator();
        while(it.hasNext()) {
            String e = (String) it.next();
            String resourceName = extractName(e);
    	    double probability = Double.parseDouble(getProperty("LandResources." + resourceName + ".probability", "1"));
            Double value = extractValue(e);
            if (Math.random() < probability)
                result.put(resourceName, instantiateLandResource(resourceName, value, landName));
            else
                result.put(resourceName, instantiateLandResource(resourceName, new Double(0), landName));
        }
        return result;
    }

    /**
     * Instantiate a LandResource.
     * @param resName The name of the landresource.
     * @param value The value to initialize the landresource with, if this is
     * null a random value will be used within the range specified in the
     * prop file.
     * @param land The landtype.
     */
    public LandResource instantiateLandResource(String resName, Double value, String land) {
        double v;
        double min = Double.parseDouble(getProperty(land + "." + resName + ".min", "0"));
        double max = Double.parseDouble(getProperty(land + "." + resName + ".max", "100"));
        if(value == null) v = Math.random() * (max - min + 1) + min;
        else v = value.doubleValue();
        boolean visible = (new Boolean(getProperty("LandResources." + resName + ".visible", "true"))).booleanValue();
        String determination = getProperty("LandResources." + resName + ".determination", "numeric");
        return new SimpleLandResource(resName, v, min, max, visible, determination);
    }

    /**
     * The landtypes in this game.
     * Returns a List of Strings.
     */
    public List getLandTypes() {
        return landTypes;
    }

    /**
     * The landtypes which may appear on the initial map in this game.
     * Returns a List of Strings.
     */
    public List getInitLandTypes() {
        return initLandTypes;
    }

    /**
     * Parse a landtypes string.
     */
    protected void parseLandTypes(String landTypesString) {
        landTypes = parseLandTypesInto(landTypesString, null);
        SystemMessage.message(landTypes.size() + " landtypes loaded");
    }

    /**
     * Parse a initial landtypes string.
     */
    protected void parseInitLandTypes(String landTypesString) {
        initLandTypes = parseLandTypesInto(landTypesString, landTypes);
    }

    /**
     * Parse a landtypes string.
     * @param retain A List of landtype names. If this is not null, only
     * the specified landtypes will be extracted.
     */
    protected List parseLandTypesInto(String landTypesString, List retain) {
        List lt = split(" ", landTypesString);
        List types = new ArrayList();
        Iterator it = lt.iterator();
        while(it.hasNext()) {
            String type = (String) it.next();
            if((retain == null) || retain.contains(type))
                types.add(type);
        }
        return types;
    }

    /**
     * Is the given landtype an auto-transformer?
     */
    public boolean isEvolver(String landType) {
        List lt = split(" ", getProperty("AutoTransform", ""));
        return lt.contains(landType);
    }

    /**
     * To which landtype will this landtype transform automatically?
     * Returns the given landtype if it doesn't auto-transform.
     */
    public String transformsTo(String landType) {
        return extractName(getProperty(landType + ".transformsTo", landType));
    }

    /**
     * With which speed will this landtype auto-transform to the given
     * landtype.
     */
    public double transformationSpeed(String landType) {
        Double speed = extractValue(getProperty(landType + ".transformsTo", landType));
        if(speed == null) return 10;
        return speed.doubleValue();
    }

    /**
     * Which land resource transforms into which land resource.
     * Returns a map with as key the name of the initial land resource and as
     * value an array with as first element the name of the land resource
     * it transforms into and as second element a Double transformationvalue.
     * Returns a Map of (String, [String, Double]).
     */
    public Map transformsLandResources(String landType) {
        Map m = new HashMap();
        List lt = split(" ", getProperty(landType + ".transformsLandResources", ""));
        Iterator it = lt.iterator();
        while(it.hasNext()) {
            String e = (String) it.next();
            List fromTo = split("-", extractName(e));
            Double value = extractValue(e);
            m.put(fromTo.get(0), new Object[] {fromTo.get(1), value});
        }
        return m;
    }

    /**
     * Return a random landtype.
     */
    public String getRandomType() {
        int i = (int) (Math.random() * getInitLandTypes().size());
        return (String) getInitLandTypes().get(i);
    }

    /**
     * The loaded landtypes.
     * These are extracted from the properties list at construction.
     * A List of Strings.
     */
    private List landTypes;

    /**
     * Landtypes which may be used to initialize the map.
     * These are extracted from the properties list at construction. 
     * A List of Strings.
     */
    private List initLandTypes;

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