package ocelot.desktop.node.nodes

import ocelot.desktop.graphics.{Graphics, IconSource}
import ocelot.desktop.inventory.item.HddItem
import ocelot.desktop.inventory.traits.DiskItem
import ocelot.desktop.inventory.{Item, SyncedInventory}
import ocelot.desktop.node.Node.{HighlightThickness, NoHighlightSize}
import ocelot.desktop.node.{EntityNode, LabeledEntityNode, WindowedNode}
import ocelot.desktop.ui.event.ClickEvent
import ocelot.desktop.ui.event.handlers.DiskActivityHandler
import ocelot.desktop.ui.widget.DiskEditWindow
import ocelot.desktop.ui.widget.DiskEditWindow.EditableDisk
import ocelot.desktop.ui.widget.contextmenu.{ContextMenu, ContextMenuEntry}
import ocelot.desktop.ui.widget.slot.HddSlotWidget
import ocelot.desktop.ui.widget.window.Windowed
import ocelot.desktop.util.{DefaultSlotItemsFillable, DrawUtils}
import ocelot.desktop.windows.RaidWindow
import totoro.ocelot.brain.entity.Raid
import totoro.ocelot.brain.entity.traits.{Environment, Inventory}
import totoro.ocelot.brain.nbt.NBTTagCompound
import totoro.ocelot.brain.util.Tier

import scala.util.Random

class RaidNode(val raid: Raid)
  extends EntityNode(raid)
    with SyncedInventory
    with LabeledEntityNode
    with DiskActivityHandler
    with DefaultSlotItemsFillable
    with WindowedNode[RaidWindow]
    with EditableDisk {

  var diskSlots: Array[HddSlotWidget] = Array.tabulate(3)(index => new HddSlotWidget(Slot(index), Tier.Three))

  override val icon: IconSource = IconSource.Nodes.Raid.Default

  override def rotatable: Boolean = true

  override def draw(g: Graphics): Unit = {
    super.draw(g)

    val x = position.x + HighlightThickness
    val y = position.y + HighlightThickness

    for (i <- 0 until raid.getSizeInventory) {
      val driveIconSource = IconSource.Nodes.Raid.Drive(i)

      // Missing disks overlay
      if (Slot(i).isEmpty) {
        g.sprite(driveIconSource.Error, x, y, NoHighlightSize, NoHighlightSize)
      }

      // Disk activity overlay
      if (
        raid.shouldVisualizeDiskActivity && Random.nextDouble() > 0.1 && i == raid.lastDiskAccess % raid.getSizeInventory
      ) {
        DrawUtils.drawFilesystemActivity(g, x, y, driveIconSource)
      }
    }
  }

  // Required for disk activity events processing
  override def shouldReceiveEventsFor(address: String): Boolean =
    super.shouldReceiveEventsFor(address) || raid.filesystem.exists(_.node.address == address)
    
  private val diskEditWindow = new Windowed[DiskEditWindow] {
    override protected def createWindow(): DiskEditWindow = new DiskEditWindow(RaidNode.this)

    override protected def windowNBTKey: String = "diskEditWindow"
  }

  override def setupContextMenu(menu: ContextMenu, event: ClickEvent): Unit = {
    DiskItem.addRealPathContextMenuEntries(
      menu,
      raid,
      realPathSetter => {
        realPathSetter()
      },
    )

    menu.addEntry(ContextMenuEntry("Edit disk", IconSource.Icons.Edit) {
      diskEditWindow.window.open()
    })

    menu.addSeparator()

    super.setupContextMenu(menu, event)
  }

  override def load(nbt: NBTTagCompound): Unit = {
    super.load(nbt)
    
    diskEditWindow.load(nbt)
  }

  override def save(nbt: NBTTagCompound): Unit = {
    super.save(nbt)
    
    diskEditWindow.save(nbt)
  }

  override def dispose(): Unit = {
    diskEditWindow.closeAndDisposeWindow()

    super.dispose()
  }

  // -------------------------------- LabeledEntityNode --------------------------------

  override def fallbackLabelAddress: Option[String] = diskAddress

  // -------------------------------- Inventory --------------------------------

  override type I = Item with HddItem

  override def brainInventory: Inventory = raid.inventory.owner

  // ---------------------------- DefaultSlotItemsFillable ----------------------------

  override def fillSlotsWithDefaultItems(): Unit = {
    for (i <- diskSlots.indices)
      diskSlots(i).setItem(new HddItem.Factory(true, Tier.Three).build())
  }

  // -------------------------------- Window --------------------------------

  override def createWindow(): RaidWindow = new RaidWindow(this)

  // -------------------------------- EditableDisk --------------------------------

  override def disk: Option[Environment] = raid.filesystem

  override def diskKind: String = "RAID"

  override def diskAddress: Option[String] = raid.filesystem.map(_.node.address)

  override def capacity: Long = raid.filesystem.fold(0L)(_.fileSystem.spaceTotal)

  override def diskLabel: Option[String] = Option(raid.label.getLabel)

  override def diskLabel_=(label: Option[String]): Unit = raid.label.setLabel(label.orNull)

  override def isLabelWriteable: Boolean = true

  override def editingAllowed: Boolean = raid.filesystem.isDefined
}
