//
// 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
}



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

    // Define a basketball object
    def ball: Draggable = object {
       // nice color for a basketball
       def orange: Color = colorGen.r (250) g (85) b (10)

       def startPosn: Point = 190 @ 300
       def size: Number = 40

       // 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 )
      }
    }
   

    // 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)


    // 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
}
