//
// Basketball program
// 
// Drag basketball into hoop
// 
// @author Kim Bruce
//

dialect "rtobjectdraw"
 
type Draggable = {
   // move item dx to right and dy down
   moveBy (dx: Number, dy: Number) -> Done

   // return true iff point is inside object
   contains (point: Point) -> Boolean

   // move object so it is located at point
   moveTo(point: Point) -> Done
}

class basketBallAt (startPosn: Point) size (size: Number)
                 on (canvas: DrawingCanvas) -> Draggable {
    // nice color for a basketball
    def orange: Color = colorGen.r (250) g (85) b (10)

    // make the basketball and color its interior orange
    def body: Graphic2D = filledOvalAt (startPosn) size (size @ size) on (canvas)
    body.color := orange
    def border: Graphic2D = framedOvalAt (startPosn) size (size @ size) on (canvas)
    
    // make the straight lines on the basketball
    def vertical: Line = 
        lineFrom ((startPosn.x + size / 2) @ (startPosn.y))
            to ((startPosn.x + size / 2) @ (startPosn.y + size))
            on (canvas)

    def horizontal: Line = 
        lineFrom ((startPosn.x) @ (startPosn.y + size / 2))
            to ((startPosn.x + size) @ (startPosn.y + size / 2))
            on (canvas)

    // length of arc (in degrees)
    def cutSize:Number = 100
    
    // angles where cuts start
    def rightCutStart:Number = 90 + (180 - cutSize) / 2
    def leftCutStart:Number = 270 + (180 - cutSize) / 2

    def rightCutPosn: Point = (startPosn.x + (size * 2 / 3)) @ (startPosn.y)
    def leftCutPosn: Point = (startPosn.x - (size * 2 / 3)) @ (startPosn.y)

    // make the arcs
    def rightCut: Graphic2D = 
        framedArcAt (rightCutPosn) size (size @ size)
                    from (rightCutStart) to (rightCutStart + cutSize) on (canvas)
    def leftCut: Graphic2D = 
        framedArcAt (leftCutPosn) size (size @ size)
                    from (leftCutStart) to (leftCutStart + cutSize) on (canvas)

    // move the ball by specified offsets
    method moveBy (dx: Number, dy: Number) -> Done {
      body.moveBy (dx, dy);
      border.moveBy (dx, dy);
      vertical.moveBy (dx, dy);
      horizontal.moveBy (dx, dy);
      leftCut.moveBy (dx, dy);
      rightCut.moveBy (dx, dy);
    }

    // check to see if the ball contains a specified location
    method contains (point: Point) -> Boolean {
      body.contains (point)
    }

    // move the ball to a specified position
    method moveTo (point: Point) -> Done{
      // figure out how far to move and then let moveBy do the rest
      self.moveBy ( point.x - body.x, point.y - body.y )
    }
}
   

// Actual program which allows user to play basketball
def basketballGame: GraphicApplication = object {
    inherit graphicApplicationSize (400 @ 400)

    // Location of score display
    def displayLocn: Point = 150 @ 200

    // Location and dimensions of hoop
    def hoopLocn: Point = 160 @ 50
    def hoopWidth: Number = 100
    def hoopHeight: Number = 60


    // starting position of basketball and diameter
    def startLocn: Point = 190 @ 300
    def ballSize: Number = 40

    // points scored
    var score: Number := 0
   
    // location mouse at last
    var lastMouse: Point
    
    // whether ball is being dragged
    var dragging: Boolean

    // create display showing current score
    def display: Text = textAt (displayLocn) with ("Your score is 0") on (canvas)
    display.fontSize := 16

    // create the basketball hoop
    def hoop: Graphic2D = 
          framedOvalAt (hoopLocn) size (hoopWidth @ hoopHeight) on (canvas)

    // the basketball
    def ball: Draggable = basketBallAt (startLocn) size (ballSize) on (canvas)

    // save mouse location
    method onMousePress (point: Point) -> Done{
      lastMouse := point
      dragging := ball.contains (point)
    }

    // drag ball if mouse on it
    method onMouseDrag (point: Point) -> Done {
      if (dragging) then {
         ball.moveBy (point.x - lastMouse.x, point.y - lastMouse.y)
         lastMouse := point
      }
    }

    // update score when mouse is released
    method onMouseRelease (point: Point) -> Done{
      
      if (ball.contains (lastMouse)) then {
         if (dragging) then {
            score := score + 2
            display.contents := "Your score is {score}"
         }
      }
      ball.moveTo (startLocn)
    }

    // required to pop up window and start graphics
    startGraphics
}
