/* CVS: $Id: Construction.java,v 1.33 2001/03/19 04:20:13 gvijf Exp $ */

package evolution.constructions;

import java.util.*;

import evolution.*;
import evolution.lands.*;
import evolution.resources.*;
import evolution.*;
import evolution.actions.*;
import evolution.events.*;

/**
 * Abstract class of constructions.
 */
abstract public class Construction implements Evolver {

    /**
     * Create a new construction.
     */
    protected Construction() {
        setSquares(new ArrayList());
    }

    /**
     * Create a new construction.
     */
    public Construction(List squares) {
        setSquares(squares);
    }

    /**
     * The checks which are made in evolve.
     * This is a template method.
     */
    protected void doChecks(double percentage) throws NotEnoughResourcesException, NotEnoughLandResourcesException {
        if(!hasEnoughResources(percentage))
            throw new NotEnoughResourcesException();
        if(!hasEnoughLandResources(percentage))
            throw new NotEnoughLandResourcesException();
    }

    /**
     * The evolution for the construction when Evolution ticks time.
     * @param percentage The percentage of production.
     */
    public void evolve(double percentage) {
        setWorking(false);
        if(percentage == 0) {
            // if we have to produce 0%, then we can return immediately
            return;
        }
        try {
            doChecks(percentage);
	        // consume resources + land resources
        	modifyResources(getUsesResources(), -1.0*percentage); // can throw NotEnoughResourcesException
            modifyLandResources(getSquares(), getUsesLandResources(), -1.0*percentage); // can throw NotEnoughLandResourcesException
	        // produce resources (+ land resources)
            // in ons spel worden er voorlopig nooit lan resources geproduceerd
            modifyResources(getProducesResources(), 1.0*percentage);
	        _evolve(percentage);
        } catch (NotEnoughResourcesException e){
        } catch (NotEnoughLandResourcesException e) {
            // In ons spel is het momenteel zo dat je nu nooit de world-resources
            // moet herstellen: de enige construction die land resources gebruikt
            // zijn immers de coalmines, en die gebruiken geen world resources.
            // daarom ook dat eerst alles geconsumeerd wordt vooraleer de constructie
            // iets gaat produceren...
        }
        setWorking(true);
    }

    /**
     * Modify the world resources for this construction.
     * @param resources A Map of (String, Double) resources to modify.
     * @param mul A multiplier for the values in the map.
     */
     protected void modifyResources(Map resources, double mul) throws NotEnoughResourcesException {
        System.err.println(getName() + ": " + mul + " * " + resources);
        Iterator it = resources.keySet().iterator();
        while(it.hasNext()) {
            String resName = (String) it.next();
            ResourceKnowledgeCatalog.getInst().modResource(resName, mul * ((Double) resources.get(resName)).doubleValue());
        }
    }

    /**
     * Modify the land resources for this construction.
     * @param resources A Map of (String, Double) resources to modify.
     * @param mul A multiplier for the values in the map.
     */
    protected void modifyLandResources(List squares, Map resources, double mul) throws NotEnoughLandResourcesException {
        System.err.println(getName() + ": " + mul + " * " + resources);
        Iterator it1 = squares.iterator();
        while (it1.hasNext()) {
            SquareOfLand square = (SquareOfLand) it1.next();
        	Iterator it = resources.keySet().iterator();
	        while(it.hasNext()) {
    	        String resName = (String) it.next();
        	    square.modResource(resName, mul * ((Double) resources.get(resName)).doubleValue());
	        }
        }
    }

    /**
     * Additional things which should be done when this construction's evolve
     * method is called.
     * Template method.
     */
    protected abstract void _evolve(double percentage);

    /**
     * Checks whether this construction had enough worldresources to be performed.
     */
    public boolean hasEnoughResources(double percentage) {
        Map neededResources = getUsesResources();
        Iterator it = neededResources.keySet().iterator();
        while (it.hasNext()) {
            String key = (String) it.next();
            double resourceValue = ((Double) neededResources.get(key)).doubleValue();
            try {
                if (!World.getInst().getResource(key).has(resourceValue * percentage)) {
                    setWorking(false);
                    System.err.println(getName() + " is missing " + key);
                    return false;
                }
            } catch (NoSuchResourceException e) {
                //can't happen
            }
        }
        setWorking(true);
        return true;
    }

    /**
     * Checks whether this construction had enough landresources to be performed.
     */
    public boolean hasEnoughLandResources(double percentage) {
        return true;
    }

    /**
     *
     */
    //public double getTotalLandResource(String name) {
    //    Iterator it = getSquares().iterator();
    //    double res = 0;
    //    while (it.hasNext()) {
    //        LandResource lr = ((LandResource) ((SquareOfLand) it.next()).getLandResource(name));
    //        res += lr.getValue();
    //    }
    //    return res;
    //}

    /**
     * Does this evolver produce energy?
     */
    public boolean producesEnergy() {
        List prodresnames = producesResourcesNames();
        Iterator it = prodresnames.iterator();
        while (it.hasNext()) {
            if (ConstructionKnowledgeCatalog.getInst().isEnergyResource((String)it.next())) {
                return true;
            }
        }
        return false;
    }

    /**
     * Return the maximum energy production of this evolver.
     * @param maxProduction A (String, Double) map wich holds the maximal
     * production of worldresources.
     * @param leftOver A (String, Double) map wich holds the actual status
     * of worldresources in calculating the maximum energy production.
     */
    public double maxEnergyProduction(Map maxProduction, Map leftOver) {
        Map producesResources = ConstructionKnowledgeCatalog.getInst().producesResources(getName());
        Map usesResources = ConstructionKnowledgeCatalog.getInst().usesResources(getName());
        System.err.println("prio = " + getPriority());
        System.err.println("max " + getName() + ": " + producesResources);
        System.err.println("max " + getName() + ": " + usesResources);
        Iterator it1 = usesResources.keySet().iterator();
        while (it1.hasNext()) {
            String name = (String) it1.next();
            double left = ((Double) leftOver.get(name)).doubleValue();
            double needed = ((Double) usesResources.get(name)).doubleValue();
            if(left >= needed) {
                leftOver.put(name, new Double(left - needed));
            } else return 0;
        }
        Iterator it2 = producesResources.keySet().iterator();
        while (it2.hasNext()) {
            String name = (String) it2.next();
            double v = ((Double) producesResources.get(name)).doubleValue();
            double p = ((Double) maxProduction.get(name)).doubleValue();
            double u = ((Double) leftOver.get(name)).doubleValue();
            maxProduction.put(name, new Double(p + v));
            leftOver.put(name, new Double(u + v));
        }
        return ((Double) maxProduction.get("Energy")).doubleValue();
    }

    /**
     * Returns a map of the names of resources that are produced
     * by this action.
     */
    protected Map getProducesResources() {
        return ConstructionKnowledgeCatalog.getInst().producesResources(getName());
    }

    /**
     * Returns a map of the names of resources that are used
     * by this action.
     */
    protected Map getUsesResources() {
        return ConstructionKnowledgeCatalog.getInst().usesResources(getName());
    }

    /**
     * Returns a map of the names of landresources that are used
     * by this action.
     */
    protected Map getUsesLandResources() {
        return ConstructionKnowledgeCatalog.getInst().usesLandResources(getName());
    }

    /**
     * Returns the name of the construction.
     */
    public String getName() {
        return ConstructionKnowledgeCatalog.getInst().stripPathFromClassName(this);
    }

    /**
     * Returns the size of the construction.
     */
    public int getSize() {
        return getSquares().size();
    }

    /**
     * Returns the priority of this construction.
     */
    public double getPriority() {
        return ConstructionKnowledgeCatalog.getInst().getPriority(this);
    }

    /*
     * Return the construction state.
     * If the construction is being build this is "C" + the construction name.
     * If the construction is in production this is just the construction name.
     */
    public String getState() {
        if(isBeingBuild()) return "C" + getName();
        if(!isWorking()) return "Not" + getName();
        return getName();
    }

    /**
     * An InfoList with the properties of this building.
     */
    public InfoList getInfo() {
        InfoList result = new InfoList();
        result.add("Type", InfoList.STRING, getName());
        if(isBeingBuild()) {
            result.add("Construction state", InfoList.PERCENTAGE, getConstructionState());
        } else {
            result.add("Producing", InfoList.BOOLEAN, isWorking());
        }
        return result;
    }

    /**
     * Is this construction being build?
     */
    public boolean isBeingBuild() {
        return constructed < 100;
    }

    /**
     * Is this construction in production state?
     */
    public boolean isProduction() {
        return constructed >= 100;
    }

    /**
     * Construct some percents of this construction.
     * @param inc The percentage to construct.
     */
    public void construct(double inc) {
        constructed += inc;
        if(constructed >= 100) {
            constructed = 100;
            SystemMessage.message("A " + getName() + " construction is now alive and will start producing!");
            Evolution.getInst().register(this);
            signalStateChange();
        }
       // System.err.println(getName() + ".constructed = " + constructed + "%");
    }

    /**
     * Signal a SquareChangedEvt on every square of this construction.
     */
    protected void signalStateChange() {
        Iterator it = getSquares().iterator();
        while(it.hasNext()) {
            EventManager.getInst().signalEvent(new SquareChangedEvt(
                (SquareOfLand) it.next()));
        }
    }

    /**
     * The percentage of construction.
     */
    public double getConstructionState() {
        return constructed;
    }

    /**
     * A list with the names of the resources produced by this construction.
     */
    public List producesResourcesNames() {
        return ConstructionKnowledgeCatalog.getInst().producesResourcesNames(this.getName());
    }

    /**
     * The squares where this construction is build on.
     */
    public List getSquares() {
        return squares;
    }

    /**
     * Set the squares where this construction is standing.
     */
    protected void setSquares(List squares) {
        this.squares = squares;
        //System.err.println("Construction on: " + squares);
        Iterator it = squares.iterator();
        while(it.hasNext()) {
            SquareOfLand square = (SquareOfLand) it.next();
            try {
                square.place(this);
            } catch(IllegalPlacementException e) {
                // if this happens, we are in deep shit
                throw new RuntimeException("Could not place construction");
            }
        }
    }

    /**
     * Change the working state of this construction.
     */
    protected void setWorking(boolean w) {
        if(w != working) {
            working = w;
            signalStateChange();
        }
    }

    /**
     * Is this construction working?
     */
    public boolean isWorking() {
        return working;
    }
   
    /**
     * Percentage of how much of this building has been build.
     */
    private double constructed;

    /**
     * The list of squares this construction is build on.
     */
    private List squares;

    /**
     * Is this consruction working?
     */
    private boolean working;
}
