dialect "rtobjectdraw"
import "animation" as animator

type Dribbler = {
  // start dribbling the ball
  start -> Done
  // stop dribbling the ball
  stopDribbling -> Done
}

// animation that will bounce a ball repeatedly
class dribblerAt(ball: Graphic2D) -> Dribbler {
  // how many times ball should be moved to go all the way down or up
  def moves: Number = 9
  // how far to move ball each time
  def moveSize: Number = 8
  
  // delay between moves of ball
  def delay: Number = 35
  
  // delay between starting new bounces
  // be sure to leave enough time for ball to finish complete bounce
  def bounceDelay: Number = delay*2*moves + 200
  
  // determine whether ball should still be bouncing
  var moving: Boolean := true
  
  // Inform dribbler to stop moving the ball
  method stopDribbling -> Done {
    moving := false
  }
  
  // Start animating the bouncing ball
  method start -> Done {
    // Each time through this block, move ball down and back up
    // Until moving is false
    animator.while {moving} pausing (bounceDelay) do {
      // move the ball slowly to bottom of its bounce
      // Terminate if moving is false
      var moveCount: Number := 1
      animator.while {(moveCount <= moves) && moving} pausing (delay) do {
        ball.moveBy(0,moveSize)
        moveCount := moveCount + 1
      } finally {
        // move the ball slowly back up to finish its bounce.
        // terminate if moving is false
        moveCount := 1
        animator.while{(moveCount <= moves) && moving} pausing (delay) do {
          ball.moveBy(0,-moveSize)
          moveCount := moveCount + 1
        }
      }
    } 
  }
}

def bouncingBBallGame: GraphicApplication = object {
  inherit graphicApplicationSize(500 @ 600)
  def displayFontSize: Number = 16
  
  def hoopPosn: Point = 100 @ 160
  
  // space between basketballs
  def ballSpacing: Number = 230

  // starting position of left & right balls
  def leftBallPosn: Point = 70 @ 420
  def rightBallPosn: Point = leftBallPosn + (ballSpacing@0)
  
  // How far below image to show score
  def scoreDownSet: Number = 55
  
  // basketball hoop
  def hoop: Graphic2D = drawableImageAt(hoopPosn) size (160 @ 141) 
        url("http://www.cs.pomona.edu/classes/cs051G/Images/hoop.png")
        on (canvas)
        
  // Size of basketball images
  def ballWidth: Number = 82
  def ballHeight: Number = 85
        
  // create basketballs
  def leftBall: Graphic2D = drawableImageAt(leftBallPosn) size (ballWidth @ ballHeight) 
        url("http://www.cs.pomona.edu/classes/cs051G/Images/bball.png")
        on (canvas)
  def rightBall: Graphic2D = drawableImageAt(rightBallPosn) size (ballWidth @ ballHeight) 
        url("http://www.cs.pomona.edu/classes/cs051G/Images/bball.png")
        on (canvas)
  
  // create displays showing scores and set font sizes
  def leftDisplay: Text = textAt(leftBallPosn + (0@(1.5*leftBall.height)))
          with "Home 0" on (canvas)
  leftDisplay.fontSize := displayFontSize
  def rightDisplay: Text = textAt(rightBallPosn + ((-3)@(1.5*leftBall.height)))
          with "Visitor 0" on (canvas)
  rightDisplay.fontSize := displayFontSize
  
  // where mouse was before current mouse action
  var lastMouse: Point
  
  // Is someone moving a ball
  var inPlay: Boolean := false
  
  // dribbler moving the ball
  var player: Dribbler
  
  // which ball is in play (if inPlay is true)
  var ballInPlay: Graphic2D := leftBall
  
  // starting position of ball in play
  var ballInitPosn: Point
  
  // current scores of two players
  var leftScore: Number := 0
  var rightScore: Number := 0
  
  // If ball is in play, stop dribbling and center ball on mouse
  method onMousePress(pt: Point) -> Done {
    lastMouse := pt
    if (inPlay) then {
      player.stopDribbling
      ballInPlay.moveTo(pt-((ballInPlay.width/2) @ (ballInPlay.width/2)))
    }
  }
  
  // If a ball is already in play, make it follow mouse (just like dragging)
  // Otherwise try to pick it up and start dribbling it
  method onMouseMove(pt: Point) -> Done {
    if (inPlay) then {
      onMouseDrag(pt)
    } else {
      if (leftBall.contains(pt)) then {
        ballInPlay := leftBall
        ballInitPosn := leftBallPosn
        inPlay := true
      } elseif {rightBall.contains(pt)} then {
        ballInPlay := rightBall
        ballInitPosn := rightBallPosn
        inPlay := true
      }
      if (inPlay) then {
        ballInPlay.moveTo(pt-((ballInPlay.width/2)@(ballInPlay.height/2)))
        player := dribblerAt(ballInPlay)
        player.start
      }
    }
    lastMouse := pt
  }
  
  // When dragging, shift the ball by the amount mouse has moved
  method onMouseDrag(pt: Point) -> Done {
    if (inPlay) then {
      ballInPlay.moveBy(pt.x - lastMouse.x, pt.y - lastMouse.y)
      lastMouse := pt
    }
  }
  
  // When mouse released, update score and move the ball back to its
  // starting position
  method onMouseRelease(pt: Point) -> Done {
    if (hoop.contains(pt) && inPlay) then {
      if (ballInPlay == leftBall) then {
        leftScore := leftScore + 2
        leftDisplay.contents := "HOME {leftScore}"
      } else {
        rightScore := rightScore + 2
        rightDisplay.contents := "VISITORS {rightScore}"
      }
      dropTheBall
    }
  }
  
  // Move the ball back to its starting position
  method dropTheBall -> Done is confidential {
    if (inPlay) then {
      ballInPlay.moveTo(ballInitPosn)
      inPlay := false
      player.stopDribbling
    }
  }
  
  startGraphics
}