/*
 * Demo Applet zur Demonstration der Moeglichkeiten von Java (Just for fun)
 * @author      K.O. Linn
 * @version     1.0    entwickelt aus einer Vorlage von J. Gosling (Sun Microsystems)
 * @version     1.1    20.5.96 Hans Huckebein als Image Loop implementiert
 * @version     1.2    29.6.96 Umlaute mit Unicode; Start/Stop Buttons
 * @version     1.3    30.6.96 Bargraph mit Zeiten
 * @version     1.4    12.12.96 Statistik mit !lokal abschaltbar
 * @version     1.5    22.1.97  Scrolbar zum Einstellen der Geschwindigkeit
 *
 * In diesem Applet kommen folgende Features von Java vor:
 *  1. Animation (Image loop)
 *  2. Thread Handling (suspend/resume)
 *  3. Synchronisation von Threads (synchronized)
 *  4. Grafische Wiedergabe von Text. (drawString)
 *  5. Bild-Darstellung mit und ohne double buffering
 *  6. Ueberwachung des Ladens der Bilder (MediaTracker)
 *  7. Zeitmessung
 *  8. File I/O (RandomAccess zur Nachbildung von "append" der in Java z.Zt. fehlt)
 *  9. Darstellung der Umlaute mit Unicode ("\u0000")
 * 10. Buttons (Start/Stop)
 * 11. Balken-grafik
 * 12 Parameteruebergabe aus html-FIle
 * 13. Anlegen von String- und Array-Objekten
 * 14. .... und viel, viel objektorientierte Programmierung
 * Anm.: Dieses Applet funktioniert z.Zt. nur mit dem Java Appletviewer vernnftig
 * Beim Test mit Netscape gibt es Security Probleme mit dem  Filezugriff
 *
 */

import java.io.*;
import java.awt.*;
import java.util.*;
import java.net.*;

public class hucke extends java.applet.Applet implements Runnable {

    int loopslot = -1;      // The current loop slot.
    String dir;             //The directory or URL from which the images are loaded
    Thread kicker = null;   //The thread animating the images.
    int pause1,pause2;      //The length of the pause between revs.
    long diffzeit,startzeit;           //Messung der Bildladezeit
    Image       im;                    //The onscreen image.(double buffering)
    Image imgs[];                      //The offscreen images
    Image intro;                       //The intro picture
    int nimgs;                         //The number of images.
    Graphics    offscreen;             //The offscreen graphics context
    Date zeit;              //dient der Zeitmessung
    MediaTracker tracker;   //zur Ladekontrolle der Bilder und zur Messung der Bandbreite
    int average;
    int akleiner;
    int aktindex;
    InetAddress lokalHost;
    URL    woher;

    String              zeitfile;
    String              title;
    Font                titleFont;
    FontMetrics         fm;
    int                 titleHeight = 15;
    int                 columns;
    int                 values[];
    int                 limits[];
    Object              colors[];
    Object              labels[];
    int                 maxLabelWidth = 0;
    int                 barWidth;
    int                 barSpacing = 10;
    int                 max = 0;
    boolean             paintStats = false;
    boolean             local = true;       //mit Lese- und Schreibrechten
    int                 nvalues = 1;
    int                 maxvalue = 1;

    public void xferStats(long aktzeit) {  //Messung der Transfer Statistik
        int summezeiten = 0;
        int anzahlkleiner = 0;
        int average;
        int i;
        long fzeit;

        try {
            RandomAccessFile infile = new RandomAccessFile(zeitfile,"r");
            if ((nvalues = (int) infile.length()/8) == 0) return;

            for (int j=0; j < nvalues; j++) {  //scan Zeiten
                fzeit = infile.readLong();
                summezeiten += (int) fzeit;
                if (aktzeit < fzeit) {
                    anzahlkleiner++;
                }
                for (i=0; i < columns-1; i++) {
                    if (fzeit <= limits[i+1]) { values[i]++; break;}
                }
                if (fzeit > limits[columns-1]) values[columns-1]++;  //Restklasse
            }
            infile.close();
        } catch (IOException e) System.out.println("IO-Error in File: " + zeitfile);

        for (i=0; i < columns-1; i++) {
            if ((aktzeit > limits[i]) && (aktzeit <= limits[i+1])) aktindex = i;
            if (maxvalue < values[i]) maxvalue = values[i];
        }
        if (aktzeit > limits[columns-1]) aktindex = columns-1;
        colors[aktindex] = Color.red;  //in dieser Klasse ist der aktuelle Wert
        average = summezeiten/nvalues;
        akleiner = 100*anzahlkleiner/nvalues;
    }  // Ende Statistik

    public void init() {      //Initialize the applet. Get attributes.
        String at = getParameter("img");
        dir = (at != null) ? at : "./images";
        at = getParameter("pause");
        pause2 = (at == null) ? 0 : Integer.valueOf(at).intValue();
        at = getParameter("local");
        local = Boolean.valueOf(at).booleanValue();

        titleFont = new java.awt.Font("Courier", Font.BOLD, 12);
        fm = getFontMetrics(titleFont);

        at = getParameter("columns");
        if (at == null) {
            columns = 5;
        } else {
            columns = Integer.valueOf(at).intValue();
        }

        int tmpindex = 1 + getCodeBase().getFile().toString().lastIndexOf((String)"/");
//      zeitfile = getCodeBase().getFile().toString().substring(0,tmpindex).concat("zeiten.dat");
        zeitfile = "zeiten.dat";

        barWidth = titleFont.getSize();
        values = new int[columns];
        limits = new int[columns];
        colors = new Color[columns];
        labels = new String[columns];
        int  i;
        int  decade;

        limits[0] = 0;              //Ini log. scale
        for (decade=1,i=1; i < columns;) {
            decade *= 10;
            limits[i++] = 20*decade;
            if (i >= columns) break;
            limits[i++] = 50*decade;
            if (i >= columns) break;
            limits[i++] = 100*decade;
        }
        for (i=1; i < columns-1; i++) {
            labels[i] = Integer.toString(limits[i]) + " - " + Integer.toString(limits[i+1]);
        }
        labels[0] = " < 200 ms";
        labels[columns-1] = " > " + Integer.toString(limits[columns-1]) + " ms";
        for (i=0; i < columns; i++) {
            colors[i] = Color.gray;
            values[i] = 0;
            maxLabelWidth = Math.max(fm.stringWidth((String)(labels[i])),
                                     maxLabelWidth);
        }

        nimgs = Integer.valueOf(getParameter("nimgs")).intValue();
        imgs = new Image[nimgs];
        zeit = new Date();
        tracker = new MediaTracker(this);
        startzeit = zeit.getTime();
        System.out.println("Time: " + startzeit);
        for (i = 0; i < nimgs; i++) {
            imgs[i] = getImage(getCodeBase(), dir + "/hucke" + (i + 1) + ".gif");
            tracker.addImage(imgs[i],i);
            this.showStatus(dir + "/hucke" + (i + 1) + ".gif");
        }
        woher = getCodeBase();
        intro = getImage(getCodeBase(), dir + "/new.gif");
        tracker.addImage(intro,nimgs);

        setLayout(new FlowLayout(FlowLayout.CENTER,5,275));
        Panel p = new Panel();
        p.setLayout(new BorderLayout());
        add(p);
        p.add("Center", new Scrollbar(Scrollbar.HORIZONTAL,pause2/100,0,1,20));
        p.add("South", new Label("(Der Slider ver\u00e4ndert die Bildfolgegeschw.)"));
        Panel pb = new Panel();
        pb.add( new Button("Start"));
        pb.add( new Button("Stop"));
		p.add("North",pb);
    }   //Ende init()

    void pause(int time) {    //Pause (Zeit in ms)
        try Thread.sleep(time);
        catch (InterruptedException e);
    }

    public boolean action (Event evt, Object arg) {
        if (arg.equals("Start")) kicker.resume();
        if (arg.equals("Stop")) kicker.suspend();
        this.showStatus("Button: " + (String)arg);
        return true;
    }

    public boolean handleEvent(Event evt) {  //Scollbar Event
        if (evt.target instanceof Scrollbar) {
            pause2 = 100*((Scrollbar)evt.target).getValue();
            this.showStatus("Scrollbarvalue " + String.valueOf(pause2));
        }
        else super.handleEvent(evt); //handle all the rest
        return true;
    }

    public void run() {    //Run the Img-Loop. This method is called by class Thread
        if (nimgs < 1) {
            this.showStatus("No Images to display; quitting!");
            return;
        }
        try tracker.waitForID(nimgs); catch(InterruptedException e);  //Start Bild
        for (int i = 0; i < nimgs; i++) {
            this.showStatus("Loading image " + i + "from " + dir);
            try tracker.waitForID(i); catch(InterruptedException e);
            if(tracker.isErrorID(i)) {
                this.showStatus("Error loading image " + dir + "/hucke" + (i+1) + ".gif; quitting!");
                return;
            }
        }  //Ende loading images
        Date zeit1 = new Date();
        diffzeit = zeit1.getTime() - startzeit;
        System.out.println("Ladezeit: " + diffzeit + " ms");
        if (tracker.isErrorAny()) this.showStatus(" An Error occurred while loading the " + (nimgs+1) + " images");
        else this.showStatus("Ladezeit: " + diffzeit + " ms");
        if (local) {       //Hier werden die stat. Parameter gespeichert
           xferStats(diffzeit);
           try {
                RandomAccessFile outfile = new RandomAccessFile(zeitfile,"rw");
                synchronized(this) {
                    outfile.seek(outfile.length());  //goto EOF
                    outfile.writeLong(diffzeit);
                }
                outfile.close();
            } catch (IOException e) System.out.println("IO-Error with " + zeitfile);
        }
        tracker = null;   //Freigabe fuer GC

        paintStats = true;
        repaint();
//      kicker.suspend();  //Wait for startbutton to be pressed
        paintStats = false;

        while (size().width > 0 && size().height > 0 && kicker != null) {
            repaint();
            if (loopslot >= 0) pause(pause2 + ((loopslot == nimgs-1) ? 2*pause2 : 0));
            if (++loopslot >= nimgs) loopslot = 0;  //wrap around
        }   //end while
    }

    public void paint(Graphics g) {     //Paint the current frame
        update(g);
    }

    public void paintHistogramm(Graphics g) {
        int i, j;
        int cx, cy;
        int ypos = 200;


        g.clearRect(0,0,size().width,size().height);
        // draw the title centered at the bottom of the bar graph
        g.setColor(Color.black);
        g.setFont(titleFont);
        try g.drawString(" Local: " + lokalHost.getLocalHost(),5,ypos); catch (UnknownHostException e);
        ypos += 15;
        g.drawString("Die Perfomance ",5,ypos);
        g.setColor(Color.red);
        g.drawString("Ihrer Verbindung ",5 + fm.stringWidth("Die Performance"),ypos);
        ypos += 15;
        g.setColor(Color.black);
        g.drawString("ist heute besser als die von ",5,ypos);
        ypos += 15;
        g.drawString(akleiner + "% Ihrer " + nvalues + " Vorgaenger",5,ypos);
        for (i=0; i < columns; i++) {
            cy = ((barWidth + barSpacing) * i) + barSpacing;
            // set the X coordinate to be the size().width of the widest
            // label
            cx = maxLabelWidth + 5;

            // draw the labels and the shadow
            g.setColor(Color.black);
            g.drawString((String)labels[i], cx - maxLabelWidth - 1,
                         cy + fm.getAscent());
            if (colors[i] == Color.black) {
                g.setColor(Color.gray);
            }
            g.fillRect(cx + 3,
                       cy + 5,
                       (100*values[i]) / maxvalue,
                       barWidth);

            // draw the bar in the current color
            g.setColor((Color)(colors[i]));
            g.fillRect(cx,
                       cy,
                       (100*values[i]) / maxvalue,
                       barWidth);
            g.drawString("" + values[i],
                         cx + (100*values[i]) / maxvalue + 3,
                         cy + fm.getAscent());

        }   //End columns loop
    }   //End Histogramm

    public void update(Graphics g) {
        if ((imgs != null) && (loopslot >= 0) &&
            (loopslot < nimgs) && (imgs[loopslot] != null)) {
            if (im == null) {
                  im = createImage(size().width, size().height);
                  offscreen = im.getGraphics();
                  offscreen.setColor(Color.lightGray);
            }
            offscreen.fillRect(0, 0, size().width, size().height);
            offscreen.drawImage(imgs[loopslot], 0, 0, this);
            g.drawImage(im, 0, 0, this);
//          System.out.println("Durchlauf:" + loopslot);
            if (loopslot < 11) g.drawString("- Bild " + (loopslot+1) + " -",80,220);
            else                g.drawString("- Moral -",80,220);
            g.fillRect(2,260,18*loopslot,10);
            switch (loopslot) {
                 case 0:
                     g.drawString("Jetzt aber naht sich das Malheur",5,235);
                     g.drawString("denn dies Getr\u00e4nke ist Lik\u00f6r",5,250);
                     break;
                 case 1:
                     g.drawString("Es duftet s\u00fc\u00df. - Hans Huckebein",5,235);
                     g.drawString("Taucht seinen Schnabel froh hinein.",5,250);
                     break;
                 case 2:
                     g.drawString("Und l\u00e4\u00dft mit still vergn\u00fcgtem Sinnen",5,235);
                     g.drawString("Den ersten Schluck herunterrinnen",5,250);
                     break;
                 case 3:
                     g.drawString("Nicht \u00fcbel! Und er taucht schon wieder",5,235);
                     g.drawString("Den Schnabel in die Tiefe nieder.",5,250);
                     break;
                 case 4:
                     g.drawString("Er hebt das Glas und schl\u00fcrft den Rest,",5,235);
                     g.drawString("Weil er nicht gern was \u00fcbrig l\u00e4\u00dft",5,250);
                     break;
                 case 5:
                     g.drawString("Ei, ei! Ihm wird so wunderlich,",5,235);
                     g.drawString("So leicht und doch absunderlich",5,250);
                     break;
                 case 6:
                     g.drawString("Er kr\u00e4chzt mit freudigem Get\u00f6n",5,235);
                     g.drawString("und mu\u00df auf einem Beine stehn.",5,250);
                     break;
                 case 7:
                     g.drawString("Der Vogel, welcher sonsten fleucht,",5,235);
                     g.drawString("Wird hier zu einem Tier, was kreucht.",5,250);
                     break;
                 case 8:
                     g.drawString("Und \u00dcbermut kommt zum Beschlu\u00df,",5,235);
                     g.drawString("Der alles ruinieren mu\u00df.",5,250);
                     break;
                 case 9:
                     g.drawString("Er zerrt voll roher Lust und T\u00fccke",5,235);
                     g.drawString("Der Tante k\u00fcnstliches Gestricke.",5,250);
                     break;
                 case 10:
                     g.drawString("Der Tisch ist glatt - der B\u00f6se taumelt -",5,235);
                     g.drawString("Das Ende naht - sieh da! Er baumelt",5,250);
                     break;
                 case 11:
                     g.drawString("Die Bosheit war sein Hauptpl\u00e4sier",5,235);
                     g.drawString("drum, spricht die Tante, h\u00e4ngt er hier!",5,250);
                     break;
                 default:
                     break;
            }
        }  //Ende normal Update
        else if (!paintStats && (loopslot < 0) && (intro != null)) {  //Initial Update
            g.drawImage(intro, 10, 10, 150, 150, this);  //... without double buffering
        }
        if (paintStats && (nvalues > 0)) {
            paintHistogramm(g);
        }
        else if (paintStats) g.drawString(" No Xfer Statistics available!",5,220);
    }      //Ende Update

    public void start() {             //Start the applet by forking an animation thread.
        if (kicker == null) {
            kicker = new Thread(this);
            kicker.start();
        }
    }

    public void stop() {         //Stop the applet. The thread will exit because kicker is set to null.
         zeit = new Date();
//       kicker = null;
         diffzeit = zeit.getTime() - startzeit;
         System.out.println("Laufzeit: " + diffzeit + " ms");
    }
}     //Ende Class hucke
