package ocelot.desktop.entity

import ocelot.desktop.entity.OcelotCard.LogEvent
import totoro.ocelot.brain.Constants
import totoro.ocelot.brain.entity.machine.{Arguments, Callback, Context}
import totoro.ocelot.brain.entity.traits.DeviceInfo.{DeviceAttribute, DeviceClass}
import totoro.ocelot.brain.entity.traits.{DeviceInfo, Entity, Environment, Tiered, result}
import totoro.ocelot.brain.event.{EventBus, NodeEvent}
import totoro.ocelot.brain.network.{Network, Node, Visibility}
import totoro.ocelot.brain.util.Tier
import totoro.ocelot.brain.util.Tier.Tier

import java.time.Instant

class OcelotCard extends Entity with Environment with DeviceInfo with Tiered {
  override val node: Node = Network.newNode(this, Visibility.Neighbors)
    .withComponent("ocelot", Visibility.Neighbors)
    .create()

  override def tier: Tier = Tier.One

  private final lazy val deviceInfo = Map(
    DeviceAttribute.Class -> DeviceClass.Generic,
    DeviceAttribute.Description -> "Yarn ball manipulator",
    DeviceAttribute.Vendor -> Constants.DeviceInfo.DefaultVendor,
    DeviceAttribute.Product -> "Catbus",
  )

  override def getDeviceInfo: Map[String, String] = deviceInfo

  @Callback(direct = true, doc = """function(message: string) -- Logs a message to the Ocelot console.""")
  def log(context: Context, args: Arguments): Array[AnyRef] = {
    val message = args.checkString(0)
    EventBus.send(LogEvent(node.address, message))
    result()
  }

  @Callback(direct = true, doc = """function(): integer -- Returns the current Unix timestamp (UTC, in milliseconds).""")
  def getTimestamp(context: Context, args: Arguments): Array[AnyRef] = {
    result(Instant.now().toEpochMilli)
  }

  @Callback(direct = true, doc = """function(): integer -- Returns a high-resolution timer value (in nanoseconds).""")
  def getInstant(context: Context, args: Arguments): Array[AnyRef] = {
    result(System.nanoTime())
  }

  @Callback(direct = true, doc = """function(): number -- Returns the remaining call budget.""")
  def getRemainingCallBudget(context: Context, args: Arguments): Array[AnyRef] = {
    result(context.getRemainingCallBudget)
  }

  @Callback(direct = true, doc = """function(): number -- Returns the maximum call budget.""")
  def getMaxCallBudget(context: Context, args: Arguments): Array[AnyRef] = {
    result(context.getMaxCallBudget)
  }
}

object OcelotCard {
  case class LogEvent(address: String, message: String) extends NodeEvent
}
