package ocelot.desktop.inventory.item

import ocelot.desktop.OcelotDesktop
import ocelot.desktop.graphics.IconSource
import ocelot.desktop.inventory.traits.{ComponentItem, PersistableItem}
import ocelot.desktop.inventory.{Item, ItemFactory, ItemRecoverer}
import ocelot.desktop.ui.widget.InputDialog
import ocelot.desktop.ui.widget.contextmenu.{ContextMenu, ContextMenuEntry, ContextMenuIcon, ContextMenuSubmenu}
import ocelot.desktop.ui.widget.tooltip.ItemTooltip
import totoro.ocelot.brain.Settings
import totoro.ocelot.brain.entity.EEPROM
import totoro.ocelot.brain.entity.traits.{Entity, Environment}
import totoro.ocelot.brain.loot.Loot.{EEPROMFactory => LootEepromFactory}
import totoro.ocelot.brain.util.Tier.Tier

import java.net.{MalformedURLException, URL}
import javax.swing.JFileChooser
import scala.util.Try

class EepromItem(val eeprom: EEPROM) extends Item with ComponentItem with PersistableItem {
  override def entity: Entity with Environment = eeprom

  override def fillTooltip(tooltip: ItemTooltip): Unit = {
    super.fillTooltip(tooltip)

    val source = eeprom.codePath
      .map(path => s"Source path: $path")
      .orElse(eeprom.codeURL.map(url => s"Source URL: $url"))

    for (source <- source) {
      tooltip.addLine(source)
    }

    if (source.isEmpty && eeprom != null) {
      if (eeprom.codeBytes != null) {
        tooltip.addLine(
          s"Code: ${eeprom.codeBytes.map(_.length).getOrElse(0)} bytes / ${Settings.get.eepromSize} bytes"
        )
      }
      if (eeprom.volatileData != null)
        tooltip.addLine(s"Data: ${eeprom.volatileData.length} bytes / ${Settings.get.eepromDataSize} bytes")
      if (eeprom.readonly)
        tooltip.addLine("Readonly")
    }
  }

  override def fillRmbMenu(menu: ContextMenu): Unit = {
    menu.addEntry(new ContextMenuSubmenu("External data source", Some(ContextMenuIcon(IconSource.Icons.Code))) {
      addEntry(ContextMenuEntry("Local file", IconSource.Icons.File) {
        OcelotDesktop.showFileChooserDialog(JFileChooser.OPEN_DIALOG, JFileChooser.FILES_ONLY) { file =>
          Try {
            for (file <- file) {
              eeprom.codePath = Some(file.toPath)
            }
          }
        }
      })

      addEntry(ContextMenuEntry("File via URL", IconSource.Icons.Link) {
        new InputDialog(
          title = "File via URL",
          onConfirmed = { text =>
            eeprom.codeURL = Some(new URL(text))
          },
          inputValidator = text => {
            try {
              new URL(text)
              true
            } catch {
              case _: MalformedURLException => false
            }
          },
        ).show()
      })

      if (eeprom.codePath.nonEmpty || eeprom.codeURL.nonEmpty) {
        addEntry(ContextMenuEntry("Detach", IconSource.Icons.LinkSlash) {
          eeprom.codeBytes = Some(Array.empty)
        })
      }
    })

    super.fillRmbMenu(menu)
  }

  override def factory: EepromItem.Factory = new EepromItem.Factory.Code(
    code = eeprom.getBytes,
    name = eeprom.label,
    readonly = eeprom.readonly,
  )
}

object EepromItem {
  abstract class Factory extends ItemFactory {
    override type I = EepromItem

    override def itemClass: Class[I] = classOf

    override def tier: Option[Tier] = None

    override def icon: IconSource = IconSource.Items.Eeprom

    override def recoverers: Iterable[ItemRecoverer[_, _]] = Some(ItemRecoverer(new EepromItem(_)))
  }

  object Factory {
    class Loot(factory: LootEepromFactory) extends Factory {
      override def name: String = factory.label

      override def build(): EepromItem = new EepromItem(factory.create())
    }

    class Code(code: Array[Byte], override val name: String, readonly: Boolean) extends Factory {
      override def build(): EepromItem = {
        val eeprom = new EEPROM
        eeprom.codeBytes = Some(code)
        eeprom.label = name
        eeprom.readonly = readonly

        new EepromItem(eeprom)
      }
    }

    object Empty extends Factory {
      override def name: String = "EEPROM"

      override def build(): EepromItem = new EepromItem(new EEPROM)
    }
  }
}
