package příklady;

import java.util.Collections;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Timer;
import java.util.TimerTask;

import rup.česky.tvary.AktivníPlátno;
import rup.česky.tvary.IKreslený;
import rup.česky.tvary.Kreslítko;


/*******************************************************************************
 * Třída Sbl_Třída slouží k ...
 *
 * @author    Rudolf Pecinovský
 * @version   0.00.000,  0.0.2003
 */
public final class Animátor extends TimerTask implements IKreslený
{
//== KONSTANTNÍ ATRIBUTY TŘÍDY =================================================
    
    public static final int FREKVENCE = 30;
    
    public static final int PERIODA = 1000 / FREKVENCE;
    
    private static final Animátor jedináček = new Animátor();
    
    private static final AktivníPlátno AP = AktivníPlátno.getPlátno();
    
    
//== PROMĚNNÉ ATRIBUTY TŘÍDY ===================================================
//== KONSTANTNÍ ATRIBUTY INSTANCÍ ==============================================
    
    private final List<IAnimovaný> animace = new LinkedList<IAnimovaný>();
    
    private final Timer timer = new Timer( "Animátor" );

    
//== PROMĚNNÉ ATRIBUTY INSTANCÍ ================================================
    
    private boolean běží = false;
    private long  minule;
    private long  uplynulo;
    
//== PŘÍSTUPOVÉ METODY VLASTNOSTÍ TŘÍDY ========================================
//== OSTATNÍ METODY TŘÍDY ======================================================

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

    /***************************************************************************
     * 
     */
    private Animátor()
    {
    }

    
    /***************************************************************************
     * 
     */
    public static Animátor getInstance()
    {
        return jedináček;
    }
    

//== PŘÍSTUPOVÉ METODY VLASTNOSTÍ INSTANCÍ =====================================
    
    public synchronized long getUplynulo()
    {
        return uplynulo;
    }
    
    
    public synchronized long getMinule()
    {
        return minule;
    }
    
    
    public synchronized long getČas()
    {
        return minule + uplynulo;
    }
    
    
    public synchronized boolean isBěží()
    {
        return běží;
    }
    
    
//== PŘEKRYTÉ METODY IMPLEMENTOVANÝCH ROZHRANÍ =================================
    
    public synchronized void nakresli( Kreslítko kreslítko )
    {
        for( IAnimovaný ia : animace )
        {
            ia.nakresli( kreslítko );
        }
    }
    
    
    
//== PŘEKRYTÉ ABSTRAKTNÍ METODY RODIČOVSKÉ TŘÍDY ===============================
    
    public synchronized void run()
    {
        long čas = System.currentTimeMillis();
        uplynulo = čas - minule;
        minule = čas;
        AP.nekresli();
        for( Iterator<IAnimovaný> it = animace.iterator();   it.hasNext();   )
        {
            IAnimovaný ia = it.next();
            if( ia.popojeď() )
                it.remove();
        }
        AP.vraťKresli();
    }
    
    
    
//== PŘEKRYTÉ KONKRÉTNÍ METODY RODIČOVSKÉ TŘÍDY ================================
//== NOVĚ ZAVEDENÉ METODY INSTANCÍ =============================================
    
    /***************************************************************************
     * Ukončí multipřesun a tím uvolní procesor.    
     */     
    public synchronized void stop()
    {
        if( ! běží )
            return;
        this.cancel();
        běží = false;
    }
    
    
    /***************************************************************************
     * Spustí nový multipřesun. 
     */     
    public synchronized void start()
    {
        if( ! běží )
        {
            běží = true;
            minule = System.currentTimeMillis();
            timer.schedule( this, PERIODA, PERIODA );
            //timer.scheduleAtFixedRate( this, PERIODA, PERIODA );
        }
    }
    
 
    /***************************************************************************
     * Není-li zadaný obrazec v seznamu animovaných, přidá jej na konec
     * (bude se animovat jako poslední.
     *
     * @param  nová  Přidávaný animovaný objekt
     *
     * @return  true  v případě, když byl objekt opravdu přidán,
     *          false v případě, když již mezi animovanými byl
     *                a pouze se přesunul
     */
    public synchronized boolean přidej( IAnimovaný nová )
    {
        if( animace.contains( nová ) )
        {
            return false;
        }
        else
        {
            animace.add( nová );
            return true;
        }
    }


    /***************************************************************************
     * Přidá objekt do seznamu animovaných tak, aby byl animován
     * před zadaným objektem.
     * Pokud již v seznamu byl, jenom jej přesune do zadané pozice.
     *
     * @param  stará  Objekt, který se má animovat před přidávaným objektem
     * @param  nová   Přidávaný objekt
     *
     * @return  true  v případě, když byl obrazec opravdu přidán,
     *          false v případě, když již mezi zobrazovanými byl
     *                a pouze se přesunul do jiné urovné
     */
    public synchronized boolean přidejPřed( IAnimovaný stará, IAnimovaný nová )
    {
        boolean nebyla = ! animace.remove( nová );
        int kam = animace.indexOf( stará );
        if( kam < 0 )
        {
            throw new IllegalArgumentException(
                "Referenční objekt není v seznamu animovaných!" );
        }
        animace.add( kam+1, nová );
        return nebyla;
    }


    /***************************************************************************
     * Přidá objekt do seznamu animovaných tak, aby byl animován
     * po zadaném objektu.
     * Pokud již v seznamu byl, jenom jej přesune do zadané pozice.
     *
     * @param  stará  Objekt, který se má animovat po přidávaném objektu
     * @param  nová   Přidávaný objekt
     *
     * @return  true  v případě, když byl obrazec opravdu přidán,
     *          false v případě, když již mezi zobrazovanými byl
     *                a pouze se přesunul do jiné urovné
     */
    public synchronized boolean přidejZa( IAnimovaný stará, IAnimovaný nová )
    {
        boolean nebyla = ! animace.remove( nová );
        int kam = animace.indexOf( stará );
        if( kam < 0 )
        {
            throw new IllegalArgumentException(
                "Referenční objekt není v seznamu animovaných!" );
        }
        animace.add( kam, nová );
        return nebyla;
    }


    /***************************************************************************
     * Přidá objekt do seznamu animovaných tak, aby byl animován
     * jako poslední.
     * Pokud již v seznamu byl, jenom jej přesune do zadané pozice.
     *
     * @param  nová   Přidávaný objekt
     *
     * @return  true  v případě, když byl obrazec opravdu přidán,
     *          false v případě, když již mezi zobrazovanými byl
     *                a pouze se přesunul do jiné urovné
     */
    public synchronized boolean přidejNaKonec( IAnimovaný nová )
    {
        boolean nebyla = ! animace.remove( nová );
        animace.add( nová );
        return nebyla;
    }


    /***************************************************************************
     * Přidá obrazec do seznamu malovaných tak, aby byl kreslen
     * pod zadaným obrazcem.
     * Pokud již v seznamu byl, jenom jej přesune do zadané pozice.
     *
     * @param  přidaný   Přidávaný obrazec
     *
     * @return  true  v případě, když byl obrazec opravdu přidán,
     *          false v případě, když již mezi zobrazovanými byl
     *                a pouze se přesunul do jiné urovné
     */
    public synchronized boolean přidejNaZačátek( IAnimovaný nová )
    {
        boolean nebyla = ! animace.remove( nová );
        animace.add( 0, nová );
        return nebyla;
    }


    /***************************************************************************
     * Odebere objekt ze seznamu animovaných.
     *
     * @param  ia   Odebíraný obrazec
     *
     * @return  true  v případě, když byl obrazec opravdu odebrán,
     *          false v případě, když již mezi zobrazovanými nebyl
     */
    public synchronized boolean odeber( IAnimovaný ia )
    {
        boolean byla = animace.remove( ia );
        return byla;
    }


    /***************************************************************************
     * Vrátí pořadí zadaného prvku v seznamu kreslených prvků.
     * Prvky se přitom kreslí v rostoucím pořadí, takže obrazec
     * s větším poradím je kreslen nad obrazcem s menším poradím.
     * Neni-li zadaný obrazec mezi kreslenými, vrátí -1.
     *
     * @param  obrazec  Objekt, na jehož kreslicí pořadí se dotazujeme
     *
     * @return  Pořadí obrazce; prvý kresleny obrazec má pořadí 0.
     *           Neni-li zadaný obrazec mezi kreslenými, vrátí -1.
     */
    public synchronized int pořadí( IAnimovaný ia )
    {
        return animace.indexOf( ia );
    }


    /***************************************************************************
     * Vrátí nemodifikovatelný seznam všech spravovaných obrázků.
     *
     * @return  Požadovaný seznam
     */
    public synchronized List<IAnimovaný> getSeznam()
    {
        return Collections.unmodifiableList( animace );
    }

   
    
//== SOUKROMÉ A POMOCNÉ METODY TŘÍDY ===========================================
//== SOUKROMÉ A POMOCNÉ METODY INSTANCÍ ========================================
//== VNOŘENÉ A VNITŘNÍ TŘÍDY ===================================================
//== TESTY A METODA MAIN =======================================================
}

