package modelling;

import java.util.Iterator;
import java.util.Set;
import java.util.TreeMap;
import java.awt.Image;
import java.util.Vector;
import sema.Agent;
import sema.Area;
import sema.Box;
import sema.Element;
import sema.Nature.CanSee;
import modelling.Selector;
import sema.World;

/**
 * Agent able to watch his environement.
 */
public class SeeingAgent extends Agent implements Vision {
    
    /**
     * Creates a new instance of seeing agent.
     */
    public SeeingAgent(World world, Area area, float delay, Image image, float visionRadius) {
        super(world, area, delay, image);
        this.personnalVision = world.nature.new CanSee(this);
        this.visionRadius = visionRadius;
    }
   
    public String getName(){
        return "Seeing Agent";
    }
    
    /*
     * Selector determinating wether the given element is hidden.
     */
    private Selector personnalVision;
    
    float visionRadius;
    
    /**
     * Returns the vision radius of the element.
     */
    protected float getVisionRadius(){
        return visionRadius;
    }
    
    /**
     * Determinates the area where the element is watching.
     */
    protected Area getAreaOfVision(){
        float x = (float) (getArea().getX()+Math.cos(getArea().getAngle())*getVisionRadius()/2);
        float y = (float) (getArea().getY()+Math.sin(getArea().getAngle())*getVisionRadius()/2);
        return new RectangularArea(world.map, x, y, getVisionRadius(), 2*getVisionRadius(), getArea().getAngle());
    }
    
    protected Area getLineVision(float angle){
        float x = (float) (getArea().getX()+Math.cos(angle)*getVisionRadius()/2);
        float y = (float) (getArea().getY()+Math.sin(angle)*getVisionRadius()/2);
        return new RectangularArea(world.map, x, y, getVisionRadius(), 0, angle);//AREA.KLENGTH
    }
    
   
    
    
    /**
     * Determinates whether the given element can be seen by our agent, according to
     */
    public boolean canSee(Element element){
        return personnalVision.isFiting(element) && (world.nature.getDistance(this, element) < getVisionRadius());
    }
    
    
    /**
     * Returns the visible elements, with the restrictions of the vision ability.
     */
    public Vector watch(){
        Vector seen = new Vector();
        TreeMap areaOfVision = world.nature.getElements(getAreaOfVision());
        Set cles = areaOfVision.keySet();
        Iterator iterator = cles.iterator();
        while (iterator.hasNext()) {
            Element element = (Element) areaOfVision.get(iterator.next());
            if (canSee(element))
                seen.add(element);
        }
        return seen;
        
    }
    
    /**
     * Returns the nearest visible element
     */
    public Element watchNearest(){
        float nearest = Float.MAX_VALUE;
        Element curentNearest = null;
        TreeMap areaOfVision = world.nature.getElements(getAreaOfVision());
        Set cles = areaOfVision.keySet();
        Iterator iterator = cles.iterator();
        while (iterator.hasNext()) {
            Element element = (Element) areaOfVision.get(iterator.next());
            float distance = world.nature.getDistance(element, this);
            if ((distance < nearest) && personnalVision.isFiting(element)) {
                curentNearest = element;
                nearest = (float) distance;
            }
        }
        return curentNearest;
    }
    
    
    /**
     * Returns the nearest element responding to the criterium induced by the given Selector <br>
     * in the vision field of the agent.
     */
    public Vector watchFor(Selector sel){
        Vector seen = new Vector();
        TreeMap areaOfVision = world.nature.getElements(getAreaOfVision());
        Set cles = areaOfVision.keySet();
        Iterator iterator = cles.iterator();
        while (iterator.hasNext()) {
            Element element = (Element) areaOfVision.get(iterator.next());
            if (canSee(element) && sel.isFiting(element))
                seen.add(element);
        }
        return seen;
    }
    
    
    /**
     * Returns the nearest visible element fiting the given selector.
     */
    public Element watchForNearest(Selector sel){
        float nearest = Float.MAX_VALUE;
        Element curentNearest = null;
        TreeMap areaOfVision = world.nature.getElements(getAreaOfVision());
        Set cles = areaOfVision.keySet();
        Iterator iterator = cles.iterator();
        while (iterator.hasNext()) {
            Element element = (Element) areaOfVision.get(iterator.next());
            float distance = world.nature.getDistance(element, this);
            if ((distance < nearest) && sel.isFiting(element) && personnalVision.isFiting(element)) {
                curentNearest = element;
                nearest = (float) distance;
            }
        }
        return curentNearest;
    }
    
    
    
    
    
    
    
}
