 /*************************************************************************
 *  Kompilacja:  javac Particle.java
 *      
 *  Poruszająca się w polu jednostkowym cząsteczka o pozycji, prędkości,
 *  promieniu i masie.
 *
 *************************************************************************/

import java.awt.Color;

public class Particle {
    private final static double INFINITY = Double.POSITIVE_INFINITY;

    private double rx, ry;    // Pozycja
    private double vx, vy;    // Prędkość
    private double radius;    // Promień
    private double mass;      // Masa
    private Color color;      // Kolor
    private int count;        // Liczba dotyczasowych zderzeń


    // Tworzenie nowej cząsteczki o danych parametrach
    public Particle(double rx, double ry, double vx, double vy, double radius, double mass, Color color) {
        this.vx = vx;
        this.vy = vy;
        this.rx = rx;
        this.ry = ry;
        this.radius = radius;
        this.mass   = mass;
        this.color  = color;
     }
         
    // Tworzenie losowej cząsteczki w polu jednostkowym (program nie sprawdza pokrywania się granic)
    public Particle() {
        rx     = Math.random();
        ry     = Math.random();
        vx     = 0.01 * (Math.random() - 0.5);
        vy     = 0.01 * (Math.random() - 0.5);
        radius = 0.01;
        mass   = 0.5;
        color  = Color.BLACK;
     }

    // Aktualizowanie pozycji
    public void move(double dt) {
        rx += vx * dt;
        ry += vy * dt;
    }

    // Rysowanie cząsteczki
    public void draw() {
        StdDraw.setPenColor(color);
        StdDraw.filledCircle(rx, ry, radius);
    }

    // Zwracanie liczby zderzeń z udziałem cząsteczek
    public int count() { return count; }
        
  
    // Ile czasu zostało do zderzenia daną cząsteczką (a) a b?
    public double timeToHit(Particle b) {
        Particle a = this;
        if (a == b) return INFINITY;
        double dx  = b.rx - a.rx;
        double dy  = b.ry - a.ry;
        double dvx = b.vx - a.vx;
        double dvy = b.vy - a.vy;
        double dvdr = dx*dvx + dy*dvy;
        if(dvdr > 0) return INFINITY;
        double dvdv = dvx*dvx + dvy*dvy;
        double drdr = dx*dx + dy*dy;
        double sigma = a.radius + b.radius;
        double d = (dvdr*dvdr) - dvdv * (drdr - sigma*sigma);
        // if (drdr < sigma*sigma) System.out.println("Cząsteczki pokrywają się");
        if (d < 0) return INFINITY;
        return -(dvdr + Math.sqrt(d)) / dvdv;
    }
	
	
    // Ile czasu zostało do zderzenia danej cząsteczki z pionową ścianą?
    public double timeToHitVerticalWall() {
        if      (vx > 0) return (1.0 - rx - radius) / vx;
        else if (vx < 0) return (radius - rx) / vx;  
        else             return INFINITY;
    }
      
    // Ile czasu zostało do zderzenia danej cząsteczki z poziomą ścianą?
    public double timeToHitHorizontalWall() {
        if      (vy > 0) return (1.0 - ry - radius) / vy;
        else if (vy < 0) return (radius - ry) / vy;
        else             return INFINITY;
    }
	
        
    // Aktualizowanie prędkości po zderzeniu cząsteczek this i that
    public void bounceOff(Particle that) {
        double dx  = that.rx - this.rx;
        double dy  = that.ry - this.ry;
        double dvx = that.vx - this.vx;
        double dvy = that.vy - this.vy;
        double dvdr = dx*dvx + dy*dvy;             
        double dist = this.radius + that.radius;   // Odległość między środkami cząsteczek w momencie zderzenia

        // Siła F oraz siły w kierunkach x i y
        double F = 2 * this.mass * that.mass * dvdr / ((this.mass + that.mass) * dist);
        double Fx = F * dx / dist;
        double Fy = F * dy / dist;

        // Aktualizowanie szybkości według normalnej siły
        this.vx += Fx / this.mass;
        this.vy += Fy / this.mass;
        that.vx -= Fx / that.mass;
        that.vy -= Fy / that.mass;

        // Aktualizowanie liczby zderzeń
        this.count++;
        that.count++;
    }
	
    // Aktualizowanie szybkości cząsteczki po zderzeniu z pionową ścianą
    public void bounceOffVerticalWall() {
        vx = -vx;
        count++;
    }

    // Aktualizowanie szybkości cząsteczki po zderzeniu z poziomą ścianą
    public void bounceOffHorizontalWall() {
        vy = -vy;
        count++;
    }


    // Zwracanie energii kinetycznej powiązanej z daną cząsteczką
    public double kineticEnergy() { return 0.5 * mass * (vx*vx + vy*vy); }

}
