package modelling;

import java.util.Vector;
import java.awt.Color;
import java.util.Iterator;
import java.util.TreeMap;

import sema.Element;
import sema.Map;
import sema.Drawing;
import sema.Area;
import sema.Box;

/**
 * Implements a ponctual area.
 * The boxes coverage is the box containing the reference point of the area.
 */
public class PonctualArea implements Area {
    
    /**
     * The rectangle where to draw image of the element is centered on reference point (x,y)
     * and has dimensions xDrawLength and yDrawLength
     */
    public PonctualArea(Map map,float x, float y, float angle, float xDrawLength, float yDrawLength) {
        this.lastX = x;
        this.lastY = y;
        this.lastAngle = angle;
        this.lastXdraw = xDrawLength;
        this.lastYdraw = yDrawLength;
        this.x = x;
        this.y = y;
        this.map = map;
        refreshBoxes();
        this.lastV = getIntersectedBoxes();
        this.xDrawLength = xDrawLength;
        this.yDrawLength = yDrawLength;
        this.angle = angle;
    }
    
    private float xDrawLength;
    private float yDrawLength;
    
    private Map map;
    private Element element;
    
    //cases occupées par le rectangle
    private Vector intersectedBoxes = new Vector();
    
    public Vector getIntersectedBoxes() {
        return intersectedBoxes;
    }
    
    private void registerBoxes() { //enregistre l'élément auprès des cases occupées par la zone
        if (element!=null)
            if (intersectedBoxes.size()>0) ((Box)intersectedBoxes.get(0)).register(element);
    }
    
    private void unregisterBoxes() { //désenregistre l'élément auprès des cases occupées par la zone
        if (element!=null)
            if (intersectedBoxes.size()>0) ((Box)intersectedBoxes.get(0)).unregister(element);
    }
    
    
    private Vector computeBoxes(){
        Vector covering = new Vector();
        try {
            covering.add(map.boxes[(int)Math.floor(x/map.boxLength)][(int)Math.floor(y/map.boxLength)]);
        } catch (Exception e) {}
        return covering;
    }
    
    private void refreshBoxes(){
        unregisterBoxes();
        intersectedBoxes = computeBoxes();
        registerBoxes();
    }
    
    public void setElement(Element el) {
        this.element = el;
        refreshBoxes();
    }
    
    private float x;
    private float y;
    
    private float angle;
    
    public float getX() {
        return(x);
    }
    
    public float getY() {
        return(y);
    }
    
    public float getAngle() {
        return(angle);
    }
    
    
    public boolean isIn(float px, float py){
        return(x==px & y==py);
    }
    
    /**
     * Rounded here, because of the approximate covering boxes (there would be undue collisions!).
     * This is because this area is designed to be fast.
     * The elementsCollision should be used for this kind of area.
     * However, the box-base collision is always an approximation!
     */
    public boolean isCrashingElements() {
        return isCrashingElements(getIntersectedBoxes());
    }
    
    public boolean isCrashingElements(Vector v) {
        if (v==null) return true;
        for (int i = 0; i<v.size(); i++){
            Box b = (Box) v.get(i);
            if (!b.isFreeFor(element)) return true;
            int s =b.getContents().size();
            if (s>1 || (s==1 & !(b.getContents().contains(element)))) {
                return(true);
            }
        }
        return(false);
    }
    
    
    
    public Vector getPolygon(){ //on retourne 8 sommets équirépartis
        Vector v = new Vector();
        float[] p8 = new float[2];
        p8[0]=x; p8[1]=y;
        v.add(p8);
        return v;
    }
    
    public boolean isPolygon(){return true;};
    
    
    private float vectorial(float px, float py, float ax, float ay, float bx, float by) { //composante suivant z du produit vectoriel PA^PB)
        return((ax-px)*(by-py)-(ay-py)*(bx-px));
    }
    
    public boolean intersect(float ax, float ay, float bx, float by){
        return false; //de toutes façons on se s'en sert pas de cette fonction dans intersect
    }
    
    
    public boolean intersect(Vector p1){
        return false;
    }
    
    public boolean intersect(Area a){
        return a.isIn(x,y);
    }
    
    
    
    public boolean exactIsCrashingElements() { //peut servir de fonction de gestion des collisions entre éléments
        return exactIsCrashingElements(getIntersectedBoxes());
    }
    
    public boolean exactIsCrashingElements(Vector v) { //peut servir de fonction de gestion des collisions entre éléments
        if (v==null) return true;
        if (v.size()>0) {
            Box b = (Box) v.get(0);
            if (!b.isFreeFor(element)) return true;
            Iterator it = b.getContents().iterator();
            while (it.hasNext()) {
                Element el = (Element)it.next();
                if (el!=element)
                    if ((el.getArea()).isIn(x,y))
                        return true;
            }
        }
        return(false);
    }
    
    
    public boolean translate(float x, float y){
        float backX = this.x;
        float backY = this.y;
        this.x = this.x + x;
        this.y = this.y + y;
        if (inMap()) {
            refreshBoxes();
            return true;} else {
            this.x = backX;
            this.y = backY;
            return false;}
        
    }
    
    public boolean setX(float x){
        float backX = this.x;
        this.x = x;
        if (inMap()) {
            refreshBoxes();
            return true;} else {
            this.x = backX;
            return false;}
        
    }
    
    public boolean setY(float y){
        float backY = this.y;
        this.y = y;
        if (inMap()) {
            refreshBoxes();
            return true;} else {
            this.y = backY;
            return false;}
    }
    
    private float lastX;
    private float lastY;
    private float lastAngle;
    private float lastXlength;
    private float lastYlength;
    private float lastXdraw;
    private float lastYdraw;
    private Vector lastV;
    
    public Vector simuleTranslate(float x, float y){
        float backX = this.x;
        float backY = this.y;
        this.x = this.x + x;
        this.y = this.y + y;
        if (!inMap()){
            this.x = backX;
            this.y = backY;
            this.lastX = this.x;
            this.lastY = this.y;
            this.lastAngle = angle;
            this.lastXdraw= xDrawLength;
            this.lastYdraw= yDrawLength;
            this.lastV = null;
            return null;
        }
        Vector v = computeBoxes();
        this.lastX = this.x;
        this.lastY = this.y;
        this.lastAngle = angle;
        this.lastXdraw= xDrawLength;
        this.lastYdraw= yDrawLength;
        this.lastV = v;
        this.x = backX;
        this.y = backY;
        return v;
    }
    
    public void confirmSimuled(){
        this.x = lastX;
        this.y = lastY;
        this.angle = lastAngle;
        this.xDrawLength = lastXdraw;
        this.yDrawLength = lastYdraw;
        unregisterBoxes();
        intersectedBoxes = lastV;
        registerBoxes();
    }
    
    
    private float toZero2PI(float f){ //retourne la valeur module 2Pi de f dans [0;2*Pi[.
        double t = Math.IEEEremainder(f, 2.*Math.PI);
        if (t<0) return (float)(t+2*Math.PI); else return (float)t;
    }
    
    public boolean rotate(float angle){ //angle de rotation dans [-2Pi;2Pi]
        this.angle = toZero2PI(this.angle + angle);
        return true;
    }
    
    
    public boolean setAngle(float a){
        this.angle = toZero2PI(a);
        return true;
    }
    
    
    public Vector simuleRotate(float angle){
        this.lastX = x;
        this.lastY = y;
        this.lastAngle = angle+this.angle;
        this.lastXdraw= xDrawLength;
        this.lastYdraw= yDrawLength;
        this.lastV = intersectedBoxes;
        return intersectedBoxes;
    }
    
    public boolean rescale(float x, float y){
        return true;
    }
    
    public Vector simuleRescale(float xS, float yS){
        this.lastX = x;
        this.lastY = y;
        this.lastAngle = angle;
        this.lastXdraw= xDrawLength;
        this.lastYdraw= yDrawLength;
        this.lastV = intersectedBoxes;
        return intersectedBoxes;
    }
    
    public boolean inMap() {
        return (x>=0 && x<=map.xLength & y>=0 && y<map.yLength);
    }
    
    public void draw(Drawing g) { //fonction de dessin de la zone, qui dessine également l'image de l'élément
        if (element!=null)
            g.drawImage(element.getImage(),x-xDrawLength/2,y-yDrawLength/2,xDrawLength,yDrawLength,angle,x,y);
    }
    
    //does nothing
    public void fill(Drawing g, Color c) {
    }
    
    //does nothing
    public void drawBorder(Drawing g, Color c) {
    }
    
    
    public void highlight(Drawing g, Color c) {
        g.setColor(c);
        g.fillRect(x-xDrawLength/2,y-yDrawLength/2,xDrawLength,yDrawLength,angle,x,y);
        g.setColor(Color.BLACK);
        g.drawRect(x-xDrawLength/2,y-yDrawLength/2,xDrawLength,yDrawLength,angle,x,y);
    }
    
    public String[][] getProperties() {
        String[][] ret = new String[4][2];
        String[] p1 = {"x",new Float(x).toString()};
        String[] p2 = {"y",new Float(y).toString()};
        String[] p5 = {"angle (in degrees)",new Float(180*angle/Math.PI).toString()};
        String[] p6 = {"intersected boxes",new Integer(intersectedBoxes.size()).toString()};
        ret[0] = p1; ret[1] = p2; ret[2] = p5; ret[3] = p6;
        return(ret);
    }
    
    public void setProperties(String s1, String s2) {
        try {
            if (s1.equals("x")) {
                setX(Float.parseFloat(s2));
            } else
                if (s1.equals("y")) {
                setY(Float.parseFloat(s2));
                } else
                    if (s1.equals("angle (in degrees)")) {
                setAngle(Float.parseFloat(s2)/180f*(float)Math.PI);
                    };
        } catch (Exception e) {}
    }
    
    public Object clone(){
        return new PonctualArea(map,x,y,xDrawLength, yDrawLength,angle);
    }
    
}
