import java.awt.Color;
import java.awt.Graphics;
import java.awt.Image;

///////////////////////////////////////////////////////////////////////////////
//
// Lines - Draws a series of qix-like lines across the screen.
//
///////////////////////////////////////////////////////////////////////////////
//
// Input Parameters:
//
// NumLines - An integer [1,200], indicating the number of lines to have
//            scrolling all over the screen.
//
// HEIGHT   - The height of the canvas.
//
// WIDTH    - The width of the canvas.
//
// Author: Patrick Martin (patrick@ucs.att.com)
//
// Author's Sidenote:
//
// Do whatever you want with this code.  Let me know about any bugs or if
// you add anything nifty to it.  It was basically a learning exercise for
// me anyhow.  Oh yeah, do everyone a favor and voice your opinions on the
// Internet Decency Act, it's a really scary piece of legislation.
//
///////////////////////////////////////////////////////////////////////////////

public class Lines extends java.applet.Applet implements Runnable
{
  int XBound, // The largest possible X coordinate.
      YBound; // The largest possible Y coordinate.

  // The number of lines on the screen.
  int NumLines = 200;

  // The structure holding all the line information.
  Line Lines[] = new Line[NumLines];

  // The thread of execution.
  Thread LineThread;

  // The off screen image for double-buffering.
  Image OffScreenImage;

  // The color of the lines.
  Color C;

  // The off screen graphics structure.
  Graphics OffScreenG;

  public void init()
  {
    // Could make this stuff configurable, but well, I'm lazy.
    int VecX1,  // Initial momentum in the X-Direction of Point 1
        VecY1,  // Initial momentum in the Y-Direction of Point 1
        VecX2,  // Initial momentum in the X-Direction of Point 2
        VecY2,  // Initial momentum in the Y-Direction of Point 2
        IX1,    // The initial X1 coordinate.
        IY1,    // The initial Y1 coordinate.
        IX2,    // The initial X2 coordinate.
        IY2;    // The initial Y2 coordinate.

    // Get the NumLines parameter from the calling html file.
    if (getParameter("NumLines") == null)
      this.NumLines = 50;
    else
    {
      // Set NumLines to 50 if we get a format error.
      try {this.NumLines = (new Integer(getParameter("NumLines"))).intValue();}
      catch (NumberFormatException e) {	this.NumLines = 50; }

      // Throw out bogus information.
      if (NumLines > 200 || NumLines < 1) NumLines = 50;
    }

    // Background is black.
    setBackground(Color.black);

    // Get our boundaries.
    XBound = getWidth();
    YBound = getHeight();

    // Create the off screen image and graphics structure.
    OffScreenImage = createImage(XBound, YBound);
    OffScreenG = OffScreenImage.getGraphics();

    // Starting points.
    IX1 = IY1 = 0;
    IX2 = IY2 = XBound / 2;

    // Initial vectors, throw some randomness in here.
    VecX1 = (int) (Math.random() * 10) + 1;
    VecX2 = (int) (Math.random() * 10) + 1;
    VecY1 = (int) (Math.random() * 10) + 1;
    VecY2 = (int) (Math.random() * 10) + 1;

    for(int i=0;i < NumLines; i++)
    {
      // Create this line.
      Lines[i] = new Line(IX1, IY1, IX2, IY2);

      // Tell the line what boundaries it has to work with.
      Lines[i].SetBoundaries(XBound, YBound);

      // Drift off a bit from where the last line was.
      IX1 += VecX1;
      // Make sure we drifted into valid coordinates.
      if (IX1 > XBound || IX1 < 0)
      {
	IX1 -= VecX1;
	VecX1 *= -1;
	IX1 += VecX1;
      }

      // Likewise for the rest of the points.
      IY1 += VecY1;
      if (IY1 > YBound || IY1 < 0)
      {
	IY1 -= VecY1;
	VecY1 *= -1;
	IY1 += VecY1;
      }

      IX2 += VecX2;
      if (IX2 > XBound || IX2 < 0)
      {
	IX2 -= VecX2;
	VecX2 *= -1;
	IX2 += VecX2;
      }

      IY2 += VecY2;
      if (IY2 > YBound || IY2 < 0)
      {
	IY2 -= VecY2;
	VecY2 *= -1;
	IY2 += VecY2;
      }

      // Give the line a push, start it moving.
      Lines[i].Push(VecX1, VecY1, VecX2, VecY2);
    }
  }

  public void start()
  {
    // Boiler-plate thread stuff.
    if (LineThread == null)
    {
      LineThread = new Thread(this);
      LineThread.start();
    }
  }

  public void stop()
  {
    // More boiler-plate thread stuff.
    if (LineThread != null)
    {
      LineThread.stop();
      LineThread = null;
    }
  }

  public void run()
  {
    int i,     // The loop control variable.
        Red,   // The level of red.
        Green, // The level of green.
        Blue;  // The level of blue.

    while (true)
    {
      // Cycle through the reds.
      for (Red = 255; Red > 128; Red--)
      {
	// Cycle through the greens.
	for (Green = 255; Green > 128; Green--)
	{
	  // Cycle through the blues.
	  for (Blue = 255; Blue > 128; Blue--)
	  {
	    // Create a new color each time.
	    C = new Color(Red, Green, Blue);

	    // Allow each line to drift from it's previous location.
	    for(i=0; i<NumLines; i++)
	      Lines[i].Drift();

	    // Put a pause between each repaint of the screen.
	    try { Thread.sleep(25); }
	    catch (InterruptedException e) {}

	    // Draw the screen.
	    repaint();
	  }
	}
      }
    }
  }

  public void update(Graphics g)
  {
    // No need to clear screen, we're double buffering.
    paint(g);
  }

  public void paint(Graphics g)
  {
    // Clear out the off screen drawing image.
    OffScreenG.setColor(Color.black);
    OffScreenG.fillRect(0, 0, XBound, YBound);

    // Set our drawing color.
    OffScreenG.setColor(C);

    // Draw the lines.
    for(int i=0; i < NumLines; i++)
    {
      Lines[i].DrawLine(OffScreenG);
    }

    // Copy the buffer into the graphic we're actually displaying.
    g.drawImage(OffScreenImage, 0, 0, this);
  }
}

