

package examples;

import java.awt.Color;
import java.util.Iterator;
import java.util.Set;
import java.util.TreeMap;
import java.util.Vector;
import modelling.Eatable;
import modelling.Message;
import modelling.Mobile;
import modelling.MovingAgent;
import modelling.RectangularArea;
import modelling.Selector;
import modelling.Vision;
import sema.Agent.Action;
import sema.Area;
import sema.Element;
import sema.World;


public class Loup extends MovingAgent2 implements Mobile, Vision {
    
    /** Creates a new instance of Loup */
    public Loup(World world, Area area, float delay, java.awt.Image image) {
        super(world,area,delay,image,7f, world.nature.getGoodKind("herbe"));
        setKind("loup");
        new CommonWolfAction(getDelay());
        setCompatibleElements();
        setDrawingPriority(10);
    }
    
    
    public Loup(World world, java.awt.Image image){
        this(world,new RectangularArea2(world.map, 1,1,2,2), 1, image);
    }
    
    
    public String etat = "Chasser";
    
    
    protected class PeutEtrePietine extends Selector {
        public PeutEtrePietine(){
            super();
        }
        
        public boolean isFiting(Element el){
            return (! el.getKind().equals("loup"));
        }
    }
    
    
    
    public void setCompatibleElements(){
        this.compatibleElements = new PeutEtrePietine();
    }
    
    
    
    
    private Selector personnalVision = world.nature.new CanSee(this);
    
    private Selector isMouton = ((Nature2)world.nature).getGoodKind("mouton","cadavre");
    
    float visionRadius = 10;
    
    /**
     * 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 = getArea().getX();
        float y = getArea().getY();
        return new RectangularArea(world.map, x, y, 2*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)) {
                curentNearest = element;
                nearest = (float) distance;
            }
        }
        return curentNearest;
    }
    
    
    
    private class isNear extends Selector {
        
        public isNear(float x, float y, float radius){
            super();
            this.x = x;
            this.y = y;
            this.radius = radius;
        }
        
        float x;
        float y;
        float radius;
        
        public boolean isFiting(Element el){
            float xd = el.getArea().getX() - x;
            float yd = el.getArea().getY() - y;
            return (Math.sqrt(xd*xd + yd*yd) < radius);
        }
    }
    
    
    private void endOfAction(String action){
    }
    
    private int comptMoveToRandomGoals;
    
    public boolean acceptAction(Action action){
        debutAction(); //on se signale la aux moutons, ca évite de réécrire Errer.
        try{
            MoveToRandomGoals errer = (MoveToRandomGoals) action;
            if (comptMoveToRandomGoals <= 0) {
                etat = "Chasser";
                new CommonWolfAction(getDelay());
                return false;
            }else{
                comptMoveToRandomGoals -=1;
                return true;
            }
        }catch(Exception e) { return true;}
        
    }
    
    
    public void debutAction(){
        
        world.nature.broadcastSignal(new Message2(world.map, "loup" , 8f , getArea().getX() , getArea().getY(), isMouton));
        //se signale aux moutons à portée de vue
        
    }
    
    
    
    
    protected class CommonWolfAction extends Action {
        
        public CommonWolfAction(double t){
            super(t);
        }
        
        
        public float step() {
            Element meat = watchForNearest(isMouton);//cherche le mouton le plus proche à 20m à la ronde
            
            if (!(meat == null)){//si y en a dans la zone
                float distance = world.nature.getDistance(meat, Loup.this);
                if (distance < 1.5) {
                    if (meat.getKind().equals("mouton"))
                        meat.kill(Loup.this);//s'il est à - de 10cm on le mange
                    new EatElement();
                    return (-1);
                }else{
                    float x = meat.getArea().getX();
                    float y = meat.getArea().getY();
                    orientate(x,y);
                    walk();
                    return (stepDelay);
                }
            }else{
                comptMoveToRandomGoals=10;//on prévoit d'errer 10 tours
                etat = "Errer";
                new MoveToRandomGoals(getDelay());
                return (-1);
            }
            
        }
        
    }
    
    
    
    boolean mangeant = false;
    
    
    protected class EatElement extends Action {
        
        public EatElement(Element goal){
            super(getDelay());
            this.goal = goal;
            mangeant = true;
        }
        
        public EatElement(){
            this(watchForNearest(world.nature.getGoodKind("cadavre")));
        }
        
        /**
         * Eated element.
         */
        protected Element goal;
        /**
         * One step of the eating. Controls if it is still possible, and eat.
         */
        protected float firstStep(){
            etat = "Manger";
            orientate(goal.getArea().getX(),goal.getArea().getY());
            walk();
            return(getDelay());
            
        }
        
        
        
        
        protected float step() {
            
            Eatable a = (Eatable) goal;
            if (a.canEat(Loup.this) && world.nature.getDistance(Loup.this, goal) < 2){
                a.eaten(Loup.this);
                
                return (float) getDelay();
            }else{
                etat = "Chasser";
                new CommonWolfAction(getDelay());
                return -1;
            }
        }
        
        protected void close(){
            mangeant = false;
        }
    }
    
    
    
    public void draw(){
        float backA = getArea().getAngle();
        getArea().setAngle(0);
        super.draw();
        getArea().setAngle(backA);
    }
    
    public void highlight(){
        highlight(new Color(0,0,1,.2f));
    }
    
    
    public String[][] getProperties() {
        String[][] ret0 = getArea().getProperties();
        String[][] ret = new String[5][2];
        String[] p1 = {"kind",getKind()};
        String[] p2 = {"height",new Float(getHeight()).toString()};
        String[] p3 = {"Drawing priority",new Float(getDrawingPriority()).toString()};
        String[] p4 = {"ID",id.toString()};
        String[] p5 = {"Activité", etat};
        ret[0] = p1; ret[1] = p2; ret[2] = p3; ret[3] = p4; ret[4] = p5;
        return(union(ret0,ret)); //propriétés de l'area et de l'élément
    }
    
    
    
    public void setProperties(String property, String value) {
        try {
            getArea().setProperties(property,value);
            if (property.equals("height")) {
                setHeight(Float.parseFloat(value));
            } else
                if (property.equals("kind")) {
                setKind(value);
                } else
                    if (property.equals("Drawing priority")) {
                float priority = Float.parseFloat(value);
                setDrawingPriority(priority);
                
                    }
        } catch (Exception e) {}
    }
    
    
}
