import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.Rectangle;
import java.awt.Shape;

import java.awt.event.KeyListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;

import java.awt.geom.Rectangle2D;

import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.WindowConstants;


/*******************************************************************************
 * Třída Vesmír slouží jako zdroj pomocných tříd pro hru UFO.
 * Sama o sobě pak představuje ekvivalent plátna.
 *
 * @author     Rudolf Pecinovský
 * @version    1.00, červen 2004
 */
public class Vesmír
{
//== KONSTANTNÍ ATRIBUTY TŘÍDY =================================================

    /** Počáteční šířka plátna v bodech. */
    public static final int ŠÍŘKA_0 = 300;

    /** Počáteční výška plátna v bodech. */
    public static final int VÝŠKA_0 = 300;

    /** Počáteční barva pozadí plátna. */
    public static final Barva BARVA_POZADÍ = Barva.ČERNÁ;

    /** Titulek v záhlaví okna plátna. */
    private static final String TITULEK  = "Vesmír s UFO";

    /** Jediná instance třídy Plátno. */
    public static final Vesmír V = new Vesmír();



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

    //Z venku neovlivnitelné Atributy pro zobrazeni plátna v aplikačním okně

        /** Aplikační okno animacniho plátna. */
        private JFrame okno;

        /** Instance lokální třídy, která je zřízena proto, aby odstínila
         *  metody svého rodiče JPanel. */
        private JPanel vlastníPlátno;

        /** Vše se kreslí na obraz - ten se snadněji překreslí. */
        private Image obrazPlátna;

//        private Image[] obraz   = new Image[2];
//        private int     aktivní = 0;

        /** Kreslítko získané od obrazu plátna, na nějž se vlastně kreslí. */
        private Graphics2D kreslítko;


    //Přímo ovlivnitelné atributy
        private int šířka;
        private int výška;



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

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

    /***************************************************************************
     * Jediná metoda umožnující získat odkaz na instanci plátna.
     * Vrací vsak pokaždé odkaz na stejnou instanci, protože tato instance
     * je V. Pokud instance při volaní metody ještě neexistuje,
     * metoda instanci vytvoří.
     *
     * @return Odkaz na instanci třídy Plátno.
     */
    public static Vesmír getVesmír()
    {
        V.okno.setVisible(true);
        return V;
    }


    /***************************************************************************
     * Implicitní (a jediný) konstruktor - je volán pouze jednou.
     */
    private Vesmír()
    {
        okno  = new JFrame();          //Vytvoří nové aplikační okno
        okno.setTitle(TITULEK);
        okno.setResizable( false );    //Není možné měnit rozměr myši
        okno.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
        okno.addWindowListener(new WindowAdapter() {
            public void windowClosing(WindowEvent evt) {
                System.exit(0);
            }
        });
        vlastníPlátno = new JPanel()
        {
            /** Povinně překrývaná abstraktní metoda třídy JPanel. */
            public void paint(Graphics g)
            {
                g.drawImage(obrazPlátna, 0, 0, null);
            }

        };
        okno.setContentPane(vlastníPlátno);
        setRozměr(ŠÍŘKA_0, VÝŠKA_0);        //Připraví a vykreslí prázdné plátno
    }



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

    /***************************************************************************
     * Nastaví viditelnost okna s vesmírem.
     *
     * @param Viditelný logická hodnota požadované viditelnost (true=viditelné)
     */
    public void setViditelný()
    {
        okno.setVisible( true );
        okno.toFront();
    }


    /***************************************************************************
     * Nastaví pro plátno barvu popředí.
     *
     * @param  barva  Nastavovaná barva popředí
     */
    public void setBarvaPopředí(Barva barva)
    {
        kreslítko.setColor( barva.getColor() );
    }


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


    /***************************************************************************
     * Vrátí výšku plátna.
     *
     * @return  Aktuální výška plátna v bodech
     */
    public int getVýška()
    {
        return výška;
    }


    /***************************************************************************
     * Nastaví nový rozměr plátna zadáním jeho výsky a šířky.
     *
     * @param  šířka  Nova šířka plátna v bodech
     * @param  výška  Nová výška plátna v bodech
     */
    public void setRozměr(int šířka, int výška)
    {
        boolean upravit;
        do
        {
            this.šířka = šířka;
            this.výška = výška;
            okno.setResizable(true);
            vlastníPlátno.setPreferredSize( new Dimension(šířka, výška) );
            okno.setMaximizedBounds( new Rectangle (šířka, výška) );
            okno.pack();
            java.awt.Dimension dim = okno.getSize( null );
            java.awt.Insets    ins = okno.getInsets();
            upravit = false;
            if( šířka < (dim.width - ins.left - ins.right) )
            {
                šířka = dim.width - ins.left - ins.right + 2;
                upravit = true;
            }
            if( výška < (dim.height - ins.top - ins.bottom) )
            {
                výška = dim.height - ins.top - ins.bottom;
                upravit = true;
            }
        }while( upravit );
        obrazPlátna = vlastníPlátno.createImage(šířka+2, výška+2);
        kreslítko = (Graphics2D)obrazPlátna.getGraphics();
        kreslítko.setBackground( BARVA_POZADÍ.getColor() );
        okno.setResizable(false);
        okno.setVisible(true);
        okno.toFront();
        připravObrázek();
        smaž();
    }



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

    /***************************************************************************
     * Vypíše text aktuálním písmem a aktuální barvou pozadí.
     *
     * @param  text   Zobrazovaný text
     * @param  x      x-ová souřadnice textu
     * @param  y      y-ová souřadnice textu
     */
    public void kresliString(String text, int x, int y)
    {
        kreslítko.setColor( BARVA_POZADÍ.getColor() );
        kreslítko.drawString( text, x, y );
        //vlastníPlátno.repaint();
    }


    /***************************************************************************
     * Nakreslí zadaný obrazec a vybarví jej barvou popředí plátna.
     *
     * @param  obrazec  Kreslený obrazec
     */
    public void zaplň( Shape obrazec )
    {
        kreslítko.fill( obrazec );
        //vlastníPlátno.repaint();
    }


    /***************************************************************************
     * Překreslí plátno.
     */
    public void překresli()
    {
        vlastníPlátno.repaint();
    }


    /***************************************************************************
     * Smaže plátno, přesněji smaže všechny obrazce na plátně.
     * Tato metoda by měla býr definována jako metodoa instance,
     * protože je instance jedináček,
     * byla metoda pro snazší dostupnost definovaná jako metoda třídy.
     * Jinak by totiž bylo potřeba vytvořit před smazáním plátna jeho instanci.
     */
    public void smaž()
    {
        if( V == null ) return;
        Color original = kreslítko.getColor();
        kreslítko.setColor( BARVA_POZADÍ.getColor() );
        Shape sh = new Rectangle2D.Double(0, 0, V.šířka, V.výška);
        kreslítko.fill( sh );
        kreslítko.setColor( original );
        //vlastníPlátno.repaint();
    }


    /***************************************************************************
     * Přihlásí posluchače událostí klávesnice.
     */
    public void přihlašKlávesnici( KeyListener posluchač )
    {
        okno.addKeyListener( posluchač );
    }



    /***************************************************************************
     * Odhlásí posluchače klávesnice
     */
    public void odhlašKlávesnici( KeyListener posluchač )
    {
        okno.removeKeyListener( posluchač );
    }



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

    /***************************************************************************
     * Nastaví viditelnost plátna.
     * Při nastavení viditelného plátna je přesune do popředí.
     * Lze ji proto použit i pro přesun již viditelného plátna do popředí.
     *
     * @param visible logická hodnota požadované viditelnost (true=viditelné)
     */
    private void připravObrázek()
    {
        obrazPlátna = vlastníPlátno.createImage(šířka, výška);
        kreslítko = (Graphics2D)obrazPlátna.getGraphics();
        kreslítko.setColor(BARVA_POZADÍ.getColor());
        kreslítko.fillRect(0, 0, šířka, výška);
        kreslítko.setColor(Color.black);
    }



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

