/***********************************************************************

 CWUhr.java
 Christian Wirthensohn <christian@wirthensohn.de>
 14.01.2003

 Quasi Fotorealistische Armbanduhr mit Digital- und Analoganzeige als
 Java-Applet.

***********************************************************************/


import java.applet.*;
import java.awt.*;
import java.util.*;


public class CWUhr extends Applet implements Runnable
{
    Thread UhrThread;

    Image UhrImage;
    Image[] UhrImageDigits = new Image[10];
    Image Buffer;
    Graphics GraphicsBuffer;

    int UhrSekundenAlt = 99;
    int UhrSekundenTakt = 0;


    public void start()
    {
        if (UhrThread == null)
        {
            UhrThread = new Thread(this);
            UhrThread.start();
        }
    }


    public void stop()
    {
        if (UhrThread != null)
        {
            UhrThread.stop();
            UhrThread = null;
        }
    }


    public void run()
    {
        while (UhrThread != null)
        {
            try
            {
                UhrThread.sleep(100);
            }
            catch (Exception e) { }

            repaint();
        }
    }


    // Initialisierung des Applets
    // (Laden der Grafiken, vorbereiten des Double Buffering)

    public void init()
    {
        MediaTracker tracker = new MediaTracker(this);
        int Count;


        // Hintergrundbild der Uhr laden
        // (der MediaTracker sorgt dafuer, dass die Grafik auch wirklich
        // geladen wird, sonst gibt das nix)

        UhrImage = getImage(getCodeBase(), "uhr.jpg");
        tracker.addImage(UhrImage, 0);
        try
        {
            // An dieser Stelle muss gewartet werden, bis der Tracker
            // seine Arbeit erledigt hat, sonst sieht man rein gar
            // nichts!

            tracker.waitForID(0);
        }
        catch (Exception e) { }

        // Hintergrundbild in Puffer laden
        // (von wegen Double-Buffering und so....)

        Buffer = createImage(UhrImage.getWidth(this),
                             UhrImage.getHeight(this));
        GraphicsBuffer = Buffer.getGraphics();


        // Bilder der Einzelziffern der Digitalanzeige laden
        // (und auch hier darf wieder der MediaTracker ran)

        for (Count=0; Count<10; Count++)
        {
            UhrImageDigits[Count] = getImage(getCodeBase(),
                                             Count + ".jpg");
            tracker.addImage(UhrImageDigits[Count], Count+1);
            try
            {
                // Siehe weiter oben, hier ginge es zwar auch ohne das
                // Warten auch den Tracker, aber besser iss das!

                tracker.waitForID(Count);
            }
            catch (Exception e) { }
        }
    }


    // 'PolygonPunkt' ist eine kleine Hilfsfunktion, die von 'Linie'
    // verwendet wird, um die einzelnen Punkt des Polygons/Zeigers
    // gemaess mathematischer Berechnung (oh Grauen) so "umzubiegen",
    // dass eine Rotation um den Winkel 'winkel' entsteht.
    // Uebrigens: der Mittelpunkt der Rotation ist hier fest auf
    // 126, 181 eingestellt.

    void PolygonPunkt(Polygon Zeiger, int x, int y, int winkel)
    {
        double rad = (winkel * 2 * Math.PI) / 360;
        double cos = Math.cos(rad);
        double sin = Math.sin(rad);

        Zeiger.addPoint ((int)(126+x*cos-y*sin), (int)(181+y*cos+x*sin));
    }


    // 'Linie' erzeugt den Zeiger.

    void Linie(int laenge, int halbebreite, int winkel, Color farbe, int offset)
    {
        Polygon Zeiger = new Polygon();

        // Ein Polygon soll den gewuenschten Zeiger erzeugen

        PolygonPunkt (Zeiger, 0-halbebreite, 15-offset, winkel);
        PolygonPunkt (Zeiger, halbebreite, 15-offset, winkel);
        PolygonPunkt (Zeiger, halbebreite, 0-laenge, winkel);
        PolygonPunkt (Zeiger, 0, 0-laenge-halbebreite, winkel);
        PolygonPunkt (Zeiger, 0-halbebreite, 0-laenge, winkel);


        // Und nun das Werk in den Doppelten Boden (Double Buffering!)
        // gemalt...

        GraphicsBuffer.setColor(farbe);
        GraphicsBuffer.fillPolygon(Zeiger);

        if (halbebreite > 2)
        {
            // Sich selbst nochmal aufrufen, um den Cyan-farbenen Teil
            // des Zeigers zu zeichnen.

            Linie (laenge - 3, halbebreite - 2, winkel, Color.cyan, 3);
        }
    }


    // Das Update-Event

    public void update(Graphics g)
    {
        paint(g);
    }


    // Das Paint-Event

    public void paint(Graphics g)
    {
        Date UhrDatum = new Date();

        int UhrSekunden = UhrDatum.getSeconds();


        // Der folgende Code wird so Pi-Mal-Daumen sekuendlich
        // ausgefuehrt:

        if (UhrSekunden != UhrSekundenAlt)
        {
            int UhrMinuten = UhrDatum.getMinutes();
            int UhrStunden = UhrDatum.getHours();

            int Digit1 = UhrStunden / 10;
            int Digit2 = UhrStunden % 10;
            int Digit3 = UhrMinuten / 10;
            int Digit4 = UhrMinuten % 10;

            g.drawImage(Buffer, 0, 0, this);

            GraphicsBuffer.drawImage(UhrImage, 0, 0, this);
            GraphicsBuffer.setColor(Color.black);


            // Darstellung der einzelnen Digits der Digitalanzeige
            // Es werden einfach die bei der Initialisierung geladenen
            // Einzelgrafiken auf das Hintergrundbild geworfen.

            GraphicsBuffer.drawImage(UhrImageDigits[Digit1],
                                     103, 222, this);
            GraphicsBuffer.drawImage(UhrImageDigits[Digit2],
                                     114, 222, this);
            GraphicsBuffer.drawImage(UhrImageDigits[Digit3],
                                     127, 222, this);
            GraphicsBuffer.drawImage(UhrImageDigits[Digit4],
                                     138, 222, this);

            // Darstellung des Sekundentakts in Form zweier Punkte
            // zwischen den Stunden- und Minuten-Digits der
            // Digitalanzeige

            UhrSekundenTakt = 1 - UhrSekundenTakt;
            if (UhrSekundenTakt == 1)
            {
                GraphicsBuffer.fillRect(125, 227, 2, 2);
                GraphicsBuffer.fillRect(125, 232, 2, 2);
            }


            // Zeichnen der analogen Zeiger
            // Da die Funktion 'Linie' den Winkel uebergeben bekommt,
            // um den es die Zeiger rotieren sollen, kann hier auch
            // eine kontinuierlichere Bewegung der Zeiger gewaehlt
            // werden.

            Linie(50, 3,
                  (UhrStunden * 30) + (UhrMinuten / 2),
                  Color.gray, 0);
            Linie(80, 3,
                  (UhrMinuten * 6) + (UhrSekunden / 10),
                  Color.gray, 0);
            Linie(80, 1, UhrSekunden * 6, Color.gray, 0);


            UhrSekundenAlt = UhrSekunden;
        }
    }
}

