////////////////// NOTE TO STUDENTS //////////////////////////
// For the purposes of our course, it’s not necessary
// that you understand or even look at the code in this file.
//////////////////////////////////////////////////////////////

package o1.carsim.gui

import CarSim.Directions.Segment
import o1.util.nice.number.*
import CarEnhancement.StudentDriveResult

/** The class `CarThread` represents threads of execution each of which is responsible
  * for controlling a single `Car` object and the `CarMarker` that represents it visually.
  *
  * **NOTE TO STUDENTS: In this course, you don’t need to understand how this class works or can be used.** */
class CarThread(val marker: CarMarker, val onDriveError: StudentDriveResult => Unit) extends Thread:

  private var pauseRequested = false
  val car = this.marker.car
  private var markerSpeed = 0.0

  override def run() =
    while true do
      this.car.synchronized {
        while this.car.isIdle || this.pauseRequested do
          this.car.wait()
      }
      val drive: StudentDriveResult = this.marker.advanceAlongRoute()
      if !drive.wasSuccessful then
        this.onDriveError(drive)
      Thread.sleep((drive.segmentLength * this.markerSpeed).toInt)
    end while

  def startDriving(segments: Iterable[Segment]): Unit =

    this.car.synchronized {
      this.markerSpeed = determineSpeed(segments)
      this.marker.updateDestination(segments)
      this.unpause()
    }

    def determineSpeed(segments: Iterable[Segment]): Double =
      if segments.nonEmpty then
        val distance = segments.map( _.distance ).sum
        val animationDuration = (distance * 0.05) atLeast 4000 atMost 240_000
        animationDuration / distance
      else
        0.0

  end startDriving

  def fuel(wantedAmount: Double) =
    this.car.synchronized {
      val actualAmount = this.car.fuel(wantedAmount)
      this.unpause()
      actualAmount
    }

  def fillUp() =
    this.car.synchronized {
      this.car.fuel()
      this.unpause()
    }

  def pause() =
    this.car.synchronized {
      this.pauseRequested = true
    }

  def unpause() =
    this.car.synchronized {
      this.marker.refresh()
      this.pauseRequested = false
      this.car.notify()
    }

end CarThread
