/*//////////
//
//  ball.c 
//
//      AUTHOR: Russell Lowke
//   CSCIE-215: Assignment Four
//
//
// Description: Modular Ball for pong
//
//      Method: Modular ball.c called from pong.c
//
//              updateball - updates all balls in play
//              addBall    - adds a new ball to the field
//              removeBall - removes ball from field
//              newBall    - mallocs memory for the ball
//              drawBall   - displays a ball
//              isBallOut  - checks if ball out of field
//            
//       Notes: balls[MAXBALLS] is a list of ptrs to all balls in play.
//              ball.c keeps track of total lives left [livesLeft]
*/

#include        "ball.h"

struct ball *balls[MAXBALLS];
static int livesLeft = 3;

/* 
//  updateBalls
//  
//     Draw all balls in the [balls] list.
//     Called from update in pong.c
*/
void updateBall()
{
    int i;
        
    for (i = 0; balls[i]; i++)
        drawBall(balls[i]);
}

/*
//  addBall
//
//     Add a new ball to the balls list.
*/
void addBall()
{
    int i;

    /* count how many balls so far */
    for(i = 0; balls[i]; i++) {}

    /* no more than MAXBALLS balls */
    if (i < MAXBALLS-1) {

        /* get a new ball */
        balls[i] = newBall(getMidCourtX(),
                           getMidCourtY(), 
                           - randomInt(6, 10)/10,
                           - randomInt(1, 4)/10);
    }
}


/*
//  removeBall
//
//     Search through the list of balls and remove ball [mBall]
//     Free memory used by [mBall]
//
//     args: *mBall         --  pointer to ball to remove
*/
void removeBall(struct ball *mBall)
{
    int i;                                 /* general purpose counter */

    struct ball *xBall = mBall;            /* copy ball ptr */
    for (i = 0; balls[i]; i++){
        if (balls[i] == xBall){            /* is this the ball to remove? */
            xBall    = balls[i+1];         /* get the next ball */
            balls[i] = xBall;              /* replace ball[i] with next ball */
        }
    }
    free(mBall);                           /* free ball from memory */
}



/*
//  ball
//
//     create a new ball
//     two case blocks for switches,
//     flag type and arg value is read from table mFlagArgs
//
//     args:  x         --  pointer to struct being modifed.
//            y         --  index of arg in mFlagArgs table
//            xV        --  switch arg to OFF (0)  or ON (1)
//            yV
*/
struct ball *newBall(double x, double y, double xV, double yV)
{
    struct ball *mReturn;
    
    mReturn = malloc(sizeof(struct ball));  /* malloc memory for ball */

    if ( mReturn == NULL )                  /* check out of memory */
        fatal("out of memory in newBall");  /* ... error */
    
    mReturn->xLoc = x;                      /* store values*/
    mReturn->yLoc = y;
    mReturn->xVel = xV;
    mReturn->yVel = yV;
    
    return mReturn;                         /* return pointer */
}


/*
//  drawBall
//
//     Erase balls old position, move ball at velocity
//     add gravity, chheck edges of court, test for paddle,
//     draw balls new position.
//     
//     args: *mBall          --  pointer to ball being drawn
*/
void drawBall(struct ball *mBall)
{
    
    // erase ball from old location
    drawAt(mBall->xLoc, mBall->yLoc, ' ');
    
    // move ball at velocity
    mBall->xLoc += mBall->xVel;
    mBall->yLoc += mBall->yVel;

    // gravity/
    mBall->yVel += getGravity();

    if (mBall->xVel > 1)
        mBall->xVel = 1;
    if (mBall->xVel < -1)
        mBall->xVel = -1;
    if (mBall->yVel > 1)
        mBall->yVel = 1;
    if (mBall->yVel < -1)
        mBall->yVel = -1;

    // bottom of court
    if (mBall->yLoc >= getBottomLimit()){
        mBall->yLoc =  getBottomLimit();      /* stop ball penitrating wall */
        mBall->yVel = 0 - mBall->yVel;        /* reverse y velocity */
    }
    // top of court
    if (mBall->yLoc <= getTopLimit()){
        mBall->yLoc =  getTopLimit();         /* stop ball penitrating wall */
        mBall->yVel = 0 - mBall->yVel;        /* reverse y velocity */
    }
    // left of court
    if (mBall->xLoc <= getLeftLimit()){
        mBall->xLoc =  getLeftLimit();        /* stop ball penitrating wall */
        mBall->xVel = 0 - mBall->xVel;        /* reverse x velocity */
    }
    
    // if ball out
    if (isBallOut(mBall)){

        /* remove the ball */
        removeBall(mBall);

        /* was that the last ball? */
        if (*balls == NULL) {
            livesLeft -= 1; 
            addBall();
        }
    }
    else
        drawAt(mBall->xLoc,
               mBall->yLoc, '*');   /* draw the ball */
}

/*
//  isBallOut?
//
//     if ball hit paddle teak velocity
//     return true if ball out
//     ...also keep track of the number of paddle hits
//        creating a new ball on very fourth paddle hit
//     
//     args: *mBall          --  pointer to ball being tested
*/
int isBallOut(struct ball *mBall)
{
    static int paddleHits = 0;
    
    // right side court
    if (mBall->xLoc >= getRightLimit()){
        mBall->xLoc =  getRightLimit();       /* stop ball penitrating*/
    
        // test for paddle
        if (is_a_hit( (int)(0.5+mBall->xLoc),
                      (int)(0.5+mBall->yLoc))) {
            
            mBall->xVel = 0 - mBall->xVel;    /* reverse x velocity */

            /* tweak the velocity by small random amount */
            mBall->xVel += (randomInt(1, 5)/10) - 0.3;
            mBall->yVel += (randomInt(1, 5)/10) - 0.3;

            /* time for an extra ball? */
            paddleHits += 1;
            if (paddleHits >= EXTRABALLAT) {
                addBall();
                paddleHits = 0;
            }
        }
        else {            
            return YES;
        }
    }

    return NO;
}


/*
//  drawAt
//
//     draw a char [symbol] at point [x,y]
//
//     args:  x         --  x location
//            y         --  y location
//            symbol    -- symbol to draw
*/
void drawAt(double x, double y, char symbol)
{
    move((int)(0.5+y), (int)(0.5+x));
    addch(symbol);
}


/*
//  disposeBalls
//
//     Clear memory of all balls.
*/
void disposeBalls()
{
    int i;
 
    for (i = 0; balls[i]; i++)          /* loop through the balls */
        free(balls[i]);                 /* free malloced memory   */
}


/*
//  getLivesLeft
//
//  return number of lives user has left
//  called from  main
//  [livesLeft] is a static var local to ball.c
*/
int getLivesLeft()
{
    return livesLeft;
}
