package molekuly;

import java.util.LinkedHashSet;
import java.util.Random;
import java.util.Set;

import rup.česky.společně.P;

import rup.česky.tvary.Kruh;


/*******************************************************************************
 * Třída Molekula_13b definuje sadu metod, které vygenerují na plátno
 * skupiny náhodně rozmístěných molekul, jež pak rozpohybují simulujíce
 * Brownův pohyb molekul. 
 *
 * Změny oproti třídě Molekula_13a:
 * - odstraněny metody další_1(), nováSada_1(int) a nová sada_2(int), 
 *   které byly pouze postupnými kroky při příprave definitivní verze
 * - metody další_2(int) a nováSada_3(int,int) přejmenovány 
 *   na další(int) a nováSada(int,int)
 * - atributy FREKVENCE, molekuly, x, y 
 *   mají změněný přístup z private na implicitní (package private).
 * - přidána metoda popojeď()
 * - přidána metoda test() testující funkčnost simulace
 * - přidána neveřejná třída AnimátorMolekul_13b (rozšiřující TimerTask)
 *   s bezperemetrickým konsturktorem a metodou run(), která je zodpovědná
 *   za animaci molekul na plátně
 *
 * @author    Rudolf Pecinovský
 * @version   0.00.000,  0.0.2003
 */
public class Molekula_13b extends Kruh
{
//== KONSTANTNÍ ATRIBUTY TŘÍDY =================================================

    /** Frekvence překreslování plánta s molekulami. */
    static final int FREKVENCE = 50;

    /** MAximální počet pokusů věnovaných na umístění další moelkuly. */
    private static final int POKUSŮ = 3;

    /** Společný průměr všech moelkul. */
    private static final int PRŮMĚR = 50;

    /** Maximální povolená rychlost molekuly, 
     * tj. kolik bodů může molekula urazit za sekundu. */
    private static int maxR = 300 ;

    /** Množina všech dosud vygenerovaných molekul. 
     *  Atribut není private aby k němu mohl přistupovat animátor. */
    static final Set<Molekula_13b> molekuly = 
                           new LinkedHashSet<Molekula_13b>();

    /** Společný generátor náhodných čísel. */
    private static final Random rnd = new Random();





//== PROMĚNNÉ ATRIBUTY TŘÍDY ===================================================

    /** Počet dosud vytvořených instancí. */
    private static int počet = 0;

    /** Maximální povolené souřadnice molekuly. */
    private static int maxX;
    private static int maxY;

    static { nastavMaxima(); }



//== KONSTANTNÍ ATRIBUTY INSTANCÍ ==============================================

    /** Pořadí vytvoření dané instance v rámci třídy. */
    private final int pořadí = ++počet;


//== PROMĚNNÉ ATRIBUTY INSTANCÍ ================================================

    /** Složky aktuální pozice molekuly. */
    private double x, y;

    /** Složky aktuální rychlosti molekuly. */
    private double rx, ry;



//== PŘÍSTUPOVÉ METODY VLASTNOSTÍ TŘÍDY ========================================
//== OSTATNÍ METODY TŘÍDY ======================================================
    
    /***************************************************************************
     * Nastaví maximální povolené souřadnice molekul 
     * podle aktuální velikosti aktivního plátna.
     */
    public static void nastavMaxima()
    {
        maxX = AP.getBšířka() - PRŮMĚR;
        maxY = AP.getBVýška() - PRŮMĚR;
    }


    /***************************************************************************
     * Vytvoří sadu náhodně umístěných molekul. Generaci ukončí poté,
     * so se po zadaném počtu pokusů nepodaří najít umístění pro další molekulu.
     *
     * @param molekul   Požadovaný počet vytvořených molekul
     */
    public static int nováSada( int molekul, int pokusů )
    {
        int počet = 0;
        molekuly.clear();
        do{
            if( počet >= molekul )
                return počet;
            System.out.println("Generace " + ++počet + ". molekuly:");
        }while( další( pokusů ) );    //Dokud se daří molekuly generovat
        System.out.println("Vygenerovali jsme " + (počet-1) + " molekul");
        return počet - 1;
    }



    /***************************************************************************
     * Najde náhodné umístění další molekuly tak, aby se nepřekrývala s žádnou
     * z dosud vytvořených molekul, a nechá molekulu na této pozici vytvořit.
     * Nepodaří-li se to do zadaného maximálního počtu pokusů, 
     * vzdá další snažení a vrátí false. Jinak vrátí true.
     *
     * @param pokusů    Maximálně povolený počet pokusů při hledání
     *                  pozice vytvářené molekuly
     *
     * @return Příznak úspěšnosti: true = podařilo se vytvořit další molekulu
     */
    public static boolean další( int pokusů )
    {
        int x, y;         //Navrhované souřadnice nové molekuly
        int K_PROVĚŘENÍ = molekuly.size();  //Počet dosud vytvořených
        int prověřeno;
        int pokus = 0;
        do{
            if( pokus >= pokusů )
                return false;   //Na daný počet pokusů se generace nezdařila
            
            //Generace návrhu nové pozice
            x = rnd.nextInt( maxX );
            y = rnd.nextInt( maxY );
            System.out.print( "  " + ++pokus + ". pokus: x=" + x +
                                                      ", y=" + y );
            //Otestování případných kolizí
            prověřeno = 0;
            //Projdeme množinu a otestujeme molekulu za molekulou
            for( Molekula_13b m : molekuly )
            {
                if( PRŮMĚR > P.délka( x, y, m.x,  m.y ) )
                {
                    System.out.println(" koliduje s " + m.pořadí + 
                                       ". molekulou" );
                    break;
                }
                prověřeno++;    //Další byla prověřena
            }
        //Nebyly-li prověřeny všechny, je třeba zkusit znovu
        }while( prověřeno < K_PROVĚŘENÍ );
        System.out.println( " - ÚSPĚŠNÝ" );

        Molekula_13b m = new Molekula_13b( x, y );
        return true;
    }

    

//##############################################################################
//== KONSTRUKTORY A TOVÁRNÍ METODY =============================================

    /***************************************************************************
     * Na pozici o zadaných souřadnicích vytvoří molekulu s náhodnou rychlostí 
     * a nechá ji zobrazit na aktivním plátně.
     */
    public Molekula_13b(int x, int y)
    {
        super( x, y, PRŮMĚR );
        this.x = x;
        this.y = y;
        this.rx = (rnd.nextDouble() - 0.5) * 2 * maxR / FREKVENCE;
        this.ry = (rnd.nextDouble() - 0.5) * 2 * maxR / FREKVENCE;
        molekuly.add( this );      //Přidej molekulu do množiny vygenerovaných
        AP.přidej( this );         //a nech ji zobrazit na aktivní plátno
    }



//== PŘÍSTUPOVÉ METODY VLASTNOSTÍ INSTANCÍ =====================================
//== PŘEKRYTÉ METODY IMPLEMENTOVANÝCH ROZHRANÍ =================================
//== PŘEKRYTÉ ABSTRAKTNÍ METODY RODIČOVSKÉ TŘÍDY ===============================
//== PŘEKRYTÉ KONKRÉTNÍ METODY RODIČOVSKÉ TŘÍDY ================================
//== NOVĚ ZAVEDENÉ METODY INSTANCÍ =============================================

    private final String ps = ((pořadí < 10) ? " " : "" ) + počet;

    /***************************************************************************
     * Posune molekulu do nové pozice definované její současnou pozicí
     * a snímkovou rychlostí. V nové pozici zkontroluje, jestli molekula
     * nenarazila do okraje plátna či některé jiné molekuly. Pokud ano,
     * tak posun zruší a upraví rychlost zúčastněných tak, aby similovaly
     * dokonalý odraz.     
     */
    public void popojeď()
    {
        double xn = x + rx;     //Plánovaná nová vodorovná souřadnice
        double yn = y + ry;     //Plánovaná nová svislá souřadnice
        boolean odraz = false;  //Předpokládáme, že se neodrazí

        //Test odrazu od zdi
        if( (xn <= 0) || (maxX <= xn) )   {  rx =-rx;  odraz = true; }
        if( (yn <= 0) || (maxY <= yn) )   {  ry =-ry;  odraz = true; }
        if( x<=0  || y<=0  )
            System.out.println( "Zeď: " +
                "xPos=" + xPos + ", x=" + x + ", rx=" + rx + ", xn=" + x +
         "\n     yPos=" + yPos + ", y=" + y + ", ry=" + ry + ", yn=" + y );

        //Testy možných odrazů od ostatních molekul
        for( Molekula_13b m : molekuly )
        {
            if( (m != this)  &&  //Sama od sebe se neodrazí
                (P.délka( xn, yn, m.x,  m.y )  <  PRŮMĚR) )
            {
                double p;   //Molekuly si prohodí rychlosti
                p = rx;   rx = m.rx;   m.rx = p;
                p = ry;   ry = m.ry;   m.ry = p;
                if( x<0  || y<0  )
                {
            System.out.println( "Mol." + ps + ": " +
                "xPos=" + xPos + ", x=" + x + ", rx=" + rx + ", xn=" + xn +
      "\n        yPos=" + yPos + ", y=" + y + ", ry=" + ry + ", yn=" + yn +
      "\n    " + m.ps + ": " +
                "xPos=" + m.xPos + ", rx=" + m.rx + ", x=" + m.x +
      "\n        yPos=" + m.yPos + ", ry=" + m.ry + ", y=" + m.y +
                 "");
                }
                odraz = true;
            }
        }
        //Pokud se molekula odrazila, její pozice se nezmění
        //(spokojíme se pouze s nastavenou změnou rchlosti)
        if( !odraz )
            setPozice( (int)(x = xn), (int)(y = yn) );
    }
        


//== SOUKROMÉ A POMOCNÉ METODY TŘÍDY ===========================================
//== SOUKROMÉ A POMOCNÉ METODY INSTANCÍ ========================================
//== VNOŘENÉ A VNITŘNÍ TŘÍDY ===================================================
//== TESTY A METODA MAIN =======================================================
    
    /***************************************************************************
     * Testovací program - vygeneruje novou sadu molekul a spustí animaci
     * jejich pohybu.     
     */         
    public static void test()
    {
        nováSada( 20, 20 );    //Zadávám počet molekul
        new AnimátorMolekul_13b();
    }


}

