package li.cil.oc.integration.nei

import codechicken.nei.recipe.IUsageHandler
import com.google.common.base.Strings
import li.cil.oc.api
import li.cil.oc.api.driver.EnvironmentAware
import li.cil.oc.api.prefab
import li.cil.oc.server.driver.Registry
import li.cil.oc.server.machine.Callbacks
import net.minecraft.item.ItemStack
import net.minecraft.util.EnumChatFormatting

import scala.collection.convert.WrapAsScala._

class CallbackDocHandler(pages: Option[Array[String]]) extends PagedUsageHandler(pages) {
  def this() = this(None)

  private val DocPattern = """(?s)^function(\(.*?\).*?) -- (.*)$""".r

  private val VexPattern = """(?s)^function(\(.*?\).*?); (.*)$""".r

  override def getRecipeName = "OpenComputers API"

  override def getUsageHandler(input: String, ingredients: AnyRef*): IUsageHandler = {
    if (input == "item") {
      ingredients.collect {
        case stack: ItemStack if stack.getItem != null =>
          val callbacks = api.Driver.environmentsFor(stack).flatMap(getCallbacks).toBuffer

          // TODO remove in OC 1.7
          if (callbacks.isEmpty) {
            callbacks ++= (Option(Registry.driverFor(stack)) match {
              case Some(driver: EnvironmentAware) =>
                getCallbacks(driver.providedEnvironment(stack))
              case _ => Registry.blocks.collect {
                case driver: prefab.DriverTileEntity with EnvironmentAware =>
                  if (driver.getTileEntityClass != null && !driver.getTileEntityClass.isInterface)
                    driver.providedEnvironment(stack)
                  else null
                case driver: EnvironmentAware => driver.providedEnvironment(stack)
              }.filter(_ != null).flatMap(getCallbacks)
            })
          }

          if (callbacks.nonEmpty) {
            val fullDocumentation = callbacks.toArray.sorted.mkString("\n\n")
            val pages = fullDocumentation.lines.grouped(12).map(_.mkString("\n")).toArray
            Option(new CallbackDocHandler(Option(pages)))
          }
          else None
      }.collectFirst {
        case Some(handler) => handler
      }.getOrElse(this)
    }
    else this
  }

  private def getCallbacks(env: Class[_]) = if (env != null) {
    Callbacks.fromClass(env).map {
      case (name, callback) =>
        val doc = callback.annotation.doc
        if (Strings.isNullOrEmpty(doc)) name
        else {
          val (signature, documentation) = doc match {
            case DocPattern(head, tail) => (name + head, tail)
            case VexPattern(head, tail) => (name + head, tail)
            case _ => (name, doc)
          }
          wrap(signature, 160).map(EnumChatFormatting.BLACK.toString + _).mkString("\n") +
            EnumChatFormatting.RESET + "\n" +
            wrap(documentation, 152).map("  " + _).mkString("\n")
        }
    }
  }
  else Seq.empty
}
