package ocelot.desktop.util

/**
  * Stores a value updated by calls to [[update]].
  */
trait Register[T] {
  /**
    * The currently stored value.
    */
  def value: T

  /**
    * Updates the stored value.
    *
    * @return `true` if the value has changed.
    */
  def update(): Boolean
}

object Register {
  class Writeable[T](initialValue: T) extends Register[T] {
    private var _value: T = initialValue
    override def value: T = _value

    /**
      * The value this register will be set to on next update.
      */
    var nextValue: T = _value

    override def update(): Boolean = {
      val changed = nextValue != _value
      _value = nextValue

      changed
    }
    
    def update(nextValue: T): Boolean = {
      this.nextValue = nextValue
      
      update()
    }
  }

  class Sampling[T](next: () => T) extends Register[T] {
    private var _value = next()
    override def value: T = _value

    override def update(): Boolean = {
      val nextValue = next()
      val changed = nextValue != _value
      _value = nextValue

      changed
    }
  }

  /**
    * Creates a [[Register.Writeable writeable register]] with the given initial value.
    */
  def apply[T](initialValue: T): Writeable[T] = new Writeable(initialValue)

  /**
    * Creates a [[Register.Sampling register]] that reevaluates the provided expression on every update.
    *
    * The expression is evaluated to compute the initial value.
    */
  def sampling[T](nextValue: => T): Sampling[T] = new Sampling(() => nextValue)
}
