package rup.česky.tvary_3;

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


/*******************************************************************************
 * Třída Kompresor slouží ke změně velikosti objektu
 * implementujicich rozhraní INafukovací.
 * Oproti verzi z projektu _06_Rozhraní doplňuje možnost 
 * definovat u na/vy-fukovaného objektu směr, v němž se předpokládá kotvící bod, 
 * který se při změně rozměru nehýbá.
 * Třída NENÍ vláknově bezpečná (thread-safe). Nepředpokládá, 
 * že její instance boudou volány simultánně z různých vláken.
 *
 * @author     Rudolf Pecinovsky
 * @version    1.01, 10.3.2003
 */
public class Kompresor
{
//== VEŘEJNÉ KONSTANTY =========================================================
//== SOUKROMÉ KONSTANTY ========================================================

    /** Doba mezi jednotlivými "šťouchy".*/
    private static int ČEKÁNÍ = 30;

    /** Tento atribut je tu pouze pro zjednodušení psaní. */
    private static final AktivníPlátno AP = AktivníPlátno.getPlátno();



//== ATRIBUTY TŘÍDY ============================================================
//== ATRIBUTY INSTANCÍ =========================================================

    /** Specifikuje sílu "nafukování" objektu danou instanci kompresoru,
     * tj. míru jeho prifouknuti. */
    private int síla;



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

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

    /***************************************************************************
     * Konstruktor kompresorů se silou nafukování 1 (síla nafukování definuje
     * počet bodů, o něž se zvětší rozměr objektu po jednom "šťouchu").     
     */
    public Kompresor()
    {
        this( 1 );
    }


    /***************************************************************************
     * Konstruktor kompresorů se zadanou silou nafukování (síla nafukování
     * definuje počet bodů, o něž se zvětší rozměr objektu po jednom "šťouchu").
     *
     * @param síla  Sila nafukování vytvářeného kompresoru
     */
    public Kompresor(int síla)
    {
        this.síla = síla;
    }



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

    /***************************************************************************
     * Metoda zvětší zadanou instanci o zadaný násobek síly svého kompresoru.
     * Při změně velikosti se zachovává poměr stran.     
     * Nejprve ale zabezpečí, aby byl objekt zobrazen na plátně.     
     *
     * @param koho     Zvětšovaný objekt
     * @param šťouchů  Počet přifouknutí o sílu kompresoru
     */
    public void přifoukni(INafukovací koho, int šťouchů )
    {
        int x = koho.getŠířka();
        int y = koho.getVýška();
        double d = delka( x, y );
        foukej( koho, šťouchů, síla*x/d, síla*y/d, Směr8.SEVEROZÁPAD );
    }


    /***************************************************************************
     * Metoda zvětší zadanou instanci o pětinásobek síly svého kompresoru.
     * Při změně velikosti se zachovává poměr stran.     
     * Nejprve ale zabezpečí, aby byl objekt zobrazen na plátně.     
     *
     * @param koho  Zvětšovaný objekt
     */
    public void přifoukni(INafukovací koho )
    {
        přifoukni( koho, 5 );
    }


    /***************************************************************************
     * Metoda zmenší zadanou instanci o zadaný násobek síly svého kompresoru.
     * Při změně velikosti se zachovává poměr stran.     
     * Nejprve ale zabezpečí, aby byl objekt zobrazen na plátně.     
     *
     * @param koho      Zmenšovany objekt
     * @param šťouchů   Počet ufouknuti o sílu kompresoru
     */
    public void ufoukni(INafukovací koho, int šťouchů )
    {
        síla = -síla;
        přifoukni( koho, šťouchů );
        síla = -síla;
    }


    /***************************************************************************
     * Metoda zmenší zadanou instanci o pětinásobek síly svého kompresoru.
     * Při změně velikosti se zachovává poměr stran.     
     * Nejprve ale zabezpečí, aby byl objekt zobrazen na plátně.     
     *
     * @param koho  Zmenšovaný objekt
     */
    public void ufoukni(INafukovací koho)
    {
        ufoukni( koho, 5 );
    }



    /***************************************************************************
     * Nafoukne či vyfoukne zadaný objekt na požadovanou velikost.
     * Nejprve ale zabezpečí, aby byl objekt zobrazen na plátně.     
     *
     * @param koho    Na(vy)fukovany objekt
     * @param šířka   Požadovaná výsledná šířka objektu
     * @param výška   Požadovaná výsledná výška objektu
     */
    public void nafoukniNa( INafukovací koho, int šířka, int výška )
    {
        nafoukniNa( koho, šířka, výška, Směr8.SEVEROZÁPAD, 0 );
    }


    /***************************************************************************
     * Nafoukne či vyfoukne zadaný objekt na požadovanou velikost.
     * Nejprve ale zabezpečí, aby byl objekt zobrazen na plátně.     
     *
     * @param koho    Na(vy)fukovany objekt
     * @param rozměr  Požadovaný rozměr objektu
     */
    public void nafoukniNa( INafukovací koho, Rozměr rozměr )
    {
        nafoukniNa( koho, rozměr.šířka, rozměr.výška );
    }


    /***************************************************************************
     * Nafoukne či vyfoukne zadaný objekt na požadovanou velikost.
     * Nejprve ale zabezpečí, aby byl objekt zobrazen na plátně.     
     *
     * @param koho    Na(vy)fukovany objekt
     * @param šířka   Požadovaná výsledná šířka objektu
     * @param výška   Požadovaná výsledná výška objektu
     * @param směr    Směr od středu obrazce, v němž se nachází pevný bod,
     *                který při změně rozměru nemění svoji pozici.
     *                směr==null označuje symetrický růst vůči středu obrazce.
     */
    public void nafoukniNa( IHýbací koho, int šířka, int výška, Směr8 směr )
    {
        nafoukniNa( (INafukovací)koho, šířka, výška, směr, 0 );
    }


    /***************************************************************************
     * Nafoukne či vyfoukne zadaný objekt na požadovanou velikost.
     * Nejprve ale zabezpečí, aby byl objekt zobrazen na plátně.     
     *
     * @param koho    Na(vy)fukovany objekt
     * @param rozměr  Požadovaný rozměr objektu
     * @param směr    Směr od středu obrazce, v němž se nachází pevný bod,
     *                který při změně rozměru nemění svoji pozici.
     *                směr==null označuje symetrický růst vůči středu obrazce.
     */
    public void nafoukniNa( INafukovací koho, Rozměr rozměr, Směr8 směr )
    {
        nafoukniNa( koho, rozměr.šířka, rozměr.výška, směr, 0 );
    }



//== SOUKROMÉ A POMOCNÉ METODY TŘÍDY ===========================================

    /***************************************************************************
     *  Spočte délku přepony pravoúhlého trojúelníku se zadanými odvěsnami. 
     * 
     * @param x   Délka první odvěsny
     * @param y   Délka druhé odvěsny
     * 
     * @return    Délka přepony
     */
    private static double delka( int x, int y )
    {
        return Math.sqrt(x*x + y*y);
    }



//== SOUKROMÉ A POMOCNÉ METODY INSTANCÍ ========================================

    /***************************************************************************
     * Nafoukne či vyfoukne zadaný objekt na požadovanou velikost.
     * Nejprve ale zabezpečí, aby byl objekt zobrazen na plátně.     
     *
     * @param koho    Na(vy)fukovany objekt
     * @param šířka   Požadovaná výsledná šířka objektu
     * @param výška   Požadovaná výsledná výška objektu
     */
    private void nafoukniNa( INafukovací koho, int šířka, int výška, 
                             Směr8 směr, int záslepka )
    {
        int    vodorovne  = šířka - koho.getŠířka();
        int    svisle     = výška - koho.getVýška();
        int    kroku      = (int)(delka(vodorovne,  svisle) / síla);
        double dx = (double)vodorovne / kroku;
        double dy = (double)svisle    / kroku;
        foukej( koho, kroku, dx, dy, směr );
    }


    /***************************************************************************
     * Výkonná metoda, která zařídí vlastní nafouknutí, resp. vyfouknutí
     * zadaného objektu na základě přípravených parametrů.
     *       
     * @param koho     Objekt, jehož velikost měníme.
     * @param šťouchů  Počet kroků, v nichž velikost objektu změníme.
     * @param dx       Zvětšení šířky objektu v jednom kroku.
     * @param dy       Zvětšení výšky objektu v jednom kroku.              
     */              
    private void foukej( INafukovací koho, int šťouchů, double dx, double dy,
                         Směr8 směr )
    {
        IHýbací ih = null;
        double dxx = 0, dyy = 0;
        double x   = 0, y   = 0;
        if( směr != Směr8.SEVEROZÁPAD )
        {
            ih = (IHýbací)koho;
            x  = ih.getX() + .4;
            y  = ih.getY() + .4;
            if(      (směr == Směr8.JIHOVÝCHOD)   || 
                     (směr == Směr8.VÝCHOD)       || 
                     (směr == Směr8.SEVEROVÝCHOD) )
            {
                dxx = -dx;
            }
            else if( (směr == Směr8.SEVER)  || 
                     (směr == Směr8.JIH)    || 
                     (směr == null) )     
            {
                dxx = -dx/2;
            }
            if(      (směr == Směr8.JIHOZÁPAD)  || 
                     (směr == Směr8.JIH)        || 
                     (směr == Směr8.JIHOVÝCHOD) )
            {
                dyy = -dy;
            }
            else if( (směr == Směr8.VÝCHOD)  || 
                     (směr == Směr8.ZÁPAD)   || 
                     (směr == null) )     
            {
                dyy = -dy/2;
            }
        }
        AP.přidej( koho );
        //Konstatnu připočítáváme proto, aby skoky byly vyrovnanější
        double šířka = koho.getŠířka() + .4;
        double výška = koho.getVýška() + .4;
        while( šťouchů-- > 0 )
        {
            P.čekej(ČEKÁNÍ);
            šířka += dx;
            výška += dy;
            AP.nekresli();
                koho.setRozměr( (int)šířka, (int)výška );
                if( směr != Směr8.SEVEROZÁPAD )
                {
                    x += dxx;
                    y += dyy;
                    ih.setPozice( (int)x, (int)y );
                }
            AP.vraťKresli();
        }
    }



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

