package mnohotvar;

import java.util.LinkedList;
import java.util.List;

import rup.česky.tvary.AHýbací;
import rup.česky.tvary.IHýbací;

import rup.česky.tvary.Barva;
import rup.česky.tvary.Směr8;

import rup.česky.tvary.AktivníPlátno;
import rup.česky.tvary.Čtverec;
import rup.česky.tvary.Obdélník;
import rup.česky.tvary.Elipsa;
import rup.česky.tvary.Trojúhelník;
import rup.česky.tvary.Kruh;
import rup.česky.tvary.Přesouvač;
import rup.česky.tvary.Kompresor;


/*******************************************************************************
 * Třída slouží k demonstraci práce se seznamy.
 * Mnohotvar je postupně skládán z řady jednodušších tvarů, které musejí být
 * instancemi rozhraní IHýbací. Jiné požadavky na ně kladeny nejsou.
 * Při sestavování mnohotvar automaticky mění svoji pozici a rozměr tak,
 * aby pozice byla neustále v levém rohu opsaného obdélníka a rozměr mnohotvaru
 * odpovídal rozměru tohoto obdélníka.
 *
 * Oproti třídě Mnohotvar_13b se změnilo:
 * - Přibyly metody přidejPod, přidejNad, přidejDolů, přidejNahoru
 *   umožňující umístit přidávaný do definované vrstvy.
 * - Přibyla metoda zařaď umožňující jednoduší definici přivávacích metod.
 *
 * @author    Rudolf Pecinovský
 * @version   0.00.000,  0.0.2003
 */
public class Mnohotvar_13d extends AHýbací
{
//== KONSTANTNÍ ATRIBUTY TŘÍDY =================================================
//== PROMĚNNÉ ATRIBUTY TŘÍDY ===================================================
//== KONSTANTNÍ ATRIBUTY INSTANCÍ ==============================================

    /** Seznam prvků, z nichž se mnohotvar skládá. */
    private final List<Část> seznam = new LinkedList<Část>();



//== PROMĚNNÉ ATRIBUTY INSTANCÍ ================================================
//== PŘÍSTUPOVÉ METODY VLASTNOSTÍ TŘÍDY ========================================
//== OSTATNÍ METODY TŘÍDY ======================================================

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

    /***************************************************************************
     * Vytvoří prázdný mnohotvar,
     * který převezme název generovaný rodičovskou třídou     .
     */
    public Mnohotvar_13d()
    {
        this( "" );
    }


    /***************************************************************************
     * Definuje prázdný mnohotvar se zadaným názvem.
     *
     * @param název  Název vytvářeného mnohotvaru.
     */
    public Mnohotvar_13d(String název)
    {
        super( 0, 0, 0, 0 );
        if( název != "" )
            this.název = název;
    }



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

    /***************************************************************************
     * Nakreslí mnohotvar pomocí dodaného kreslítka.
     *
     * @param kreslitko   Kreslítko dodané aktivním plátnem
     */
    public void nakresli(rup.česky.tvary.Kreslítko kreslítko)
    {
        for( Část č : seznam )
        {
            č.ih.nakresli( kreslítko );
        }
    }



//== PŘEKRYTÉ KONKRÉTNÍ METODY RODIČOVSKÉ TŘÍDY ================================

    /***************************************************************************
     * Přemístí celý mnohotvar na zadanou pozici.
     * Všechny součásti instance se přemisťují jako celek
     *
     * @param x  Nastavovaná vodorovná souřadnice
     * @param y  Nastavovaná nastavovaná svislá souřadnice
     */
    public void setPozice( int x, int y )
    {
        int dx = x - xPos;
        int dy = y - yPos;
        AP.nekresli();
            for( Část č : seznam )
            {
                IHýbací čih = č.ih;
                čih.setPozice( dx + čih.getX(), dy + čih.getY() );
            }
            super.setPozice( x, y );
        AP.vraťKresli();
    }


    /***************************************************************************
     * Nastaví nový rozměr mnohotvaru. Upraví rozměry a pozice všech jeho
     * souřástí tak, aby výsledný mnohotvar i při novém rozměru stejný vzhled.
     *
     * @param šířka  Nastavovaná šířka
     * @param výška  Nastavovaná výška
     */
    public void setRozměr( int šířka, int výška )
    {
        //Zapamatuj si poměry změny šířky a výšky celého obrazce
        double ss = (double)šířka / this.šířka;
        double vv = (double)výška / this.výška;

        Část.připravKonstanty( xPos, yPos, this.šířka, this.výška,
                               xPos, yPos, šířka,      výška );
        //Uprav velikosti a pozice jednotlivých částí
        AP.nekresli();
            for( Část č : seznam )
                č.poNafouknutí();
            super.setRozměr( šířka, výška );
        AP.vraťKresli();
    }



//== NOVĚ ZAVEDENÉ METODY INSTANCÍ =============================================

    /***************************************************************************
     * Vrátí index zadaného prvku v mnohotvaru. Prvek, který je zcela vespod
     * má index 0, prvek který je zcela navrchu má index o jedničku menší,
     * než je počet prvků mnohotvaru.
     *
     * @param ih  Přidávaný hýbací tvar
     * 
     * @return    Index zadaného prvku v mnohotvaru. 
     *            Není-li prvek součástí mnohotvaru, vrátí -1.
     */
    public int index( IHýbací prvek )
    {
        int kde = 0;
        for( Část c : seznam )
        {
            if( c.ih == prvek )
                return kde;     //==========>
            kde++;
        }
        return -1;
    }


    /***************************************************************************
     * Přidá do mnohotvaru zadaný prvek a příslušně upraví novou
     * pozici a velikost mnohotvaru.
     *
     * @param ih  Přidávaný hýbací tvar
     */
    public void přidej( IHýbací ih )
    {
        if( index( ih ) >= 0 )
            throw new IllegalArgumentException( "V obrazci " + this.název +
                " je přidávaný prvek již zahrnut.\n   Přidáváno: " + ih);
        zařaď( seznam.size(), ih );
    }
    
    
    /***************************************************************************
     * Přidá do mnohotvaru zadaný prvek těsně pod označený prvek
     * a příslušně upraví novou pozici a velikost mnohotvaru.
     *
     * @param starý  Tvar, který je součástí mnohotvaru
     *               a pod nějž se nový prvek přidává
     * @param nový   Přidávaný hýbací tvar
     */
    public boolean přidejPod( IHýbací starý, IHýbací nový )
    {
        ZdaKde zk = připrav( starý, nový );
        zařaď( zk.kde, nový );
        return zk.zda;
    }
    
    
    /***************************************************************************
     * Přidá do mnohotvaru zadaný prvek těsně nad označený prvek
     * a příslušně upraví novou pozici a velikost mnohotvaru.
     *
     * @param starý  Tvar, který je součástí mnohotvaru
     *               a nad nějž se nový prvek přidává
     * @param nový   Přidávaný hýbací tvar
     */
    public boolean přidejNad( IHýbací starý, IHýbací nový )
    {
        ZdaKde zk = připrav( starý, nový );
        zařaď( zk.kde+1, nový );
        return zk.zda;
    }
    
    
    /***************************************************************************
     * Přidá do mnohotvaru zadaný prvek těsně nad označený prvek
     * a příslušně upraví novou pozici a velikost mnohotvaru.
     *
     * @param starý  Tvar, který je součástí mnohotvaru
     *               a nad nějž se nový prvek přidává
     * @param nový   Přidávaný hýbací tvar
     */
    public boolean přidejDolů( IHýbací nový )
    {
        ZdaKde zk = připrav( null, nový );
        zařaď( 0, nový );
        return zk.zda;
    }
    
    
    /***************************************************************************
     * Přidá do mnohotvaru zadaný prvek těsně nad označený prvek
     * a příslušně upraví novou pozici a velikost mnohotvaru.
     *
     * @param starý  Tvar, který je součástí mnohotvaru
     *               a nad nějž se nový prvek přidává
     * @param nový   Přidávaný hýbací tvar
     */
    public boolean přidejNahoru( IHýbací nový )
    {
        ZdaKde zk = připrav( null, nový );
        zařaď( seznam.size(), nový );
        return zk.zda;
    }
    
    
    
    //== SOUKROMÉ A POMOCNÉ METODY TŘÍDY ===========================================
    //== SOUKROMÉ A POMOCNÉ METODY INSTANCÍ ========================================
    
    /***************************************************************************
     * Přidá do mnohotvaru zadaný prvek těsně nad označený prvek
     * a příslušně upraví novou pozici a velikost mnohotvaru.
     *
     * @param starý  Tvar, který je součástí mnohotvaru
     *               a nad nějž se nový prvek přidává
     * @param nový   Přidávaný hýbací tvar
     */
    private ZdaKde připrav( IHýbací starý, IHýbací nový )
    {
        ZdaKde zk = new ZdaKde();
    
        int in = index( nový );
        if( in >= 0 )
        {
            seznam.remove( in );
            zk.zda = false;
        }
        else
            zk.zda = true;
    
        if( starý == null )
        {
            return zk;          //==========>
        }
    
        int is = index( starý );
        if( is < 0 )
        {
            throw new IllegalArgumentException(
                "Referenční objekt není součástí mnohotvaru!" );
        }
        zk.kde = is;
        return zk;
    }
    
    
    /***************************************************************************
     * Přidá do mnohotvaru zadaný prvek na zadané místo v seznamu
     * a příslušně upraví novou pozici a velikost mnohotvaru.
     *
     * @param index  Pozice v seznamu, kam bude nový prvek zařazen.
     *               Původní prvek na této pozici a následující prvky
     *               budou posunuty o jenu pozici dále.
     * @param ih     Přidávaný hýbací tvar
     */
    private void zařaď( int index, IHýbací ih )
    {
        if( seznam.isEmpty() )  //Přidáváme první
        {
            xPos  = ih.getX();
            yPos  = ih.getY();
            šířka = ih.getŠířka();
            výška = ih.getVýška();
            Část.připravKonstanty( 0, 0, 0, 0, xPos, yPos, šířka, výška );
        }
        else                    //Přidáváme další
        {
            int mx = xPos;
            int my = xPos;
            int mš = šířka;
            int mv = výška;
    
            int ihx = ih.getX();
            int ihy = ih.getY();
            int ihš = ih.getŠířka();
            int ihv = ih.getVýška();
    
            if( ihx < xPos )
            {
                šířka += xPos - ihx;
                xPos = ihx;
            }
            if( ihy < yPos )
            {
                výška += yPos - ihy;
                yPos = ihy;
            }
            if( (xPos + šířka) < (ihx + ihš) )
                šířka = ihx + ihš - xPos;
            if( (yPos + výška) < (ihy + ihv) )
                výška = ihy + ihv - yPos;
    
            Část.připravKonstanty( mx, my, mš, mv, xPos, yPos, šířka, výška );
            for( Část č : seznam )
                č.poPřidání();
        }
        AP.nekresli();
            AP.odstraň( ih );
            seznam.add( index, new Část( ih ) );
        AP.vraťKresli();
    }


//== VNOŘENÉ A VNITŘNÍ TŘÍDY ===================================================

    private static final class ZdaKde
    {
        boolean zda;
        int kde;
    }



    /*******************************************************************************
     * Třída slouží jako přepravka pro uchovávání pomocných informací
     * pro co nelepší změnu velikosti mnohotvaru.
     *
     * @author    Rudolf Pecinovský
     * @version   0.00.000,  0.0.2003
     */
    private static final class Část
    {
    //== KONSTANTNÍ ATRIBUTY TŘÍDY =================================================
    //== PROMĚNNÉ ATRIBUTY TŘÍDY ===================================================

        /** Staré a nové souřadnice a rozměry celého mnohotvaru. */
        static double smx, smy, smš, smv,
                      nmx, nmy, nmš, nmv;

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

        /** Odkaz na část mnohotvaru. */
        IHýbací ih;

        /** Podíl odstupu od levého kraje mnohotvaru na jeho celkové šířce. */
        double dx;

        /** Podíl odstupu od horního kraje mnohotvaru na jeho celkové výšce. */
        double dy;

        /** Podíl šířky části k celkové šířce mnohotvaru. */
        double dš;

        /** Podíl výšky části k celkové výšce mnohotvaru. */
        double dv;



    //== PŘÍSTUPOVÉ METODY VLASTNOSTÍ TŘÍDY ========================================
    //== OSTATNÍ METODY TŘÍDY ======================================================


        /***************************************************************************
         * Připravuje podklady pro následnou aktualizaci uchovávaných poměrů
         * klíčových rozměrů jednotlivých součástí mnohotvaru.
         * Tyto konstanty jsou pro všechny součásti mnohotvaru společné.
         *
         * @param sx  Původní (staré) x.
         * @param sy  Původní (staré) y.
         * @param sš  Původní (stará) šířka.
         * @param sv  Původní (stará) výška.
         * @param nx  Nové x.
         * @param ny  Nové y.
         * @param nš  Nová šířka.
         * @param nv  Nová výška.
         */
        static void připravKonstanty( int sx, int sy, int sš, int sv,
                                      int nx, int ny, int nš, int nv )
        {
            smx = sx;   smy = sy;   smš = sš;   smv = sv;
            nmx = nx;   nmy = ny;   nmš = nš;   nmv = nv;
        }

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

        /***************************************************************************
         * Vytvoří přepravku a zapamatuje si aktuální stav některých poměrů
         * vůči současné porobě multitvaru.
         */
        Část( IHýbací ih )
        {
            this.ih = ih;

            dx = (ih.getX() - nmx) / nmš;
            dy = (ih.getY() - nmy) / nmv;
            dš = ih.getŠířka() / nmš;
            dv = ih.getVýška() / nmv;
        }


    //== 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Í =============================================

        /***************************************************************************
         * Aktiualizuje uchovávané poměry klíčových rozměrů tvaru vůči mnohotvaru.
         * Očekává, že metoda připrav nastavila hodnoty potřebných konstant.
         */
        void poPřidání()
        {
            //Souřadnice se mohou pouze zmenšovat
            dx = (smx - nmx + dx*smš) / nmš;
            dy = (smy - nmy + dy*smv) / nmv;

            dš = dš * smš / nmš;
            dv = dv * smv / nmv;
        }


        /***************************************************************************
         * Aktualizuje uchovávané poměry klíčových rozměrů a vykreslí daný tvar
         * v aktuální pozici a rozměru.
         */
        void poNafouknutí()
        {
            ih.setPozice( (int)Math.round( nmx + dx*nmš ),
                          (int)Math.round( nmy + dy*nmv )  );
            ih.setRozměr( (int)Math.round( nmš*dš ),
                          (int)Math.round( nmv*dv ) );
        }


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

//== TESTY A METODA MAIN =======================================================
}

