package ocelot.desktop.entity.traits

import ocelot.desktop.entity.traits.OcelotInterface.LogEvent
import totoro.ocelot.brain.entity.machine.{Arguments, Callback, Context}
import totoro.ocelot.brain.entity.traits.{result, Entity, Environment}
import totoro.ocelot.brain.event.{EventBus, NodeEvent}

import java.time.Instant

trait OcelotInterface extends Entity with Environment {
  @Callback(direct = true, doc = """function(message: string) -- Logs a message to the card's console.""")
  def log(context: Context, args: Arguments): Array[AnyRef] = {
    val message = args.checkString(0)
    EventBus.send(LogEvent.CardToUser(node.address, message))
    result()
  }

  @Callback(direct = true, doc = """function() -- Clears messages logged to the card's console.""")
  def clearLog(context: Context, args: Arguments): Array[AnyRef] = {
    EventBus.send(LogEvent.Clear(node.address))
    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)
  }

  def pushMessage(message: String): Unit = {
    node.sendToReachable("computer.signal", "ocelot_message", message)
    EventBus.send(LogEvent.UserToCard(node.address, message))
  }
}

object OcelotInterface {
  sealed trait LogEvent extends NodeEvent

  object LogEvent {
    case class CardToUser(address: String, message: String) extends LogEvent
    case class UserToCard(address: String, message: String) extends LogEvent
    case class Clear(address: String) extends LogEvent
  }
}
