import java.awt.Font;
import java.awt.Graphics;
import java.awt.Color;
import java.awt.Image;

public class Banner extends java.applet.Applet implements Runnable
{
  int       xstart, ystart, NumChars, currStart, currEnd, currParam = -1,
            maxParams = 0, Godot;
  Color     cBack, cOn, cOff;
  final     int CELLWIDTH = 17;
  Thread    mainThread = null;
  String    params[];
  Image     charImage[];

  char cell[][] =
    { /* */ {0x00,0x00,0x00,0x00,0x00},  /*!*/ {0x00,0x00,0x5F,0x00,0x00}, 
      /*"*/ {0x00,0x03,0x00,0x03,0x00},  /*#*/ {0x14,0x7F,0x14,0x7F,0x14}, 
      /*$*/ {0x24,0x2A,0x7F,0x2A,0x12},  /*%*/ {0x63,0x13,0x08,0x64,0x63}, 
      /*&*/ {0x30,0x4A,0x4D,0x32,0x48},  /*'*/ {0x00,0x04,0x03,0x00,0x00}, 
      /*(*/ {0x00,0x1C,0x22,0x41,0x00},  /*)*/ {0x00,0x41,0x22,0x1C,0x00},
      /***/ {0x22,0x14,0x7F,0x14,0x22},  /*+*/ {0x08,0x08,0x3E,0x08,0x08}, 
      /*,*/ {0x00,0x80,0x60,0x00,0x00},  /*-*/ {0x08,0x08,0x08,0x08,0x08}, 
      /*.*/ {0x00,0x00,0x40,0x00,0x00},  /*/*/ {0x60,0x10,0x08,0x04,0x03}, 
      /*0*/ {0x3E,0x51,0x49,0x45,0x3E},  /*1*/ {0x00,0x42,0x7F,0x40,0x00}, 
      /*2*/ {0x42,0x61,0x51,0x49,0x46},  /*3*/ {0x22,0x49,0x49,0x49,0x36}, 
      /*4*/ {0x08,0x0C,0x0A,0x7F,0x08},  /*5*/ {0x27,0x45,0x45,0x45,0x39}, 
      /*6*/ {0x3E,0x49,0x49,0x49,0x32},  /*7*/ {0x03,0x01,0x79,0x05,0x03}, 
      /*8*/ {0x36,0x49,0x49,0x49,0x36},  /*9*/ {0x26,0x49,0x49,0x49,0x3E}, 
      /*:*/ {0x00,0x00,0x14,0x00,0x00},  /*;*/ {0x00,0x40,0x34,0x00,0x00}, 
      /*<*/ {0x08,0x14,0x22,0x41,0x00},  /*=*/ {0x14,0x14,0x14,0x14,0x14}, 
      /*>*/ {0x00,0x41,0x22,0x14,0x08},  /*?*/ {0x02,0x01,0x51,0x09,0x06}, 
      /*@*/ {0x3E,0x41,0x5D,0x5D,0x5E},  /*A*/ {0x7C,0x12,0x11,0x12,0x7C}, 
      /*B*/ {0x7F,0x49,0x49,0x49,0x36},  /*C*/ {0x3E,0x41,0x41,0x41,0x22}, 
      /*D*/ {0x7F,0x41,0x41,0x41,0x3E},  /*E*/ {0x7F,0x49,0x49,0x49,0x41}, 
      /*F*/ {0x7F,0x09,0x09,0x09,0x01},  /*G*/ {0x3E,0x41,0x49,0x49,0x3A}, 
      /*H*/ {0x7F,0x08,0x08,0x08,0x7F},  /*I*/ {0x00,0x41,0x7F,0x41,0x00}, 
      /*J*/ {0x30,0x40,0x41,0x41,0x3F},  /*K*/ {0x7F,0x08,0x14,0x22,0x41}, 
      /*L*/ {0x7F,0x40,0x40,0x40,0x40},  /*M*/ {0x7F,0x02,0x0C,0x02,0x7F}, 
      /*N*/ {0x7F,0x02,0x04,0x08,0x7F},  /*O*/ {0x3E,0x41,0x41,0x41,0x3E}, 
      /*P*/ {0x7F,0x09,0x09,0x09,0x06},  /*Q*/ {0x3E,0x41,0x51,0x61,0x7E}, 
      /*R*/ {0x7F,0x09,0x19,0x29,0x46},  /*S*/ {0x26,0x49,0x49,0x49,0x32}, 
      /*T*/ {0x01,0x01,0x7F,0x01,0x01},  /*U*/ {0x3F,0x40,0x40,0x40,0x3F}, 
      /*V*/ {0x1F,0x20,0x40,0x20,0x1F},  /*W*/ {0x7F,0x20,0x18,0x20,0x7F}, 
      /*X*/ {0x63,0x14,0x08,0x14,0x63},  /*Y*/ {0x03,0x04,0x78,0x04,0x03}, 
      /*Z*/ {0x61,0x51,0x49,0x45,0x43},  /*[*/ {0x00,0x7F,0x41,0x41,0x00}, 
      /*\*/ {0x03,0x04,0x08,0x10,0x60},  /*]*/ {0x00,0x41,0x41,0x7F,0x00}, 
      /*^*/ {0x04,0x02,0x01,0x02,0x04},  /*_*/ {0x80,0x80,0x80,0x80,0x80}, 
      /*`*/ {0x00,0x00,0x03,0x04,0x00},  /*a*/ {0x20,0x54,0x54,0x54,0x78}, 
      /*b*/ {0x7F,0x44,0x44,0x44,0x38},  /*c*/ {0x38,0x44,0x44,0x44,0x44}, 
      /*d*/ {0x38,0x44,0x44,0x44,0x7F},  /*e*/ {0x38,0x54,0x54,0x54,0x58}, 
      /*f*/ {0x08,0x7E,0x09,0x09,0x02},  /*g*/ {0x18,0xA4,0xA4,0xA4,0x7C}, 
      /*h*/ {0x7F,0x04,0x04,0x04,0x78},  /*i*/ {0x00,0x44,0x7D,0x40,0x00}, 
      /*j*/ {0x80,0x84,0x7D,0x00,0x00},  /*k*/ {0x7F,0x10,0x28,0x44,0x00}, 
      /*l*/ {0x00,0x41,0x7F,0x40,0x00},  /*m*/ {0x7C,0x04,0x78,0x04,0x78}, 
      /*n*/ {0x7C,0x08,0x04,0x04,0x78},  /*o*/ {0x38,0x44,0x44,0x44,0x38}, 
      /*p*/ {0xFC,0x24,0x24,0x24,0x18},  /*q*/ {0x18,0x24,0x24,0x24,0xFC}, 
      /*r*/ {0x7C,0x08,0x04,0x04,0x08},  /*s*/ {0x48,0x54,0x54,0x54,0x24}, 
      /*t*/ {0x04,0x3F,0x44,0x44,0x24},  /*u*/ {0x3C,0x40,0x40,0x20,0x7C}, 
      /*v*/ {0x1C,0x20,0x40,0x20,0x1C},  /*w*/ {0x3C,0x40,0x38,0x40,0x3C}, 
      /*x*/ {0x44,0x28,0x10,0x28,0x44},  /*y*/ {0x1C,0xA0,0xA0,0xA0,0x7C}, 
      /*z*/ {0x44,0x64,0x54,0x4C,0x44},  /*{*/ {0x08,0x36,0x41,0x41,0x00}, 
      /*|*/ {0x00,0x00,0x77,0x00,0x00},  /*}*/ {0x00,0x41,0x41,0x36,0x08}, 
      /*~*/ {0x02,0x01,0x03,0x02,0x01},  /* */ {0x55,0xAA,0x55,0xAA,0x55}};

  byte hexDigit( char c )
  {
    if ((c >= '0') && (c <= '9')) return (byte) (c - 48);
    if ((c >= 'A') && (c <= 'F')) return (byte) (c - 55);
    if ((c >= 'a') && (c <= 'f')) return (byte) (c - 87);
    return 0;
  }

  Color getColor( String param, String deflt )
  {
    int r, g, b, j;
    long l = 0;
    byte byt;
    char c;
    boolean ok = true;

    String s = getParameter( param );
    if (s == null) s = deflt;

    j = s.length();
    for (int i = 0; i < j; i++)
    {
      c = s.charAt( i );
      if (!(((c >= '0') && (c <= '9')) || ((c >= 'A') && (c <= 'F')) ||
          ((c >= 'a') && (c <= 'a'))))
      {
        s = deflt;
        i = j;
      }
    }

    j = s.length();
    for (int i = 0; i < j; i++)
    {
      byt = hexDigit( s.charAt( i ));
      l <<= 4;
      l |= byt;
    }
    r = (int) ((l & 0x00FF0000) >>> 16);
    g = (int) ((l & 0x0000FF00) >>> 8);
    b = (int) ((l & 0x000000FF));
    return new Color( r, g, b );
  } 

  void getParameters()
  {
    int i = 1, count = 0;
    boolean ok = true;
    String p, s;

    do
    {
      p = "data" + i++;
      s = getParameter( p );
      if (s != null) count++;
    } while (s != null);

    if (count == 0)
    {
      params = new String[1];
      params[0] = new String( "This space for rent." );
      maxParams = 1;
    }
    else
    {
      i = 0;
      params = new String[ count ];
      maxParams = count;

      while (i < count)
      {
        p = "data" + (i+1);
        params[i++] = getParameter( p );
      }
    }

    Godot = 0;
    s = getParameter( "Delay" );
    if (s != null)
    {
      Integer _i = null;
      try
      {
         _i = Integer.valueOf( s );
         Godot = _i.intValue();
      }
      catch ( NumberFormatException e ) {}
    }
    if (Godot <= 0) Godot = 300;

    cBack = getColor( "bgColor", "000000" );
    cOff  = getColor( "offColor", "005C5C" );
    cOn   = getColor( "onColor", "00FFFF" );
  }

  Image createCharImage( char c )
  {
    Image im = createImage( CELLWIDTH, 24 );
    Graphics g = im.getGraphics();
    int x = 0, y = 0;
    char c2;
    Color curr = cOn;
    Color newCol;

    g.setColor( cBack );
    g.fillRect( 0, 0, CELLWIDTH, 24 );
    g.setColor( cOn );

    for (int i = 0; i < 5; i++)
    {
      y = 0;
      c2 = cell[c-32][i];
      for (int j = 0; j < 8; j++)
      {
        newCol = (((c2 & 1) == 0) ? cOff : cOn);  // These take up space, but I think
        if (curr != newCol)                       // it'll be quicker than explicitly
        {                                         // setting the colour each time the
          g.setColor( newCol );                   // routine draws a dot - especially
          curr = newCol;                          // when there are runs of identical
        }                                         // "pixels".
        g.fillRect( x, y, 2, 2 );
        y += 3;
        c2 >>= 1;
      }
      x += 3;
    }
    return im;
  }

  void getDimensions()
  {
    int height = this.size().height, width = this.size().width;
 
    NumChars = (width  - (width % CELLWIDTH)) / CELLWIDTH;
    xstart   = (width  - (NumChars * CELLWIDTH)) >> 1;
    ystart   = (height - 24) >> 1;
  }

  void drawLetter( Graphics g, char c, int n )
  {
    int x = (n * CELLWIDTH) + xstart, y;

    if (charImage[ c-32 ] == null)
      charImage[ c-32 ] = createCharImage( c );

    g.drawImage( charImage[ c-32 ], x, ystart, this );
  }

  void drawBanner( Graphics g, String s, int pos, int len )
  {
    int i = 0, j = NumChars - len, k = 0, m;
    int sl = s.length();
    int x = 0;

    if (NumChars > len)
    {
      for (x = 0; x < j; x++)
        drawLetter( g, ' ', x );
      j = len;
      if ((j + x) > NumChars) j = NumChars - x;
    }
    else
    {
      j = len;
      if (j > NumChars) j = NumChars;
    }

    for (i = 0; i < j; i++)
    {
      m = pos + i;
      if (m < sl)
        drawLetter( g, s.charAt( m ), x++);
      else
        drawLetter( g, ' ', x++ );
    }
    while (x < NumChars)
      drawLetter( g, ' ', x++ );
  }

  public String getAppletInfo()
  {
    return "LED Banner v1.0 (20 Nov 1996), by Jeff Lee.";
  }

  public void paint( Graphics g )
  {
    if (currParam != -1)
      drawBanner( g, params[ currParam ], currStart, currEnd-currStart );
    else
      drawBanner( g, "", 0, 0 );
  }

  public void init()
  {
    getParameters();
    getDimensions();
    setBackground( cBack );
    charImage = new Image[96];
    for (int i = 0; i < 96; i++) charImage[i] = null;
  }

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

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

  public void run()
  {
    int strlen;

    while (true)
    {
      if ((currParam >= maxParams) || (currParam < 0)) currParam = 0;
      currStart = currEnd = 0;
      strlen = params[ currParam ].length();

      while (currStart < strlen)
      {
        drawBanner( getGraphics(), params[ currParam ], currStart,
                    currEnd-currStart );
        currEnd++;
        if ((currEnd-currStart) > NumChars) currStart++;
        try { Thread.sleep( Godot ); }
        catch ( InterruptedException e ) {}
      }

      currParam++;
    }
  }
}
