package javacodebook.media.animation;

import java.awt.*;
import java.awt.geom.*;
import java.awt.image.*;
import javax.swing.*;

/**
 * Eine einfache Animation, bei der die Bewegung eines Planeten um eine Sonne
 * und eines Mondes um den Planeten simuliert wird. Physikalisch absolut nicht
 * überprüft ;-)
 */
public class Animator extends JComponent implements Runnable{
    double earthArc = 0;
    double moonArc = 0;
    private BufferedImage bufImage;

    /* Ausführung der Animation innerhalb eines Threads. */
    public void run() {
        while(true) {
            //neu Zeichnen
            animate();
            //kurze Pause einlegen
            try {
                Thread.sleep(10);
            } catch(InterruptedException e) {
                e.printStackTrace(System.out);
            }
        }
    }

    /**
     * Hier werden die Berechnungen zur Bewegung der Objekte durchgeführt und
     * die Objekte erst auf einem Offscreen-Image gezeichnet, dann auf den
     * Bildschirm.
     */
    public void animate() {
        //Die Erde im Gegenuhrzeigersinn um die Sonne drehen
        earthArc -= Math.PI / 360;
        if(earthArc >= Math.PI*2)
            earthArc = 0;
        //den Mond im Uhrzeigersinn um die Erde drehen
        moonArc += Math.PI / 60;
        if(moonArc >= Math.PI*2)
            moonArc = 0;
        //Eine Referenz auf den Grafik-Kontext der Komponente holen
        Graphics g = getGraphics();
        if(g != null) {
            paintComponent(g);
        }
        g.dispose();
    }

    public void paintComponent(Graphics g) {
        if(createBuffer()) {
            //Es wird zunächst in das Offscreen-Image gezeichnet. Dazu holen
            //wir uns dessen Grafik-Kontext
            Graphics2D g2 = (Graphics2D) bufImage.getGraphics();
            //Mit Hintergrundfarbe füllen
            g2.setColor(getBackground());
            g2.fillRect(0, 0, getWidth(), getHeight());
            //Mittelpunkt des Panels als Koordinatenursprung
            g2.translate(getWidth()/2, getHeight()/2);
            g2.setColor(Color.ORANGE);
            //Sonne zeichnen
            g2.fill(new Ellipse2D.Double(-25, -25, 50, 50));
            //Das Koordinatensystem so drehen, wie sich die Erde um die Sonne dreht
            g2.rotate(earthArc);
            //Zum Mittelpunkt der Erde verschieben
            g2.translate(120, 0);
            g2.setColor(Color.blue);
            //Erde zeichnen
            g2.fill(new Ellipse2D.Double(-10, -10, 20, 20));
            //Das Koordinatensystem so drehen, wie sich der Mond um die Erde dreht
            g2.rotate(moonArc);
            //Zum Mittelpunkt des Mondes verschieben
            g2.translate(20, 0);
            g2.setColor(Color.white);
            //Mond zeichnen
            g2.fill(new Ellipse2D.Double(-5, -5, 10, 10));
            //jetzt das BufferedImage auf den Grafik-Kontext der Komponente,
            //also auf den Bildschirm zeichnen
            g.drawImage(bufImage, 0, 0, this);
            g2.dispose();
        }
    }

    /*
     * Das BufferedImage kann erst erzeugt werden, wenn die Komponente gezeichnet
     * und die Größe damit bekannt ist.
     */
    private boolean createBuffer() {
        if(bufImage != null)
            return true;//BufferedImage wurde bereits erzeugt
        else {
            if(getWidth() == 0 || getHeight() == 0)
                return false;//Komponente wurde noch nicht gezeichnet
            bufImage = new BufferedImage(getWidth(), getHeight(), Transparency.OPAQUE);
        }
        return true;
    }

    public Dimension getPreferredSize() {
        return new Dimension(320,320);
    }

    public static void main(String[] args) {
        JFrame f = new JFrame();
        f.setResizable(false);
        f.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
        f.getContentPane().setLayout(new BorderLayout());
        Animator animator = new Animator();
        f.getContentPane().add(animator, BorderLayout.CENTER);
        f.pack();
        f.show();
        Thread thread = new Thread(animator);
        thread.start();
    }


}
