package ocelot.desktop.ui.widget

import ocelot.desktop.OcelotDesktop
import ocelot.desktop.audio.{ClickSoundSourceFactory, SoundSource}
import ocelot.desktop.geometry.Padding2D
import ocelot.desktop.ui.layout.LinearLayout
import ocelot.desktop.ui.widget.ChangeSimulationSpeedDialog.validateIntervalUs
import ocelot.desktop.ui.widget.modal.ModalDialog
import ocelot.desktop.util.Orientation

import scala.concurrent.duration.{Duration, DurationLong}

class ChangeSimulationSpeedDialog extends ModalDialog {
  private var tickInterval: Option[Duration] = Some(OcelotDesktop.ticker.tickInterval)

  private def confirm(): Unit = {
    if (tickInterval.isDefined) {
      OcelotDesktop.ticker.tickInterval = tickInterval.get
      OcelotDesktop.pushToTickerIntervalHistory(tickInterval.get)
    }
    close()
  }

  children :+= new PaddingBox(
    new Widget {
      override val layout = new LinearLayout(this, orientation = Orientation.Vertical)

      children :+= new PaddingBox(new Label("Change simulation speed"), Padding2D(bottom = 16))

      private var inputTPS: TextInput = _
      private var inputMSPT: TextInput = _
      private def formatMSPT(interval: Duration): String = (interval.toMicros / 1000f).toString
      private def formatTPS(interval: Duration): String = (1_000_000f / interval.toMicros).toString

      inputMSPT = new TextInput(formatMSPT(OcelotDesktop.ticker.tickInterval)) {
        focus()

        private def parseInput(text: String): Option[Duration] = {
          try {
            validateIntervalUs((text.toFloat * 1000).toLong)
          } catch {
            case _: NumberFormatException => None
          }
        }

        override def onInput(text: String): Unit = {
          tickInterval = parseInput(text).map { interval =>
            val tps = formatTPS(interval)
            if (inputTPS.text != tps) inputTPS.text = tps
            interval
          }
        }

        override def validator(text: String): Boolean = parseInput(text).isDefined

        override def onConfirm(): Unit = confirm()
      }

      inputTPS = new TextInput(formatTPS(OcelotDesktop.ticker.tickInterval)) {
        private def parseInput(text: String): Option[Duration] = {
          try {
            validateIntervalUs((1_000_000 / text.toFloat).toLong)
          } catch {
            case _: NumberFormatException => None
          }
        }

        override def onInput(text: String): Unit = {
          tickInterval = parseInput(text).map { interval =>
            val mspt = formatMSPT(interval)
            if (inputMSPT.text != mspt) inputMSPT.text = mspt
            interval
          }
        }

        override def validator(text: String): Boolean = parseInput(text).isDefined

        override def onConfirm(): Unit = confirm()
      }

      children :+= new Label("Milliseconds per tick:")
      children :+= new PaddingBox(inputMSPT, Padding2D(bottom = 8))
      children :+= new Label("Ticks per second:")
      children :+= new PaddingBox(inputTPS, Padding2D(bottom = 8))

      children :+= new Widget {
        children :+= new Filler

        children :+= new PaddingBox(
          new Button {
            override def text: String = "Cancel"
            override protected def clickSoundSource: ClickSoundSourceFactory = SoundSource.InterfaceClickLow
            override def onClick(): Unit = close()
          },
          Padding2D(right = 8),
        )

        children :+= new Button {
          override def text: String = "Apply"
          override def onClick(): Unit = confirm()
          override def enabled: Boolean = tickInterval.nonEmpty
        }
      }
    },
    Padding2D.equal(16),
  )
}

object ChangeSimulationSpeedDialog {
  private val MaxUpdateInterval = 1.minute
  private val MinUpdateInterval = 1.micro

  private def validateIntervalUs(us: Long): Option[Duration] = {
    try {
      val interval = us.micros

      Option.when(interval >= MinUpdateInterval && interval <= MaxUpdateInterval)(interval)
    } catch {
      case _: IllegalArgumentException => None
    }
  }
}
