/*******************************************************************************
 * Třída Strom_6b obsahuje oproti třídě Strom některá vylepšení kódu.
 *
 *  Od Strom je změněno:
 *  - přibyla definice statické konstanty AP
 *  - byl odstraněn statický atribut krok a odkazy na něj nahrazeny
 *    odkazy na krok aktivního plátna - AP.getKrok()
 *  - posunové metody posunVpravo(int) a  posunDolů(int) nyní odvozují
 *    svoji činnost od metody setPozice(int,int)
 *  - Byla zavedena optimalizace činnosti některých metod vhodným voláním
 *    dvojice AP.nekresli() - AP.vraťKresli()  
 *
 * @author     Rudolf Pecinovský
 * @version    2.01, duben 2004
 */
public class Strom_6b implements IKreslený, IPosuvný
{
//== KONSTANTNÍ ATRIBUTY TŘÍDY =================================================

    /** Udává, kolikrát je koruna širší než kmen. */
    public static final int IMPLICITNÍ_POMĚR_ŠÍŘKY = 10;

    /** Udává, kolikrát je strom vyšší než samotný kmen. */
    public static final int IMPLICITNÍ_POMĚR_VÝŠKY =  3;

    /** Pamatuje si odkaz na aktivní plátno, aby se zrychlily některé operace,
     *  ale hlavně aby se zjednodušilo jeho použití. */
    private static final AktivníPlátno AP = AktivníPlátno.getPlátno();



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

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



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

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

    /** Název sestávající z názvu třídy a pořadí instance */
    private final String název  = "Strom_" + pořadí;

    /* Odkaz na instanci představující korunu stromu. */
    private final Elipsa   koruna;

    /* Odkaz na instanci představující korunu stromu. */
    private final Obdélník kmen;



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

    /***************************************************************************
     * Vytvoří instanci zadané velikosti a upraví rozměr plátna tak,
     * aby byla na plátně právě zarámovaná.
     *
     * @param šířka   Šířka stromu, který chceme vytvořit a zarámovat.
     * @param výška   Výška stromu, který chceme vytvořit a zarámovat.
     */
    public static void zarámuj( int šířka, int výška )
    {
        AP.nekresli();        
            AP.setKrokRozměr( 1, šířka, výška );
            new Strom_6b( 0, 0, šířka, výška ).nakresli();
        AP.vraťKresli();
    }


    /***************************************************************************
     * Metoda upraví rozměr plátna a "vysadí" na něj alej dvou řad stromů
     * se třemi stromy v každé řadě. Stromy budou vysazeny šikmo ve směru
     * hlavní diagonály s kmenem zabírajícím 1/3 výšky a 1/10 šířky stromu.
     */
    public static void alej()
    {
        AP.nekresli();        
            AP.setKrokRozměr( 50, 8, 7 );
            new Strom_6b( 100,   0 ).nakresli();
            new Strom_6b( 300,   0 ).nakresli();
            new Strom_6b(  50, 100 ).nakresli();
            new Strom_6b( 250, 100 ).nakresli();
            new Strom_6b(   0, 200 ).nakresli();
            new Strom_6b( 200, 200 ).nakresli();
        AP.vraťKresli();
    }


    /***************************************************************************
     * Vytvoří v zadané oblasti zarámovaný obrázek stromu, přičemž šířka
     * rámu je zadána jako druhý parametr.
     * 
     * @param oblast     Oblast, do níž se má strom v rámu vykreslit.
     * @param šířkaRámu  Šířka rámu v obrazových bodech.
     */
    public static void obrázek( Oblast oblast, int šířkaRámu )
    {
        AP.nekresli();        
            AP.přidej( new Obdélník(oblast, Barva.ČERNÁ) );
            oblast.x += šířkaRámu;
            oblast.y += šířkaRámu;
            oblast.šířka -= 2*šířkaRámu;
            oblast.výška -= 2*šířkaRámu;
    
            AP.přidej( new Obdélník(oblast, Barva.AZUROVÁ) );
            new Strom_6b( oblast ).nakresli();
        AP.vraťKresli();
    }



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

    /***************************************************************************
     * Implicitní konstruktor vytvoří v levém horním rohu plátna
     * instanci širokou 100 bodů, vysokou 150 bodů
     * s kmenem zabírajícím 1/3 výška a 1/10 šířky stromu.
     */
    public Strom_6b()
    {
        this( 0, 0 );
    }


    /***************************************************************************
     * Vytvoří na zadaných souřadnicích
     * instanci širokou 100 bodů, vysokou 150 bodů
     * s kmenem zabírajícím 1/3 výška a 1/10 šířky stromu.
     *
     * @param x       x-ová souřadnice počátku, x>=0, x=0 má levý okraj plátna
     * @param y       y-ová souřadnice počátku, y>=0, y=0 má horní okraj plátna
     */
    public Strom_6b( int x, int y )
    {
        this( x, y, 100, 150 );
    }


    /***************************************************************************
     * Vytvoří na zadaných souřadnicích instanci se zadanou šířkou a výškou.
     * Poměr velikosti kmene ku zbytku stromu zůstane implicitní, tj.
     * kmen bude zabírat 1/3 výška a 1/10 šířky stromu.
     *
     * @param x       x-ová souřadnice počátku, x>=0, x=0 má levý okraj plátna
     * @param y       y-ová souřadnice počátku, y>=0, y=0 má horní okraj plátna
     * @param šířka   Šířka vytvářené instance,  šířka > 0
     * @param výška   Výška vytvářené instance,  výška > 0
     */
    public Strom_6b( int x, int y, int šířka, int výška )
    {
        this( x, y, šířka, výška,
            IMPLICITNÍ_POMĚR_ŠÍŘKY, IMPLICITNÍ_POMĚR_VÝŠKY );
    }


    /**************************************************************************
     * Vytvoří novou instanci se zadanou polohou a rozměry.
     *
     * @param pozice  Pozice vytvářené instance
     * @param rozměr  Rozměr vytvářené instance
     */
    public Strom_6b(Pozice pozice, Rozměr rozměr)
    {
        this( pozice.x, pozice.y, rozměr.šířka, rozměr.výška );
    }


    /**************************************************************************
     * Vytvoří novou instanci vyplňující zadanou oblast.
     *
     * @param oblast   Oblast definující pozici a rozměr vytvářené instance
     */
    public Strom_6b(Oblast oblast)
    {
        this( oblast.x, oblast.y, oblast.šířka, oblast.výška );
    }


    /***************************************************************************
     * Vytvoří na zadaných souřadnicích instanci se zadanou šířkou, výškou.
     * a poměrem velikosti kmene ku zbytku stromu.
     * Vytvořené instanci přiřadí její "rodné číslo".
     *
     * @param x       x-ová souřadnice počátku, x>=0, x=0 má levý okraj plátna
     * @param y       y-ová souřadnice počátku, y>=0, y=0 má horní okraj plátna
     * @param šířka   Šířka vytvářené instance,  šířka > 0
     * @param výška   Výška vytvářené instance,  výška > 0
     * @param podílŠířkyKmene  Kolikrát je kmen užší než celý strom
     * @param podílVýškyKmene  Kolikrát je kmen menší než celý strom
     */
    public Strom_6b( int x, int y, int šířka, int výška,
                    int podílŠířkyKmene, int  podílVýškyKmene )
    {
        int výškaKmene  = výška / podílVýškyKmene;
        int výškaKoruny = výška - výškaKmene;
        int šířkaKmene  = šířka / podílŠířkyKmene;
        int posunKmene  = ( šířka - šířkaKmene) / 2;
        AP.nekresli();        
            koruna = new Elipsa  ( x, y, šířka, výškaKoruny, Barva.ZELENÁ  );
            kmen   = new Obdélník( x+posunKmene, y+výškaKoruny,
                                   šířkaKmene, výškaKmene, Barva.HNĚDÁ );
        AP.vraťKresli();
    }



//== PŘÍSTUPOVÉ METODY VLASTNOSTÍ INSTANCÍ =====================================

    /***************************************************************************
     * Vrátí x-ovou souřadnici pozice instance.
     *
     * @return  x-ová souřadnice.
     */
    public int getX()
    {
        return koruna.getX();
    }


    /***************************************************************************
     * Vrátí y-ovou souřadnici pozice instance.
     *
     * @return  y-ová souřadnice.
     */
    public int getY()
    {
        return koruna.getY();
    }


    /***************************************************************************
     * Nastaví novou pozici instance.
     *
     * @param x   Nová x-ová pozice instance
     * @param y   Nová y-ová pozice instance
     */
    public void setPozice( int x, int y )
    {
        AP.nekresli();        
            koruna.setPozice( x, y );
            kmen  .setPozice( x  +  (koruna.getŠířka() - kmen.getŠířka()) / 2,
                              y  +  koruna.getVýška()     );
        AP.vraťKresli();
    }


    /***************************************************************************
     * Nastaví novou pozici instance.
     *
     * @param pozice   Nová pozice instance
     */
    public void setPozice(Pozice pozice)
    {
        setPozice( pozice.x, pozice.y );
    }


    /***************************************************************************
     * Vrátí šířku instance.
     *
     * @return  Šířka instance v bodech
     */
    public int getŠířka()
    {
        return koruna.getŠířka();
    }


    /***************************************************************************
     * Vrátí výšku instance.
     *
     * @return  Výška instance v bodech
     */
    public int getVýška()
    {
        return koruna.getVýška() + kmen.getVýška();
    }


    /***************************************************************************
     * Vrátí instanci třídy Rozměr s rozměry instance.
     *
     * @return   Rozměr s rozměry instance.
     */
    public Rozměr getRozměr()
    {
        return new Rozměr( getŠířka(), getVýška() );
    }


    /***************************************************************************
     * Vrátí instanci třídy Oblast s informacemi o pozici a rozměrech instance.
     *
     * @return   Oblast s informacemi o pozici a rozměre instance.
     */
    public Oblast getOblast()
    {
        return new Oblast( getX(), getY(), getŠířka(), getVýška() );
    }


    /***************************************************************************
     * Vrátí název instance, tj. název její třídy následovaný pořadím.
     *
     * @return  Řetězec s názvem instance.
     */
    public String getNázev()
    {
       return název;
    }


    /***************************************************************************
     * Vrátí barvu koruny stromu.
     *
     * @return  Instance třídy Barva definující nastavenou barvu koruny.
     */
    public Barva getBarvaKoruny()
    {
        return koruna.getBarva();
    }


    /***************************************************************************
     * Nastaví novou barvu koruny.
     *
     * @param nová  Požadovaná nová barva.
     */
    public void setBarvaKoruny( Barva nová )
    {
        koruna.setBarva( nová );
    }



//== PŘEKRYTÉ METODY IMPLEMENTOVANÝCH ROZHRANÍ =================================

    /***************************************************************************
     * Vykreslí obraz své instance na plátno.
     *
     * @param kreslítko  Objekt, jehož prostřednictvím se má instance nakreslit.
     */
    public void nakresli(Kreslítko kreslítko)
    {
        //Tady se dvojice nekresli() - vraťKresli() nepoužije,
        //protože se beztak právě překresluje
        koruna.nakresli(kreslítko);
        kmen  .nakresli(kreslítko);
    }



//== PŘEKRYTÉ ABSTRAKTNÍ METODY RODIČOVSKÉ TŘÍDY ===============================
//== PŘEKRYTÉ KONKRÉTNÍ METODY RODIČOVSKÉ TŘÍDY ================================
//== NOVĚ ZAVEDENÉ METODY INSTANCÍ =============================================

    /***************************************************************************
     * Převede instanci na řetězec obsahující název třídy, pořadí instance,
     * její souřadnice a rozměry.
     *
     * @return Řetězcová reprezentace dané instance.
     */
    public String toString()
    {
        return název + ": x=" + getX() + ", y=" + getY() +
               ", šířka=" + getŠířka() + ", výška=" + getVýška();
    }


    /***************************************************************************
     * Přihlási instanci u aktivního plátna do jeho správy.
     */
    public void nakresli()
    {
        AP.přidej( this );
    }


    /***************************************************************************
     * Odstraní obraz své instance z plátna.
     */
    public void smaž()
    {
        AP.odstraň( this );
    }


    /***************************************************************************
     * Přesune instanci o zadaný počet bodů vpravo,
     * při záporné hodnotě parametru vlevo.
     *
     * @param vzdálenost Vzdálenost, o kterou se instance přesune.
     */
    public void posunVpravo( int vzdálenost )
    {
        setPozice( getX()+vzdálenost, getY() );
    }


    /***************************************************************************
     * Přesune instanci o krok bodů vpravo.
     */
    public void posunVpravo()
    {
        posunVpravo( AP.getKrok() );
    }


    /***************************************************************************
     * Přesune instanci o krok bodů vlevo.
     */
    public void posunVlevo()
    {
        posunVpravo( -AP.getKrok() );
    }


    /***************************************************************************
     * Přesune instanci o zadaný počet bodů dolů,
     * při záporné hodnotě parametru nahoru.
     *
     * @param vzdálenost   Počet bodů, o které se instance přesune.
     */
    public void posunDolů( int vzdálenost )
    {
        setPozice( getX(), getY()+vzdálenost );
    }


    /***************************************************************************
     * Přesune instanci o krok bodů dolů.
     */
    public void posunDolů()
    {
        posunDolů( AP.getKrok() );
    }


    /***************************************************************************
     * Přesune instanci o krok bodů nahoru.
     */
    public void posunVzhůru()
    {
        posunDolů( -AP.getKrok() );
    }


    /***************************************************************************
     * Odstraní z plátna všechny ostatní instance a nastaví
     * parametry okna s plátnem tak, aby právě zarámovalo danou instanci.
     */
    public void zarámuj()
    {
        AP.nekresli();        
            AP.odstraňVše();
            AP.setKrokRozměr( 1, getŠířka(), getVýška() );
            setPozice( 0, 0 );
            AP.přidej( this );
        AP.vraťKresli();
    }


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

