import java.awt.Canvas;
import java.awt.Color;


/**The ball class constructs a runnable object for the Thread class.
 *The ball moves in a line and bounces at a random angle off the boundary
 *of the Canvas object parameter in the constructor. The balls initialcolor,
 *size and speed are also parameters in the constructor. The speed, size,
 *color and direction can be changed the life of the associated thread.
 *@author Bill Kraynek
 */

public class Ball implements Runnable {
    private int x;
    private int oldx;
    private int y;
    private int oldy;
    private Color color;
    private boolean isRunning;
    private boolean isMoving;
    private int xO;
    private int yO;
    private int xIncrement;
    private int yIncrement;
    private double angle;
    private double slope;
    private Canvas theCanvas;
    private int speed;
    private int size;
    private int width;
    private int height;
    
    /**
     * The constructor
     */
    public Ball(Canvas C,Color color,int speed, int size) {
        super();
        theCanvas = C;
        this.width = C.getSize().width;
        this.height = C.getSize().height;
        this.color = color;
        this.speed = speed;
        this.size = size;
        isMoving = true;
        isRunning = true;
    }
    
    /**
     *The run method. This is here the 'action' is. When the ball reaches the
     *border the equation of a new line to bring it back is calculated.
     *The slope is random.
     */
    public void run() {
        x = 1;
        oldx = 0;
        y = 1;
        oldy = 0;
        xO = 1;
        yO = 1;
        xIncrement = speed;
        yIncrement = speed;
        angle = Math.PI / 2.0 * Math.random();
        slope = Math.tan(angle);
        // This is the equation of the initial line
        if( -1.0 <= slope && slope <= 1.0 ) {
            y = (int)(slope * (x - xO) + yO);
        } else {
            x = (int)((1.0/slope)*(y-yO) + xO);
        }
        while( isRunning ) {
            if( x <= 0 ) {
                xIncrement = speed;
                if( yIncrement > 0 )
                    angle = Math.PI / 2.0 * Math.random();
                else
                    angle = -Math.PI / 2.0 * Math.random();
                slope = Math.tan(angle);
                xO = x;
                yO = y;
            } // end if
            if( y <= 0 ) {
                if( xIncrement < 0 ) {
                    angle = Math.PI / 2.0 * Math.random() + Math.PI /2.0;
                } else {
                    angle = Math.PI / 2.0 * Math.random();
                } // end if
                yIncrement = speed;
                slope = Math.tan(angle);
                xO = x;
                yO = y;
            } // end if
            if( x >= width - size ) {
                xIncrement = -speed;
                if( yIncrement > 0 )
                    angle = Math.PI / 2.0 * Math.random() + Math.PI / 2.0;
                else
                    angle = Math.PI  / 2.0 * Math.random() + Math.PI;
                slope = Math.tan(angle);
                xO = x;
                yO = y;
            } // end if
            if( y >= height - size ) {
                yIncrement = -speed;
                if( xIncrement > 0 ) {
                    angle = Math.PI / 2.0 * Math.random() - Math.PI / 2.0;
                } else {
                    angle = Math.PI / 2.0 * Math.random() + Math.PI;
                } // end if
                slope = Math.tan(angle);
                xO = x;
                yO = y;
            } // end if
            if( xIncrement > 0 )
                xIncrement = speed;
            else
                xIncrement = -speed;
            if( yIncrement > 0 )
                yIncrement = speed;
            else
                yIncrement = -speed;
            if( isMoving ) {
                if( -1.0 <= slope && slope <= 1.0) {
                    x += xIncrement;
                    y = (int)(slope * (x - xO) + yO);
                } else {
                    y += yIncrement;
                    x = (int)((y-yO)/slope + xO);
                } // end if
            } // end if
            if( oldx != x || oldy != y ) {
                theCanvas.repaint();
            }
            oldx = x;
            oldy = y;
            try {Thread.sleep(1);} catch(InterruptedException e){}
        } // end while
    } // end run
    
    // Access methods
    
    /**
     *@return the x coordinate of the ball
     */
    public int getX() {
        return x;
    }
    
    /**
     *@return the y coordinate of the ball
     */
    public int getY() {
        return y;
    }
    
    /**
     *@return the color of the ball
     */
    public Color getColor() {
        return color;
    }
    
    /**
     *@return the size of the ball
     */
    public int getSize() {
        return size;
    }
    
    /**
     *@return the speed of the ball
     */
    public int getSpeed() {
        return speed;
    }
    
    // Modifier methods
    
    /**
     *Set the base x-coordinate of the line of the ball
     */
    public void setXO(int xO) {
        this.xO = xO;
    }
    
    /**
     *Set the base y-coordinate of the line of the ball
     */
    public void setYO(int yO) {
        this.yO = yO;
    }
    
    /**
     *Set the slope of the line of the ball
     */
    public void setSlope(double s) {
        slope = s;
    }
    
    /**
     *Set the x direction (+1 means right, -1 means left)
     */
    public void setXDirection(int d) {
        xIncrement = d*Math.abs(xIncrement);
    }
    
    /**
     *Set the y direction (+1 means down, -1 means up)
     */
    public void setYDirection(int d) {
        yIncrement = d*Math.abs(yIncrement);
    }
    
    /**
     *Set the speed of the ball
     */
    public void setSpeed(int speed) {
        this.speed = speed;
    }
    
    /**
     *Set the color of the ball
     */
    public void setColor(Color color) {
        this.color = color;
    }
    
    /**
     *Set the size of the ball
     */
    public void setSize(int size) {
        this.size = size;
    }
    
    /**
     *Set isRunning. If isRunning is false the corresponding thread
     *is stopped.
     */
    public void setIsRunning(boolean r) {
        isRunning = r;
    }
    
    /**
     *Set isMoving. If isMoving is false the ball stops. Setting
     *isMoving to true starts the ball again
     */
    public void setIsMoving(boolean m) {
        isMoving = m;
    }
    
} // end Ball

