/** The amount of time I worked on Assignment A4 is roughly 18 hours.
 * 
 * My interesting procedure includes a number of individual elements:
 * smileyBall(), moveBallOnceS(), inMotionS(), 
 * smileyBallnC(), moveBallOnceOC(), inMotionOC(), 
 * drawTopOfDesk(), drawUndersideOfDesk(), and ballGame().
 * 
 * The first 3 procedures/methods can be standalone, but create a variant
 * on the ball (a smiley ball). This was a bit difficult to create
 * as I had to code a smile relative to the size of the ball, and it
 * actually goes a little askew when the radius is divisible by 6
 * because of the way its coded, but the discrepancy isn't really
 * noticeable.
 * 
 * The second 3, respectively, generate a smiley that does
 * not clear the screen (so as to be used in a game), move the ball
 * with object collision (for a game), and keep the ball moving until
 * a win flag is set.
 * 
 * The last 3 draw the graphics for the game environment and
 * allow the ball to interact with it without leaving a trail.
 * It requires two user-inputted elements: a vx and vy for the ball.
 * The starting place is always the same for the ball and the radius
 * is fixed. I created a version of this game that allows the ball
 * to bounce around the whole room, but the game kept flickering with
 * all the redrawing, so I decided to limit the ball's bouncing to the
 * underside of the desk in this version.
 * 
 * Game background: You are an ant who has found himself
 * at CIT, a haven of bug killers and smiley ball throwers. 
 * Luckily, you managed to scoot under a smiley ball to hide yourself
 * from your enemies. However, one of them has found you: in fact, the
 * GenInf employee makes a deal with you that if you can toss the ball
 * into the garbage can, he will let you live (and have some sugar).
 * Little does he know you're the strongest ant in the world! When you
 * toss the ball, it will keep bouncing until you either win or squish
 * yourself with the ball.
 * 
 * Good luck my little ant friend!
 * 
 * Welcome to CIT Hoops! */

import java.awt.*;

/** Note: do NOT extend Turtle; extend YourTurtle */
public class Ball extends YourTurtle {
    private int radius;  // radius of the ball.
    private int vx;      // speed of the ball, moving in the horizontal direction.
    private int vy;      // speed of the ball, moving in the vertical direction.
    boolean win= false;  // condition for ball game
    boolean ant= true;
    
    /** Constructor: a window without anything in it */
    public Ball() {
        clear();
    }
    
    
    /** = radius of the ball */
    public int getRadius() {
        return radius;
    }
    
    /** = speed of the ball in the horizontal direction */
    public int getVx() {
        return vx;
    }
    
    /** = speed of the ball in the vertical direction */
    public int getVy() {
        return vy;
    }

    /** Create a ball with the turtle starting at the midpoint of the 
     * panel that has speed (vx, vy), has radius r, and is black. */
    public Ball(int vx, int vy, int r) {
        moveTo(250, 250, 0);    // start turtle at midpoint of panel
        setColor(Color.black);  // make ball black
        radius= r;
        fillCircle(2*radius);   // make a ball of diameter 2*radius
        this.vx= vx;
        this.vy= vy;
    }
    
    /** Create a ball with the turtle starting at (x, y) that has
     * speed (vx, vy), has radius r, and is drawn with Color c. */
    public Ball(int x, int y, int vx, int vy, int r, Color c) {
        moveTo(x, y, 0);          // start turtle at point (x, y)
        setColor(c);              // make ball color c
        radius= r; 
        fillCircle(2*radius);     // make a ball of diameter 2*radius
        this.vx= vx;
        this.vy= vy;       
    }

    /** Moves the ball once, as given by its speed (vx, vy). If an edge
     * of the ball comes in contact with a wall, reverse its 
     * corresponding coordinate speed (x or y, depending on the wall). */
    public void moveBallOnce() {
        int vangle = 0;  // angle of movement
        
        Color save= getColor();  // store old color, as per specs.
        setColor(12);            // turn color white to
        fillCircle(2*getRadius());    // erase ball.
        
        /** make sure that the y-position + the speed (next movement)
         * + the radius of the circle (edge) will not go past the panel edge. */
        if(getY() + getVx() + getRadius() > 500 || getY() + getVy() - getRadius() < 0) {
            vy= - vy;            
        }
        
        /** make sure that the x-position + the speed (next movement)
         * + the radius of the circle (edge) will not go past the panel edge. */
        if(getX() + getVx() + getRadius() > 500 || getX() + getVx() - getRadius() < 0) {
            vx= - vx;            
        }
        
        /** make sure the angle has no x-movement if vx is 0 (also avoids
         * divide by 0) */
        if(getVx() == 0) {
            vangle= getVy();
        }
        
        /** make sure the angle has no y-movement if vy is 0 (consistent) */
        if(getVy() == 0) {
            vangle= getVx();
        }
        
        /** set slope angle to vy / vx if both vx and vy are non-zero */
        if(getVx() != 0 && getVy() != 0) {
            vangle= getVy() / getVx();
        }
            
        /** move ball vx and vy pixels away from current position at a 
         * direction of vangle */
        moveTo(getX() + getVx(), getY() + getVy(), vangle);
        
        setColor(save);  // restore color to ball
        fillCircle(2*getRadius());  // redraw ball
    }
        
    /** Put the ball perpetually in motion (i.e. a loop that does
     * not terminate). The loop will pause the ball for 100 microseconds
     and then move the ball once. */
    public void inMotion() {
        boolean keepgoing= true;   // make sure loop is infinite.
        while(keepgoing == true) {
            pause(100);
            moveBallOnce();
        }
    }
    
    /** My procedures */
    
    /** #1 Smiley Ball Creation */
    /** Create a black ball with the turtle starting at (x, y) that has
     * speed (vx, vy) and radius r. Draw a yellow circle over this
     * of diameter - 1 (smiley face with border). Set eyes proportional
     * to the radius and the center of the circle. Draw a mouth with
     * a while loop.
     * 
     * Limitation: since r and moveTo are specified as ints,
     * smiley mouth position will be a little askew every time the r
     * is divisible by 6. I tried to rewrite move and moveTo to use
     * ints, but I'd also have to change the int specification of
     * drawLine in java.awt.Graphics... yikes.
     */
    public void smileyBall(int x, int y, int vx, int vy, int r) {
            clear();
            moveTo(x, y, 0);          // start turtle at point (x, y)
            setColor(Color.black);    // make ball have a black border
            radius= r; 
            this.vx= vx;
            this.vy= vy;
            fillCircle(2*radius);     // make a ball of diameter 2*radius
            
            setColor(Color.yellow);   // make ball smiley-colored
            fillCircle(2*(radius-1));
            
            setColor(Color.black);    // change to black to...
            moveTo((x - 2*r/7), (y - r/4), 0); // draw left-eye
            fillCircle(radius/4);
            moveTo((x + 2*r/7), (y - r/4), 0); // and right eye
            fillCircle(radius/4);
            
            int k= 0;
            int smile= 300;
            liftPen();
            moveTo(x - 2*r/5, y + r/3, 300);
            putPenDown();
            
            while(k != 7) {      // while loop to draw smile
              setAngle(smile);
                 move(r/6);
                 smile= smile + 20;
                 k= k + 1;               
            } 
            liftPen();           // place pen back in center for moving
            moveTo(x, y, 0);
            putPenDown();
             
    }
    
    /** #2 Smiley Ball Movement */
    /** Moves the smiley ball once, as given by its speed (vx, vy). 
     * If an edge of the ball comes in contact with a wall, 
     * reverse its corresponding coordinate speed (x or y, 
     * depending on the wall). */
    public void moveBallOnceS() {       
        setColor(12);            // turn color white to
        fillCircle(2*getRadius());    // erase ball.
        
        if(getY() + getVy() + getRadius() > 500 || 
           getY() + getVy() - getRadius() < 0) {
            vy= - vy;            
        }
        if(getX() + getVx() + getRadius() > 500 || 
           getX() + getVx() - getRadius() < 0) {
            vx= - vx;            
        }
        moveTo(getX() + getVx(), getY() + getVy(), 0);
        
        /** since the regions of the smiley can't be saved,
         * redraw the ball with all the given variables. */
        this.smileyBall(getX(), getY(), getVx(), getVy(), getRadius());
    }    
    
    /** Put the smiley ball perpetually in motion (i.e. a loop that does
     * not terminate). The loop will pause the ball for 100 microseconds
     and then move the ball once. */
    public void inMotionS() {
        boolean keepgoing= true;   // make sure loop is infinite.
        while(keepgoing == true) {
            pause(100);
            moveBallOnceS();
        }
    }
    
    /** #3 CIT Hoops */
    /** Smiley ball that doesn't clear the screen. For use in
     * ball game */
    public void smileyBallNC(int x, int y, int vx, int vy, int r) {
        moveTo(x, y, 0);          // start turtle at point (x, y)
        setColor(Color.black);    // make ball have a black border
        radius= r; 
        this.vx= vx;
        this.vy= vy;
        fillCircle(2*radius);     // make a ball of diameter 2*radius
            
        setColor(Color.yellow);   // make ball smiley-colored
        fillCircle(2*(radius-1));
            
        setColor(Color.black);    // change to black to...
        moveTo((x - 2*r/7), (y - r/4), 0); // draw left-eye
        fillCircle(radius/4);
        moveTo((x + 2*r/7), (y - r/4), 0); // and right eye
        fillCircle(radius/4);
            
        int k= 0;
        int smile= 300;
        liftPen();
        moveTo(x - 2*r/5, y + r/3, 300);
        putPenDown();
            
        while(k != 7) {      // while loop to draw smile
             setAngle(smile);
             move(r/6);
             smile= smile + 20;
             k= k + 1;               
        } 
        liftPen();           // place pen back in center for moving
        moveTo(x, y, 0);
        putPenDown();     
    }
    
    /** Same as moveBallOnce, but with object collision */
    public void moveBallOnceOC() {
        int xsave= getX();    // save old x-coordinate for after re-drawing
        int ysave= getY();    // save old y-coordinate for after re-drawing
        
        drawUndersideOfDesk();   // erase the ball track completely.
        moveTo(xsave, ysave, 0); // restores turtle to where ball should be.
        
        /** for ball coming from the left at left trashcan wall. */
        if(getX() + getVx() + getRadius() >= 395 && 
           getY() + getVy() + getRadius() >= 340 &&
           getVx() > 0) {
            vx= - vx;
        }
        /** for ball falling into the trashcan */
        if(getX() + getVx() - getRadius() <= 402 &&
           getY() + getVy() + getRadius() >= 340 &&
           getX() > 402 && getVx() < 0 && getVy() < 0) {
            vx= - vx;
        }
        
        /** for ball hitting the bottom: win! */
        if(getY() + getVy() + getRadius() >= 470 &&
           getX() + getRadius() > 402) {
            win= true;
        }
        
        if(getY() + getVy() + getRadius() > 500 || 
           getY() + getVy() - getRadius() < 190) {
            vy= - vy;            
        }
        if(getX() + getVx() + getRadius() > 500 || 
           getX() + getVx() - getRadius() < 0) {
            vx= - vx;            
        }
        
        if(getX() + getVx() - getRadius() <= 25 &&
           getY() + getVy() + getRadius() >= 490) {
            ant= false;
        }
                  
        moveTo(getX() + getVx(), getY() + getVy(), 0);
        
        /** since the regions of the smiley can't be saved,
         * redraw the ball with all the given variables. */
        this.smileyBallNC(getX(), getY(), getVx(), getVy(), getRadius());
    }    
    
    /** Put the smiley ball perpetually in motion (i.e. a loop that does
     * not terminate). The loop will pause the ball for 100 microseconds
     and then move the ball once.
     *
     * If the ball hits the bottom of the basket, the loop is stopped
     * and the win condition is met.
     * If the ball hits you (the ant), the loop is stopped and the
     * lose condition is met. And the ant is squished (no black border).
     */
    public void inMotionOC() {
        win= false;   // make sure loop is infinite if win condition not met.
        ant= true;    // "" "" if lose condition is not met.
        while(win == false && ant == true) {  // stop when ball hits bottom of basket (win then set to true).
            pause(100);
            moveBallOnceOC();
        }
        if(win == true) System.out.println("You win!");  // congrats!
        if(ant == false) {
            moveTo(20, 495, 0);
            setColor(7);
            drawRectangle(10, 3);
            setColor(9);
            moveTo(20, 496, 0);
            fillRectangle(9, 2);  // squashed ant
            System.out.println("You lose, ouch!"); // lose
        }
    }    
    
    /** CIT Hoops graphics */
    
    /** Top of the Desk, drawn in parts:
     * Computer, monitor, 3D elements, and "CIT" text */
    public void drawTopOfDesk() {
        /** background color */
        setColor(12);
        moveTo(250, 100, 0);
        fillRectangle(499, 199);
        
        setColor(1);             // C
        moveTo(340, 30, 0);
        drawRectangle(25, 5);
        setColor(11);
        moveTo(341, 31, 0);
        fillRectangle(24, 4);
        moveTo(340, 60, 0);
        setColor(1);
        drawRectangle(25, 5);
        setColor(11);
        moveTo(341, 61, 0);
        fillRectangle(24, 4);
        moveTo(330, 45, 0);
        setColor(1);
        drawRectangle(5, 25);
        setColor(11);
        moveTo(331, 46, 0);
        fillRectangle(4, 26);
        
        moveTo(372, 45, 0);    // I
        setColor(1);
        drawRectangle(5, 36);
        liftPen();
        setAngle(0);
        move(1);
        putPenDown();
        setColor(11);
        fillRectangle(4, 35);
        
        moveTo(405, 46, 0);    // T
        setColor(1);
        drawRectangle(5, 36);
        moveTo(405, 29, 0);
        setColor(1);
        drawRectangle(32, 5);
        moveTo(405, 30, 0);
        setColor(11);
        fillRectangle(31, 4);
        moveTo(406, 46, 0);
        setColor(11);
        fillRectangle(4, 36);
        
        /** desk top */
        setColor(5);
        moveTo(470, 160, 180);  // horizontal 3d line
        move(470);
        moveTo(470, 160, 327);  // diagonal 3d line
        move(37);
        moveTo(470, 160, 90);   // vertical 3d line
        move(160);
                
        /** computer; also looks like a big iPod */
        moveTo(50, 90, 0);
        setColor(1);
        fillRectangle(98, 179);  // tower
        moveTo(49, 16, 0);
        setColor(4);
        drawRectangle(88, 16);   // cd-rom #1
        moveTo(49, 36, 0);
        drawRectangle(88, 16);   // cd-rom #2
        
        moveTo(49, 136, 0);
        setColor(5);
        drawRectangle(80, 60);   // big dell dimension cover for usb ports
        setColor(7);
        fillRectangle(79, 59);
        moveTo(49, 106, 0);
        setColor(7);
        fillCircle(50);          // circular top with power button
        setColor(5);
        fillCircle(40);          // this says Dell on it
        setColor(7);
        fillCircle(15);          // power button
       
        /** monitor */
        moveTo(200, 70, 0);
        setColor(1);
        fillRectangle(139, 139); // monitor box
        setColor(2);
        fillRectangle(109, 109); // screen
        moveTo(170, 157, 0);
        setColor(7);
        fillRectangle(19, 34);   // left stand
        moveTo(230, 157, 0);
        fillRectangle(19, 34);   // right stand
        moveTo(200, 177, 0);
        fillRectangle(139, 7);   // stand base
        
        /** on screen */
        moveTo(200, 121, 0);
        setColor(7);
        fillRectangle(109, 7);   // start menu
        moveTo(152, 121, 0);
        setColor(5);
        drawRectangle(9, 4);     // start button
        moveTo(152, 23, 0);
        setColor(7);
        fillRectangle(7, 7);     // 1 icon for posterity
        moveTo(152, 35, 0);
        fillRectangle(7, 7);     // another icon!
    
        /** desk edge */
        moveTo(250, 190, 0);
        setColor(5);
        drawRectangle(498, 20);
        
    }
    
    /** Underside of desk, which is just one big slab of grey, some 3D elements,
     * and a mesh trashcan, drawn with a loop of squares. */
    public void drawUndersideOfDesk() {
        /** underside of desk */
        moveTo(250, 350, 0);
        setColor(5);
        drawRectangle(498, 298);
        setColor(7);
        fillRectangle(497, 297); // back part of desk
        setColor(4);
        moveTo(470, 200, 270);
        move(180);               // 3d side
        
        /** trashcan goal */
        moveTo(450, 420, 0);
        setColor(1);
        drawRectangle(97, 157);
        drawRectangle(98, 158);
        setColor(7);
        fillRectangle(95, 155);
        
        /** loop to make trashcan mesh */
        setColor(1);
        int k= 0;         // increment to move to next vertical line
        int xcounter= 0;  // inc. to move to next horizontal box
        int xval= 405;
        int yval= 345;
        while(k != 31) {  // fill lines of boxes
            while(xcounter != 155) {   // fill boxes in a line
                moveTo((xval + xcounter), yval, 0);
                drawRectangle(5, 5);
                xcounter= xcounter + 5;
            }          
            xcounter= 0;
            yval= yval + 5;
            k= k + 1;
        }
        
        /** if ant is still alive, the ant is drawn */
        if(ant == true) {
            moveTo(20, 495, 0);
            setColor(1);
            drawRectangle(10, 3);
            setColor(9);
            moveTo(20, 496, 0);
            fillRectangle(9, 2);
        }
    }
    
    /** CIT Hoops! (i.e. ballGame())
     * Requires two ints for vx and vy, and the game uses a variant
     * of inMotion to keep the ball moving until the bottom of the
     * basket is hit (if it is). */
    public void ballGame(int vx, int vy) {
        this.vx= vx;
        this.vy= vy;
        
        clear();
        drawTopOfDesk();
        drawUndersideOfDesk();
            
        /** smiley game time! */
        this.smileyBallNC(20, 475, this.vx, this.vy, 20);
        inMotionOC();  
    } 
}