// Fichier: Tore.java
// Par: J. Laforgue
//
// Ce fichier doit appartenir au package Tore.
//
// Il contient des methodes statiques permettant de realiser des
// operations vectorielles dans un espace de Tore
//
package fr.cnam.tore;

import java.awt.Point;
import java.util.*;

/**
  Classe d'implémentation d'un espace rectangulaire orthonormé de Tore
*/
public class Tore
{
    private int nbX; // Taille en colonne (largeur) de l'espace
    private int nbY; // Taille en ligne (hauteur) de l'espace
    
    /** Création d'un espace de Tore défini par sa longueur et sa hauteur
        @param width longueur
        @param height largeur
    */
    public Tore(int width, int height)
    {
        this.nbX = width;
        this.nbY = height;
    }

    /**
        Cette methode retourne l'image de B la plus proche de A dans un espace de tore
        @param A : coordonnees du point A
        @param B : coordonnees du point B
        @return coordonnees de l'image de B
    */
    public Point image(Point A,
                       Point B)
    {
        int xA;
        int yA;
        int xB;
        int yB;
        int x;
        int y;
        double min;
        double distance;
        
        xA = (int)(A.x);
        yA = (int)(A.y);
        xB = (int)(B.x);
        yB = (int)(B.y);
        
        x=0;
        y=0;
        
        int TX[] = new int[3];
        int TY[] = new int[3];
        TX[0] = -nbX+xB;
        TX[1] = xB;
        TX[2] = nbX+xB;
        TY[0] = -nbY+yB;
        TY[1] = yB;
        TY[2] = nbY+yB;
        
        min=9*(nbX*nbX+nbY*nbY);
        for(int i=0;i<3;i++)
            {
                for(int j=0;j<3;j++)
                    {
                        xB = TX[i] - xA;
                        yB = TY[j] - yA;
                        distance = Math.sqrt(xB*xB+yB*yB);
                        if (distance < min)
                            {
                                min = distance;
                                x = TX[i];
                                y = TY[j];
                            }
                    }
            }
        return ( new Point(x,y) );
    }
    
    // retourne la distance entre 2 points
    //
    static public double distance(Point p1, Point p2)
    {
        return ( Math.sqrt( (p2.x - p1.x)*(p2.x - p1.x) +
                            (p2.y - p1.y)*(p2.y - p1.y) ));
    }


    /**
       Retourne le sens du vecteur de A vers B dans l'espace de Tore.
       @param A le point A
       @param B le point B
       @return la direction ( (-1,O,+1)(ex: (-1,-1) (1,0), ...)
     */
    public Point sensTore(Point A,
                          Point B)
    {
        return sens(A,image(A,B));
    }

    /**
     Cette methode consiste à déterminer le point le plus proche de A dans un espace de Tore parmi un ensemble de points ET dont la distance est superieur à une distance seuil.
     @param A coordonnees du point A
     @param points Ensemble des points
     @parma distanceSeuil disnance seuil
     @return le point le plus proche<br>
     si il n'existe pas de tel point alors retourne null
     <br>
     Remarque: A peut appartenir à l'ensemble des points.
    */
    public Point lePlusProche(Point A,
                              Vector points,
                              int distanceSeuil)
    {
        double min;
        double dis;
        Point image;
        Point p = null;
        Point pcour;
        
        if (points == null) return(p);
        if (points.size() == 0) return(p);
        
        min = 999999;
        
        for(int i=0;i<points.size();i++)
            {
                pcour = (Point)(points.get(i));
                if (! pcour.equals(A))
                    {
                        image = image(A,pcour);
                        dis = distance(A,image);
                        if ( (dis<min) && (dis>(double)distanceSeuil) )
                            {
                                min=dis;
                                p = image;
                            }
                    }
            }
        return(p);
    }

    /**
     Cette methode consiste à déterminer le point le plus proche de A dans un espace de Tore parmi un ensemble de points ET dont la distance est inferieur à une distance seuil et si le nombre d'agent trouve est inferieur a un certain nombre d'agent
     @param A coordonnees du point A
     @param points Ensemble des points
     @parma distanceSeuil disnance seuil
     @param nbAgent nombre d'agent seuil
     @return ArrayList [le point le plus proche, le nombre d'agent trouve] <br>
     si il n'existe pas de tel point alors retourne null
     <br>
     Remarque: A peut appartenir à l'ensemble des points.
    */
    public Point lePlusProcheInferieur(Point A,
                                       Vector points,
                                       int distanceSeuil,
                                       int nbAgent)
    {
        double min;
        double dis;
        Point image;
        Point p = null;
        Point pcour;
        
        if (points == null) return(null);
        if (points.size() == 0) return(null);
        
        min = 999999;
        int nb=0;
        for(int i=0;i<points.size();i++)
            {
                pcour = (Point)(points.get(i));
                if (! pcour.equals(A))
                    {
                        image = image(A,pcour);
                        dis = distance(A,image);
                        if ( (dis<min) && (dis<(double)distanceSeuil) )
                            {
                                nb++;
                                min=dis;
                                p = image;
                            }
                    }
            }
        if (nb >= nbAgent) return null;
        return(p);
    }
    
    
    /**
     Cette methode consiste à déterminer le point le plus loin de A dans un espace de Tore parmi un ensemble de points ET dont la distance est superieur à une distance seuil.
     @param A coordonnees du point A
     @param points Ensemble des points
     @parma distanceSeuil disnance seuil
     @return le point le plus proche<br>
     si il n'existe pas de tel point alors retourne null
     <br>
     Remarque: A peut appartenir à l'ensemble des points.
    */
    public Point lePlusLoin  (Point A,
                              Vector points,
                              int distanceSeuil)
    {
        double max;
        double dis;
        Point image;
        Point p = null;
        Point pcour;
        
        if (points == null) return(p);
        if (points.size() == 0) return(p);
        
        max = 0;
        
        for(int i=0;i<points.size();i++)
            {
                pcour = (Point)(points.get(i));
                if (! pcour.equals(A))
                    {
                        image = image(A,pcour);
                        dis = distance(A,image);
                        if ( (dis>max) && (dis>(double)distanceSeuil) )
                            {
                                max=dis;
                                p = image;
                            }
                    }
            }
        return(p);
    }
    
    /**
     Cette methode retourne le sens de deplacement à effectuer sur A pour que A se rapproche de B. B etant exprime en coordonnes dans un espace de Tore.
     @param A le point A
     @param B le point B
     @return La valeur de retour est de la forme (+1,+1) , (+1,-1), (+1,0), ....
    */
    public Point sens(Point A,Point B)
    {
        int sensX;
        int sensY;
        
        // Vecteur AB
        int x = (int)B.x - (int)A.x;
        int y = (int)B.y - (int)A.y;
        
        // Norme du vecteur
        double norme = Math.sqrt(x*x+y*y);
        
        // Vecteur unitaire vers cet agent le plus proche
        double dx = (double)x/norme;
        double dy = (double)y/norme;
        
        // On definie le sens en X et Y afin de se diriger vers B
        sensX=0;
        if (dx>0.5){sensX=1;}
        if (dx<-0.5){sensX=-1;}
        sensY=0;
        if (dy>0.5){sensY=1;}
        if (dy<-0.5){sensY=-1;}
        
        return( new Point(sensX,sensY));
    }
    
    /**
       Calcule les nouvelles coordonnées d'un point en fonction de son déplacment d'une case dans une des 8 directions.
       @param x coordonnée en x du point
       @parma y coordonnée en y du point
       @param sensX direction en X (-1, 0, +1)
       @param sensY direction en Y (-1, 0, +1)
       @return les nouvelles coordonnées du point
    */
    public Point deplacer(int x,int y,int sensX,int sensY)
    {
        int xn = x+sensX;
        int yn = y+sensY;
        
        if (xn<0) { xn=nbX-1; }
        if (xn>=nbX) { xn=0; }
        if (yn<0) { yn=nbY-1; }
        if (yn>=nbY) { yn=0; }
        
        return (new Point(xn,yn));
    }

    public static void main(String... args)
    {
        Tore t = new Tore(10,10);
        int x = 9;
        int y = 9;

        Point p = t.deplacer(x,y,1,1);
        System.out.println("x,y   = "+x +" "+ y);
        System.out.println("px,py = "+p.x +" "+ p.y);

        x=0;
        y=0;
        p = t.deplacer(x,y,-1,-1);
        System.out.println("x,y   = "+x +" "+ y);
        System.out.println("px,py = "+p.x +" "+ p.y);

    }
}