package ocelot.desktop.windows import ocelot.desktop.audio.{SoundSource, SoundSources} import ocelot.desktop.color.Color import ocelot.desktop.geometry.{Padding2D, Size2D} import ocelot.desktop.graphics.Graphics import ocelot.desktop.node.nodes.ComputerNode import ocelot.desktop.ui.layout.{Layout, LinearLayout} import ocelot.desktop.ui.widget._ import ocelot.desktop.ui.widget.tooltip.Tooltip import ocelot.desktop.ui.widget.window.BasicWindow import ocelot.desktop.util.animation.UnitAnimation import ocelot.desktop.util.{DrawUtils, Orientation} import ocelot.desktop.{ColorScheme, OcelotDesktop} import totoro.ocelot.brain.entity.Case import totoro.ocelot.brain.nbt.NBTTagCompound class ComputerWindow(computerNode: ComputerNode) extends BasicWindow { def computer: Case = computerNode.computer def updateSlots(): Unit = { eepromBox.children(0) = computerNode.eepromSlot inner.children(1).children(2) = slotsWidget } private val eepromBox = new PaddingBox(computerNode.eepromSlot, Padding2D(right = 10)) private val bottomDrawerAnimation = UnitAnimation.easeInOutQuad(0.2f) bottomDrawerAnimation.goDown() private val bottomDrawer = new Widget { override def shouldClip: Boolean = true override def minimumSize: Size2D = Size2D(layout.minimumSize.width, 0) children :+= new PaddingBox(new Widget { children :+= new PaddingBox(new Widget { override protected val layout: Layout = new LinearLayout(this, orientation = Orientation.Vertical) class PerTickHistogram extends Histogram { private var lastTick = OcelotDesktop.ticker.tick def tickUpdate(): Unit = {} def reset(): Unit = { text = "N/A" history = Seq.fill(21)(0.0f).toArray } override def update(): Unit = { super.update() val curTick = OcelotDesktop.ticker.tick if (curTick > lastTick) { if (!computer.machine.isRunning) reset() lastTick = curTick tickUpdate() } } } children :+= new PerTickHistogram { override def tickUpdate(): Unit = { val (free, total) = computer.machine.latestMemoryUsage val used = total - free val ratio = if (total == 0) 0 else used.toFloat / total.toFloat text = f"${used.toFloat / 1024f / 1024f}%.1fM" history = history.slice(1, history.length) ++ Array(ratio) } override protected val tooltip: Option[Tooltip] = Some( new LabelTooltip("Memory usage (in Mb)") ) } children :+= new PaddingBox(new PerTickHistogram { override def tickUpdate(): Unit = { val (start, _end) = computer.machine.latestExecutionInfo val end = if (start < _end) _end else System.nanoTime() val cpuUsage = if (start == 0) 0 else ((end - start).toFloat / 1000000000f * OcelotDesktop.tpsCounter.fps).min(1f) text = f"${cpuUsage * 100}%.0f%%" history = history.slice(1, history.length) ++ Array(cpuUsage) } override protected val tooltip: Option[Tooltip] = Some( new LabelTooltip("CPU usage (in %)") ) }, Padding2D(top = 8)) children :+= new PaddingBox(new PerTickHistogram { override def tickUpdate(): Unit = { val (budget, maxBudget) = computer.machine.latestCallBudget text = f"${maxBudget - budget}%.1f" history = history.slice(1, history.length) ++ Array((1.0 - budget / maxBudget).toFloat) } override protected val tooltip: Option[Tooltip] = Some( new LabelTooltip("CPU usage (call budget)") ) }, Padding2D(top = 8)) }, Padding2D.equal(7)) override def draw(g: Graphics): Unit = { DrawUtils.panel(g, position.x, position.y, width, height) super.draw(g) } }, Padding2D(10, 12, 0, 12)) override def draw(g: Graphics): Unit = { if (height < 1) return super.draw(g) g.rect(position.x + 6, position.y + 2, width - 12, 2, ColorScheme("BottomDrawerBorder")) } } private val drawerButton = new IconButton( "buttons/BottomDrawerOpen", "buttons/BottomDrawerClose", isSwitch = true, tooltip = Some("Toggle computer usage histogram") ) { override def onPressed(): Unit = bottomDrawerAnimation.goUp() override def onReleased(): Unit = bottomDrawerAnimation.goDown() } private val inner = new Widget { override protected val layout: Layout = new LinearLayout(this, orientation = Orientation.Vertical) children :+= new PaddingBox(new Label { override def text: String = computerNode.labelOrAddress.take(36) override def isSmall: Boolean = true override def color: Color = ColorScheme("ComputerAddress") }, Padding2D(bottom = 8)) children :+= new Widget { children :+= new PaddingBox(drawerButton, Padding2D(top = 120)) children :+= new PaddingBox(new Widget { children :+= new PaddingBox(computerNode.eepromSlot, Padding2D(right = 10)) children :+= new IconButton( "buttons/PowerOff", "buttons/PowerOn", isSwitch = true, sizeMultiplier = 2 ) { override def isOn: Boolean = computer.machine.isRunning override def onPressed(): Unit = computerNode.turnOn() override def onReleased(): Unit = computerNode.turnOff() protected override def clickSoundSource: SoundSource = SoundSources.MinecraftClick } }, Padding2D(top = 44, left = 22)) children :+= slotsWidget } } children :+= new PaddingBox(new Widget { override protected val layout: Layout = new LinearLayout(this, orientation = Orientation.Vertical) children :+= new PaddingBox(inner, Padding2D(10, 12, 0, 12)) children :+= bottomDrawer }, Padding2D(bottom = 10)) private def slotsWidget: Widget = { val rows = Array( (19, computerNode.cardSlots), (8, Array(computerNode.cpuSlot) ++ computerNode.memorySlots), (8, computerNode.diskSlots ++ computerNode.floppySlot.toArray) ) new Widget { for ((padding, row) <- rows) { children :+= new PaddingBox(new Widget { override protected val layout: Layout = new LinearLayout(this, orientation = Orientation.Vertical) for (slot <- row) children :+= slot }, Padding2D(left = padding, top = 8)) } override def minimumSize: Size2D = Size2D(158, 140) override def draw(g: Graphics): Unit = { g.sprite("ComputerMotherboard", bounds.mapX(_ - 4).mapW(_ - 4)) drawChildren(g) } } } override def update(): Unit = { super.update() bottomDrawerAnimation.update() height = minimumSize.height + (bottomDrawerAnimation.value * bottomDrawer.maximumSize.height).round } override def draw(g: Graphics): Unit = { beginDraw(g) DrawUtils.windowWithShadow(g, position.x, position.y, size.width, size.height, 1f, 0.5f) drawChildren(g) endDraw(g) } override def save(nbt: NBTTagCompound): Unit = { super.save(nbt) bottomDrawerAnimation.save(nbt, "drawerAnimation") } override def load(nbt: NBTTagCompound): Unit = { bottomDrawerAnimation.load(nbt, "drawerAnimation") if (bottomDrawerAnimation.isGoingUp) drawerButton.playPressAnimation() super.load(nbt) } }