/**
 * Styx Applet
 * Jens.Trapp@DLR.de, 12.05.96
 *
 * Draws a bunch of moving lines on the Screen.
 *
 *
 * Parameters:
 * delay -- Length auf Pause. Influences speed. Default: 20
 * num_lines --Number of Lines. Default: 10/20
 * seed -- Factor for how chaotic the styx starts. Default: 10
 * dx1 -- Start velocity x-value for first points. Default: random(seed)+1
 * dy1 -- Start velocity y-value for first points. Default: random(seed)+1
 * dx2 -- Start velocity x-value for second points. Default: random(seed)+1
 * dy2 -- Start velocity y-value for second points. Default: random(seed)+1
 * color -- Enter fixed color for Styx. Default: Randomized
 * variable_num -- allow randomized number of lines for Styx.
 * min_num --specifies minimum number of lines for variable number. Default: 10
 * max_num --specifies maximum number of lines for variable number. Default: 100
 */
 
 
import java.applet.*;
import java.awt.*;
import java.lang.*;

public class styx extends Applet implements Runnable
{
	protected Thread daemon;
	protected boolean threadSuspended = false;
	private int delay = 30;  // Verzögerung.
	private int speed = 1;

	private int width=100;
	private int height=100;

	private boolean random_colors = true;
	private Color bg_color   = Color.white;

	private boolean variate_num_lines= false;
	private int max_lines= 100;
	private int min_lines = 1;
	private int num_lines=10;
	private int defined_lines=0;
	private int actual_line=0;
	private int last_line = 0;

	private Point points[][];

	private Point dv[];
	
	private int red=0;
	private int green=0;
	private int blue=0;

	public styx()
	{
			points= allocate_pointfield(2,max_lines);
			dv=new Point[2];
			
			for (int i=0; i<2; i++)
			{
				dv[i]= new Point(0,0);
			}
	}

	private Point[][] allocate_pointfield(int m, int n)
	{
		Point[][] pts= new Point[m][];
			
		for (int i=0; i<m; i++)
		{
			pts[i]= new Point[n];
			for (int j=0; j<n; j++)
				pts[i][j] = new Point(0,0);
		}
		
		return pts;
	}
	
	public void draw_styx(Graphics g)
	{
		if(actual_line<defined_lines)
		{
			// Delete old line
			g.setColor(bg_color);
			g.drawLine(points[0][actual_line].x, points[0][actual_line].y,
				points[1][actual_line].x, points[1][actual_line].y);
		}

		if(actual_line<num_lines)
		{
			points[0][actual_line].x= points[0][last_line].x+dv[0].x*speed;
			points[0][actual_line].y= points[0][last_line].y+dv[0].y*speed;
			points[1][actual_line].x= points[1][last_line].x+dv[1].x*speed;
			points[1][actual_line].y= points[1][last_line].y+dv[1].y*speed;

			check_coordinate_x(points[0][actual_line], dv[0], width);
			check_coordinate_y(points[0][actual_line], dv[0], height);
			check_coordinate_x(points[1][actual_line], dv[1], width);
			check_coordinate_y(points[1][actual_line], dv[1], height);
	
			if(false)
			{
			// If lines get too long, let the endpointss move towards each other
			if (Math.abs(points[0][actual_line].x-points[1][actual_line].x)>width/3)
			{
				if (dv[0].x*dv[1].x>=0)
				{
					if(Math.abs(dv[0].x)>Math.abs(dv[1].x))
						dv[0].x=-dv[0].x;
					else
						dv[1].x=-dv[1].x;
				}
			}
			
			if (Math.abs(points[0][actual_line].y-points[1][actual_line].y)>height/3)
			{		
				if (dv[0].y*dv[1].y>=0)
				{
					if(Math.abs(dv[0].y)>Math.abs(dv[1].y))
						dv[0].y=-dv[0].y;
					else
						dv[1].y=-dv[1].y;
				}			
			}
			}
			if (Math.abs(dv[0].x)>width/6)
				dv[0].x = random(width/12)*(width/12)*sign(dv[0].x);
			if (Math.abs(dv[1].x)>width/6)
				dv[1].x = random(width/12)*(width/12)*sign(dv[1].x);
			if (Math.abs(dv[0].y)>height/6)
				dv[0].y = random(height/12)*(height/12)*sign(dv[0].y);
			if (Math.abs(dv[1].y)>height/6)
				dv[1].y = random(height/12)*(height/12)*sign(dv[1].y);
		

			// Randomize Colors
			if (random_colors)
			{
				red=(red+signed_random(8))%256;
				green=(green+signed_random(8))%256;
				blue=(blue+signed_random(8))%256;
		
				while (red<0) red=256+red;
				while (green<0) green=256+green;
				while (blue<0) blue=256+blue;
			}

			// Draw it!
			if ((points[0][actual_line].x!=points[1][actual_line].x)
				||(points[0][actual_line].y!=points[1][actual_line].y))
			{
				g.setColor(new Color(red,green,blue));
				g.drawLine(points[0][actual_line].x, points[0][actual_line].y,
				points[1][actual_line].x, points[1][actual_line].y);
			}

			last_line = actual_line;
		}

		actual_line++;

		if (defined_lines<num_lines)
		{
			if(actual_line>num_lines)
			{
				actual_line=0;
				if (variate_num_lines)
					num_lines+=signed_random(1);
			}
			if(actual_line>defined_lines)
				defined_lines=actual_line;
		}
		else
		{
			if (actual_line>=defined_lines)
			{
				actual_line=0;
				defined_lines=num_lines;
			}
		}
	}

	private void check_coordinate_x(Point value, Point velocity, int cwidth)
	{
	 		if (value.x>cwidth)
			{
				value.x=cwidth;
				velocity.x=-Math.abs(velocity.x)+signed_random(2);
				if (velocity.x>=0)
					velocity.x=-random(2)-1;				
				velocity.y+=signed_random(2);				
			}
			else if (value.x<0)
			{
				value.x=0;
				velocity.x=Math.abs(velocity.x)+signed_random(2);
				if (velocity.x<=0);
					velocity.x=random(2)+1;				

				velocity.y+=signed_random(Math.abs(velocity.x/2));
			}
	}
	private void check_coordinate_y(Point value, Point velocity, int cwidth)
	{
	 		if (value.y>cwidth)
			{
				value.y=cwidth;
				velocity.y=-Math.abs(velocity.y)+signed_random(2);
				if (velocity.y>=0);
					velocity.y=-random(2)-1;				
				velocity.x+=signed_random(2);				
			}
			else if (value.y<0)
			{
				value.y=0;
				velocity.y=Math.abs(velocity.y)+signed_random(2);
				if (velocity.y>=0);
					velocity.y=random(2)+1;				
				velocity.x+=signed_random(2);
			}
	}
	
	
	public int sign(int value)
	{
		if(value==0)
			return 0;
		return value/Math.abs(value);
	}

	public int random(int range)
	{
		return (int)(Math.random()*(double)range);
	}

	public int random(int start_value, int end_value)
	{
		return start_value
			+ (int)(Math.random()*(double)(end_value -start_value));
	}

	public int signed_random(int range)
	{
		return (int)(Math.random()*2*(double)range)-range;
	}

	public void set_movement(int seed)
	{
		if (seed<0)
			seed=1;
		if (dv!=null)
		{
			dv[0].move(random(seed)+1, random(seed)+1);
			dv[1].move(random(seed)+1, random(seed)+1);
		}
		else
			System.out.println("Something went wrong in set_movement");
		
	}

	public void get_params()
	{
		if(getParameter("WIDTH") !=  null)
			width=Integer.parseInt(getParameter("WIDTH"));
		if(getParameter("HEIGHT") !=  null)
			height=Integer.parseInt(getParameter("HEIGHT"));

		if (getParameter("delay") != null)
			delay = Integer.parseInt(getParameter("delay"));
		if (getParameter("num_lines") != null)
			num_lines = Integer.parseInt(getParameter("num_lines"));

		if (getParameter("seed") != null)
			set_movement(Integer.parseInt(getParameter("seed")));

		if (getParameter("dx1") != null)
			dv[0].x = Integer.parseInt(getParameter("dx1"));
		if (getParameter("dy1") != null)
			dv[0].y = Integer.parseInt(getParameter("dy1"));
		if (getParameter("dx2") != null)
			dv[1].x = Integer.parseInt(getParameter("dx2"));
		if (getParameter("dy2") != null)
			dv[1].y = Integer.parseInt(getParameter("dy2"));


		if(getParameter("color") != null)
		{
			random_colors=false;
			red = get_color(getParameter("color")).getRed();
			green = get_color(getParameter("color")).getGreen();
			blue = get_color(getParameter("color")).getBlue();
		}


		if (getParameter("variable_num")!= null)
			variate_num_lines=Boolean.getBoolean(getParameter("variable_num"));
		if (getParameter("min_lines") != null)
			min_lines= Integer.parseInt(getParameter("min_num"));
		if (getParameter("max_lines") != null)
			max_lines= Integer.parseInt(getParameter("max_num"));

		if(num_lines<1)
			num_lines=1;
		if(max_lines<num_lines)
			max_lines=num_lines;
		if(min_lines<1)
			min_lines=1;
		points=allocate_pointfield(2,max_lines); // realloc
	}

	public void init()
	{
		bg_color=this.getBackground();
		set_movement(10);
		get_params();

	}

	public void clear_screen()
	{
		Graphics g=getGraphics();

			g.clearRect(0,0,width,height);
	}

	public boolean action(Event e, Object arg)
	{
		if("Reset".equals(arg)){
			clear_screen();
			set_movement(10);

			return true;
		}

		return super.handleEvent(e);
	}


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

	public synchronized void stop()
	{
		daemon.stop();
		daemon = null;
	}

	public boolean mouseDown(Event evt, int x, int y)
	{
		if (threadSuspended)
			daemon.resume();
		else
			daemon.suspend();

		threadSuspended = !threadSuspended;

		return true;
	}

	public void run()
	{
		while(daemon != null)
		{
			try {Thread.sleep(delay);}
			catch (InterruptedException e){}
			draw_styx(getGraphics());
		}
	}

	public Color get_color(String color)
	{
		if (color.compareTo("White")==0)
			return Color.white;
		else if (color.compareTo("Red")==0)
			return Color.red;
		else if (color.compareTo("Green")==0)
			return Color.green;
		else if (color.compareTo("Blue")==0)
			return Color.blue;
		else if (color.compareTo("Cyan")==0)
			return Color.cyan;
		else if (color.compareTo("DarkGray")==0)
			return Color.darkGray;
		else if (color.compareTo("LightGray")==0)
			return Color.lightGray;
		else if (color.compareTo("Magenta")==0)
			return Color.magenta;
		else if (color.compareTo("orange")==0)
			return Color.orange;
		else if (color.compareTo("pink")==0)
			return Color.pink;
		else if (color.compareTo("yellow")==0)
			return Color.yellow;
		else 
			return Color.black;
	}

}


