// Plogis Laufschrift
//
// Folgende Befehle versteht die Laufschrift:
// (Die Befehle sind in <> eingeschlossen. Wird der Parameter weggelassen, so gilt
//  der Defaultwert)
// P n   : Pause n=Wartezeit in Syncs                        default = 100
// SP n  : Speed n=Scrollgeschwindigkeit in Pixel pro Sync   default = 1
// CX n  : Character X Offset n=Offset in Pixel              default = 0
// CY n  : Character Y Offset n=Offset in Pixel              default = 0
// VS n  : VSYNC n=Sync-Zeit in Millisekunden                default = 10
// FB n  : Fontbold n=0 oder 1                               default = 1
// FI n  : Fontitalic n=0 oder 1                             default = 1
// FN s  : Fontname s=Name der Zeichensatzes                 default = "Helvetica"
// FS n  : Fontsize n=Fontgröße                              default = 15
// X n   : X-Position n=Position des Ausgabefensters         default = 0
// Y n   : Y-Positiom n=Position des Ausgabefensters         default = 0
// WV n  : Wave n=Stärke des Ausschlags                      default = 0
// FC n  : Vordergrundfarbe n=rrrgggbbb (dezimal)            default = Browsereinstellung
// BC n  : Hintergrundfarbe n=rrrgggbbb (dezimal)            default = Browsereinstellung
// TX n  : ToX n=Position, die angefahren wird               default = 0
// TY n  : ToY n=Position, die angefahren wird               default = 0
// TM    : Time
// for english version contact plogi@yahoo.com
import java.awt.Font;
import java.awt.Graphics;
import java.awt.FontMetrics;
import java.awt.Color;
import java.awt.Image;
import java.awt.Event;

import java.applet.*;

import java.util.Date;

public class Lauf extends Applet implements Runnable {
  private Thread Steuerung;                                // Thread
  private int VSync = 10;                                  // Zeit für einen VSync
  private String Hilf = "Hallo";                           // Für debuggingzwecke
  private Image Buffer;                                    // Image für logischen Bildschirn
  private Graphics logBildschirm;                          // Logischer Bildschirm
  private boolean grInit = true;                           // Schalter zum Initialisieren des Bildschirms
  private int sx;                                          // X-Größe des Bildschirms
  private int sy;                                          // Y-Größe des Bildschirms
  private int syh;                                         // sy / 2
// -------------------------------------------------------- Für cmdScroll
  private String Fontname = "Helvetica";                   // Fontname
  private int fontisplain = 0;                             // Normaler Font
  private int fontisbold = 1;                              // Fetter Text
  private int fontisitalic = 0;                            // schraeger Text
  private int Fontgroesse = 15;                            // Punktgroesse des Fonts
  private Color Vordergrund;                               // Vordergrundfarbe
  private Color Hintergrund;                               // Hintergrundfarbe
  private Color dVordergrund;                              // default Vordergrundfarbe
  private Color dHintergrund;                              // default Hintergrundfarbe
  private String scrollChr;                                // nächstes zu scrollendes Zeichen
  private int scrollSpeed = 1;                             // Scrollgeschwindigkeit
  private int scrollCount = 0;                             // Zähler
  private int ChrXOff = 0;                                 // X-Offset für Chr
  private int ChrYOff = 0;                                 // Y-Offset für Chr
  private int r;                                           // r
  private int g;                                           // g
  private int b;                                           // b
  private Date Datum;                                      // zum Holen der Uhrzeit
// -------------------------------------------------------- Für lauf
  private int x = 0;                                       // x Ausgabeposition logBildschirm
  private int y = 0;                                       // y Ausgabeposition logBildschirm
  private int mainCommand = 0;                             // Aktuelles Kommando
    // 0 : Scroll
    // 1 : Pause zeit
  private int mainParm1 = 0;                               // Parameter für Kommando
  private int exCommand = 0;                               // Aktuelles Interrupt Kommando
    // 0 : kein Kommando
    // 1 : Wave radius
    // 2 : ToX x
    // 3 : ToY y
  private int exParm1 = 0;                                 // Parameter für Interrupt Kommando
  private int exParm2 = 0;                                 // Parameter für Interrupt Kommando
  private int exParm3 = 0;                                 // Parameter für Interrupt Kommando
// -------------------------------------------------------- Für parseText
  private String text;                                     // das ist der zu scrollende Text
  private String textbuffer;                               // Sicherung, um nochmal anfangen zu können
  private boolean getnextChr = true;                       // Schalter, daß das nächste Zeichen oder
                                                           // der nächste Befehl geholt werden soll
// -------------------------------------------------------- Für parseScrollCmd
  private String ScrollCmd;                                // hier wird der aktuelle Befehl "hingeparsed"
  private String ScrollCmdParm;                            // Parameter für Befehl
  private int ScrollCmdParmNum;                            // Parameter für Befehl als Zahl
  private int ScrollCmdRc;                                 // Returncode für ScrollCommandos
  private boolean zweiteKlammer;                           // Schalter für > in einem Befehl
  private boolean weitereKlammern;                         // Schalter für weitere < in
  private int ki;                                          // position der 2. >
  private int pai;                                         // trennzeichen zu parameter
// -------------------------------------------------------- Für numKonv
  private boolean isNum;
  private boolean isNeg;

  public void start() {
    if (Steuerung == null) {
      Steuerung = new Thread (this);
      Steuerung.start();
    } // endif
  }

  public void stop() {
    if (Steuerung != null) {
      Steuerung.stop();
      Steuerung = null;
    } // endif
  }

  public void run () {
    while (true) {
      repaint();
      if (VSync > 0) {
        try {
          Steuerung.sleep(VSync);
        }catch (Exception e) {
        } // endtry
      } // endif
    } // endwhile
  }

  public void init() {
    super.init();                                          // init von parent
    sx = size().width;                                     // Breite des Bildschirms
    sy = size().height;                                    // Höhe des Bildschirms
    syh = sy / 2;
    try {
      text = new String (getParameter("Scrolltext"));
    }catch (NullPointerException except) {
      text = new String("Laufschrift programmiert von                  Klaus-Peter Plog                    <tx -50><p><tx 0><p>");
    } // endtry
    textbuffer = new String (text);                        // Kopie vom Text anlegen
    Buffer = createImage(sx,sy);                           // Logischen Bildschirm erzeugen
    Vordergrund = new Color(getForeground().getRGB());     // Vordergrund und Hintergrundfarbe
    Hintergrund = new Color(getBackground().getRGB());     // setzen
    dVordergrund = new Color(Vordergrund.getRGB());        // default Vordergrund und Hintergrundfarbe
    dHintergrund = new Color(Hintergrund.getRGB());        // setzen
    logBildschirm = Buffer.getGraphics();
    logBildschirm.setColor(Hintergrund);                   // Logischen Bildschirm leermachen
    logBildschirm.fillRect(0,0,sx,sy);
    logBildschirm.setColor(Vordergrund);
    Fontsetzen(logBildschirm);
  		//{{INIT_CONTROLS
		setLayout(null);
		addNotify();
		resize(500,30);
		//}}
}

  public boolean handleEvent(Event event) {
    return super.handleEvent(event);
  }

  public void update(Graphics Bildschirm) {                // update überladen um das Bildschirm-
    paint(Bildschirm);                                     // löschen zu verhindern
  }

  public void paint(Graphics Bildschirm) {
    if (grInit) {                                          // Beim 1. Mal Bildschirm init
/*      Bildschirm.setColor(Hintergrund);
      Bildschirm.fillRect(0,0,sx,sy);
      Bildschirm.setColor(Vordergrund);*/
      grInit = false;
    } //endif
    lauf(Bildschirm);
    // repaint();
  }

  private void Fontsetzen(Graphics Bildschirm) {
    Bildschirm.setFont(new Font(Fontname,Font.PLAIN * fontisplain
      + Font.BOLD * fontisbold + Font.ITALIC * fontisitalic,Fontgroesse));
  }

  private void lauf(Graphics Bildschirm) {
    parseText(logBildschirm);                                 // Text parsen
// ---------------------------------------------------------- Kommandobearbeitung
    if (mainCommand == 0) {
      cmdScroll(logBildschirm);
    }else {
      cmdPause();
    } // endif
// -------------------------------------------------------- Exceptionbearbeitung
    switch (exCommand) {
      case 0:
        break;
      case 1:
        excmdWave();
        break;
      case 2:
        excmdToX();
        break;
      case 3:
        excmdToY();
    } // endswitch
// -------------------------------------------------------- Ende der Verarbeitung
    if (x < 0) {
      Bildschirm.setColor(Hintergrund);
      Bildschirm.fillRect(sx + x,0,sx,sy);
    }else {
      if (x > 0) {
        Bildschirm.setColor(Hintergrund);
        Bildschirm.fillRect(0,0,x,sy);
      } // endif
    } // endif
    if (y < 0) {
      Bildschirm.setColor(Hintergrund);
      Bildschirm.fillRect(0,sy + y,sx,sy);
    }else {
      if (y > 0) {
        Bildschirm.setColor(Hintergrund);
        Bildschirm.fillRect(0,0,sx,y);
      } // endif
    } // endif
    Bildschirm.drawImage(Buffer,x,y,this);
  }

  private void excmdWave() {
    y = y + exParm3 * scrollSpeed;
    if (y < -exParm1 || y > exParm1) {
      exParm3 = -exParm3;
    } // endif
  }

  private void excmdToX() {
    if (x > exParm1) {
      x--;
    }else {
      if (x < exParm1) {
        x++;
      }else {
        exCommand = 0;
      } // endif
    } // endif
  }

  private void excmdToY() {
    if (y > exParm1) {
      y--;
    }else {
      if (y < exParm1) {
        y++;
      }else {
        exCommand = 0;
      } // endif
    } // endif
  }

  private void cmdScroll(Graphics Bildschirm) {
    scrollCount = scrollCount + scrollSpeed;
    Bildschirm.copyArea(0,0,sx,sy,-scrollSpeed,0);
    Bildschirm.setColor(Hintergrund);
    Bildschirm.fillRect(sx - scrollCount,0,sx,sy);
    Bildschirm.setColor(Vordergrund);
    Bildschirm.drawString(scrollChr,sx - scrollCount + ChrXOff,syh + Bildschirm.getFontMetrics().getHeight() / 2 + ChrYOff);
    if (scrollCount > Bildschirm.getFontMetrics().stringWidth(scrollChr)) {
      getnextChr = true;
    } // endif
  }

  private void cmdPause() {
    mainParm1--;
    if (mainParm1 < 0) {
      mainCommand = 0;
      getnextChr = true;
    } // endif
  }

  private void parseText(Graphics Bildschirm) {
    while (getnextChr) {
      if (text.length() == 0) {
        text = new String(textbuffer);
      } // endif
      if (text.substring(0,1).equals("<")){
        parseScrollCmd(Bildschirm);
      }else {
        parsenextChr();
      } // endif
    } // endwhile
  }

  private void parsenextChr() {
    getnextChr = false;
    scrollChr = text.substring(0,1);
    text = text.substring(1,text.length());
    scrollCount = 0;
  }

  private void parseScrollCmd(Graphics Bildschirm) {
// ---------------------------------------------------- Suchen nach Klammerpaaren
    zweiteKlammer = weitereKlammern = false;
    ki = text.indexOf(">");
    if (ki != -1) {
      zweiteKlammer = true;
      if (text.substring(1,ki).indexOf("<") != -1) {
        weitereKlammern = true;
      } // endif
    } // endif
    if (!zweiteKlammer || weitereKlammern) {          // Im Fehlerfall Befehl als Text
      parsenextChr();                                 // behandeln
      return;
    } // endif
// ----------------------------------------------------- Heraustrennen des Kommandos
    ScrollCmd = text.substring(1,ki).trim();
    pai = ScrollCmd.indexOf(" ");
    if (pai == -1) {                                   // keine Parameter
      ScrollCmdParm = null;
    }else {
      ScrollCmdParm = ScrollCmd.substring(pai + 1,ScrollCmd.length()).trim();
      ScrollCmd = ScrollCmd.substring(0,pai).trim();
    } // endif
    ScrollCmd = ScrollCmd.toUpperCase();
// ----------------------------------------------------- Verarbeiten des Kommandos
    ScrollCmdRc = 0;
    wh2: while (true) {
      wh1: while (true) {
        if (ScrollCmd.equals("P")) {                      // Wait
          ScrollCmdPause();
          break wh1;
        } // endif
        if (ScrollCmd.equals("SP")) {                     // Speed
          ScrollCmdSysVar(0);
          break wh1;
        } // endif
        if (ScrollCmd.equals("VS")) {                     // VSYNC-Zeit
          ScrollCmdSysVar(1);
          break wh1;
        } // endif
        if (ScrollCmd.equals("CX")) {                     // Character X Offset
          ScrollCmdChrXYOffset(0);
          break wh1;
        } // endif
        if (ScrollCmd.equals("CY")) {                     // Character Y Offset
          ScrollCmdChrXYOffset(1);
          break wh1;
        } // endif
        if (ScrollCmd.equals("FB")) {                     // Fontbold
          ScrollCmdFontStyle(Bildschirm, 0);
          break wh1;
        } // endif
        if (ScrollCmd.equals("FI")) {                     // Fontitalic
          ScrollCmdFontStyle(Bildschirm, 1);
          break wh1;
        } // endif
        if (ScrollCmd.equals("FN")) {                     // Fontname
          ScrollCmdFontname(Bildschirm);
          break wh1;
        } // endif
        if (ScrollCmd.equals("FS")) {                     // Fontsize
          ScrollCmdFontsize(Bildschirm);
          break wh1;
        } // endif
        if (ScrollCmd.equals("X")) {                      // X-Position der Laufschrift
          ScrollCmdPosXY(0);
          break wh1;
        } // endif
        if (ScrollCmd.equals("Y")) {                      // Y-Position der Laufschrift
          ScrollCmdPosXY(1);
          break wh1;
        } // endif
        if (ScrollCmd.equals("WV")) {                     // Wave-Bewegung der Laufschrift
          ScrollCmdWave();
          break wh1;
        } // endif
        if (ScrollCmd.equals("TX")) {                     // Bewegung der Laufschrift zu X
          ScrollCmdTXY(0);
          break wh1;
        } // endif
        if (ScrollCmd.equals("TY")) {                     // Bewegung der Laufschrift zu Y
          ScrollCmdTXY(1);
          break wh1;
        } // endif
        if (ScrollCmd.equals("FC")) {                     // Foregroundcolor
          ScrollCmdFBColor(0);
          break wh1;
        } // endif
        if (ScrollCmd.equals("BC")) {                     // Backgroundcolor
          ScrollCmdFBColor(1);
          break wh1;
        } // endif
        if (ScrollCmd.equals("TM")) {                     // Time
          ScrollCmdTime();
          break wh1;
        } // endif
//------------------------------------------------------- none
        ScrollCmdRc = -1;
        break wh2;
      } // endwhile wh1
//------------------------------------------------------- any value
      break wh2;
    } // endwhile wh2
//
    if (ScrollCmdRc == 0) {                           // Verarbeitung ok
      text = text.substring(ki + 1,text.length());    // Kommando heraustrennen
    }else {                                           // Fehler (wahrscheinlich Parameterfehler)
      parsenextChr();                                 // Eingaben als Text
      return;
    } // endif
  }
//------------------------------------------------------ Scrollkommandos
  private void ScrollCmdPause() {
    if (ScrollCmdParm == null) {
      mainParm1 = 100;
    }else {
      numKonv();
      if (!isNum || isNeg) {
        ScrollCmdRc = -1;
        return;
      }else {
        mainParm1 = ScrollCmdParmNum;
      } // endif
    } // endif
    mainCommand = 1;
    getnextChr = false;
  }

  private void ScrollCmdWave() {
    if (ScrollCmdParm == null) {
      exCommand = exParm1 = exParm2 = exParm3 = 0;
    }else {
      numKonv();
      if (!isNum || isNeg) {
        ScrollCmdRc = -1;
        return;
      }else {
        exParm1 = ScrollCmdParmNum;
        exParm2 = 0;
        exCommand = exParm3 = 1;
      } // endif
    } // endif
  }
  private void ScrollCmdTXY(int c) {
    if (ScrollCmdParm == null) {
      exCommand = exParm1 = exParm2 = exParm3 = 0;
    }else {
      numKonv();
      if (!isNum) {
        ScrollCmdRc = -1;
        return;
      }else {
        exParm1 = ScrollCmdParmNum;
        if (c == 0) {
          exCommand = 2;
        }else {
          exCommand = 3;
        } // endif
      } // endif
    } // endif
  }

  private void ScrollCmdSysVar(int c) {
    if (ScrollCmdParm == null) {
      if (c == 0) {
        scrollSpeed = 1;
      }else {
        VSync = 10;
      } // endif
    }else {
      numKonv();
      if (!isNum || isNeg) {
        ScrollCmdRc = -1;
        return;
      }else {
        if (c == 0) {
          scrollSpeed = ScrollCmdParmNum;
        }else {
          VSync = ScrollCmdParmNum;
        } // endif
      } // endif
    } // endif
  }

  private void ScrollCmdChrXYOffset(int c) {
    if (ScrollCmdParm == null) {
      if (c == 0) {
        ChrXOff = 0;
      }else {
        ChrYOff = 0;
      } // endif
    }else {
      numKonv();
      if (!isNum) {
        ScrollCmdRc = -1;
        return;
      }else {
        if (c == 0) {
          ChrXOff = ScrollCmdParmNum;
        }else {
          ChrYOff = ScrollCmdParmNum;
        } // endif
      } // endif
    } // endif
  }

  private void ScrollCmdFontStyle(Graphics Bildschirm, int c) {
    if (ScrollCmdParm == null) {
      if (c==0) {
        fontisbold = 1;
      }else {
        fontisitalic = 1;
      } // endif
      Fontsetzen(Bildschirm);
    }else {
      numKonv();
      if (!isNum || isNeg) {
        ScrollCmdRc = -1;
        return;
      }else {
        if (ScrollCmdParmNum > 0) {
          ScrollCmdParmNum = 1;
        } // endif
        if (c==0) {
          fontisbold = ScrollCmdParmNum;
        }else {
          fontisitalic = ScrollCmdParmNum;
        } // endif
        Fontsetzen(Bildschirm);
      } // endif
    } // endif
  }

  private void ScrollCmdFontsize(Graphics Bildschirm) {
    if (ScrollCmdParm == null) {
      Fontgroesse = 15;
      Fontsetzen(Bildschirm);
    }else {
      numKonv();
      if (!isNum || isNeg) {
        ScrollCmdRc = -1;
        return;
      }else {
        Fontgroesse = ScrollCmdParmNum;
        Fontsetzen(Bildschirm);
      } // endif
    } // endif
  }

  private void ScrollCmdFontname(Graphics Bildschirm) {
    if (ScrollCmdParm == null) {
      Fontname = "Helvetica";
      Fontsetzen(Bildschirm);
    }else {
      Fontname = new String(ScrollCmdParm);
      Fontsetzen(Bildschirm);
    } // endif
  }

  private void ScrollCmdPosXY(int c) {
    if (ScrollCmdParm == null) {
      if (c == 0) {
        x = 0;
      }else {
        y = 0;
      } // endif
    }else {
      numKonv();
      if (!isNum) {
        ScrollCmdRc = -1;
        return;
      }else {
        if (c == 0) {
          x = ScrollCmdParmNum;
        }else {
          y = ScrollCmdParmNum;
        } // endif
      } // endif
    } // endif
  }

  private void ScrollCmdFBColor(int c) {
    if (ScrollCmdParm == null) {
      if (c == 0) {
        Vordergrund = new Color(dVordergrund.getRGB());
      }else {
        Hintergrund = new Color(dHintergrund.getRGB());
      } // endif
    }else {
      numKonv();
      if (!isNum || ScrollCmdParm.length() != 9) {
        ScrollCmdRc = -1;
        return;
      }else {
        r = Integer.parseInt(ScrollCmdParm.substring(0,3));
        g = Integer.parseInt(ScrollCmdParm.substring(3,6));
        b = Integer.parseInt(ScrollCmdParm.substring(6,9));
        if (c == 0) {
          Vordergrund = new Color(r,g,b);
        }else {
          Hintergrund = new Color(r,g,b);
        } // endif
      } // endif
    } // endif
  }

  private void ScrollCmdTime() {
    Datum = new Date();
    text = text.substring(0,ki + 1) + Datum.getHours() + ":" +
      Datum.getMinutes() + ":" + Datum.getSeconds() +
      text.substring(ki + 1,text.length());
  }

//---------------------------------------------------------------------------------
  private void numKonv() {
    isNum = isNeg = false;
    ScrollCmdParmNum = 0;
    try {
      ScrollCmdParmNum = Integer.parseInt(ScrollCmdParm);
    }catch (NumberFormatException except) {
      return;
    } // endtry
    isNum = true;
    if (ScrollCmdParmNum < 0) {
      isNeg = true;
    } // endif
  }

	//{{DECLARE_CONTROLS
	//}}
}
