/**

  Code from the Connect the Dots UIViewController to
  animate the dots moving.

*/


  /// True if we are currently moving the dots
  ///
  /// -Note: Be sure to set to false when you load a new puzzle,
  /// or you could end up trying to set the locations of the new
  /// puzzle's dots with a location map computed for the old puzzle.
  ///
  private var animating = false
  
  /**
   Responds to double-tap by toggle our animating flag.
   If we set the flag to true, then we begin the animation.
   
   **Modifies**: self
   
   **Effects**: Toggles whether the animation is running.
   
   */
  @IBAction private func animate(_ sender: UITapGestureRecognizer) {
    if sender.state == .ended {      
      if (animating) {
        animating = false
      } else {
        animating = true
        animateMovingDots()
      }
    }
  }
  
  ////////////////////////////////////

  // Version 1:

  /**
   Runs the animation in two steps.  In the global queue, compute the
   "forces" on the dots.  Then update the puzzle and view from back
   inside the main thread.
   
   This method illustrates how to add closures to both the global and
   main DispatchQueues.
   
   **Modifies**: self
   
   **Effects**: Changes the positions of the dots in the puzzle.
   
   */
  private func animateMovingDots() {
    DispatchQueue.global().async { [weak self] in
      if let locations = self?.puzzle.computeNewLocations() {
        DispatchQueue.main.async {
          if self?.animating ?? false {
            self?.puzzle.set(locations: locations )
            self?.updateUI()
            self?.graphView.zoomToMax()
            self?.animateMovingDots()
          }
        }
      }
    }
  }
  
  ////////////////////////////////////////

  // Version 2:

  /// Same as previous version, but more robust since it ensures
  /// the puzzle has not changed during animation
  /**
  Runs the animation in two steps.  In the global queue, compute the
  "forces" on the dots.  Then update the puzzle and view from back
  inside the main thread.
  
  This method illustrates how to add closures to both the global and
  main DispatchQueues.
  
  **Modifies**: self
  
  **Effects**: Changes the positions of the dots in the puzzle.
  
  */
  private func animateMovingDots() {
    let currentPuzzle = self.puzzle
    DispatchQueue.global().async { [weak self] in
      if let locations = currentPuzzle.computeNewLocations() {
        DispatchQueue.main.async {
          if (self?.animating ?? false) && currentPuzzle === self?.puzzle {
            self?.puzzle.set(locations: locations )
            self?.updateUI()
            self?.graphView.zoomToMax()
            self?.animateMovingDots()
          }
        }
      }
    }
  }