package li.cil.oc.server.fs

import java.io.FileNotFoundException
import java.util.concurrent.Callable

import li.cil.oc.api
import li.cil.oc.api.fs.Mode
import li.cil.oc.util.ExtendedNBT._
import net.minecraft.nbt.NBTTagCompound

import scala.collection.mutable

class CompositeReadOnlyFileSystem(factories: mutable.LinkedHashMap[String, Callable[api.fs.FileSystem]]) extends api.fs.FileSystem {
  var parts = mutable.LinkedHashMap.empty[String, api.fs.FileSystem]
  for ((name, factory) <- factories) {
    val fs = factory.call()
    if (fs != null) {
      parts += name -> fs
    }
  }

  // ----------------------------------------------------------------------- //

  override def isReadOnly = true

  override def spaceTotal = math.max(spaceUsed, parts.values.map(_.spaceTotal).sum)

  override def spaceUsed = parts.values.map(_.spaceUsed).sum

  // ----------------------------------------------------------------------- //

  override def exists(path: String) = findFileSystem(path).isDefined

  override def size(path: String) = findFileSystem(path).fold(0L)(_.size(path))

  override def isDirectory(path: String) = findFileSystem(path).fold(false)(_.isDirectory(path))

  override def lastModified(path: String) = findFileSystem(path).fold(0L)(_.lastModified(path))

  override def list(path: String) = if (isDirectory(path)) {
    parts.values.foldLeft(mutable.Set.empty[String])((acc, fs) => {
      if (fs.exists(path)) try {
        val l = fs.list(path)
        if (l != null) for (e <- l) {
          val f = e.stripSuffix("/")
          val d = f + "/"
          // Avoid duplicates and always only use the latest entry.
          acc -= f
          acc -= d
          acc += e
        }
      }
      catch {
        case _: Throwable =>
      }
      acc
    }).toArray
  }
  else null

  // ----------------------------------------------------------------------- //

  override def delete(path: String) = false

  override def makeDirectory(path: String) = false

  override def rename(from: String, to: String) = false

  override def setLastModified(path: String, time: Long) = false

  // ----------------------------------------------------------------------- //

  override def open(path: String, mode: Mode) = findFileSystem(path) match {
    case Some(fs) => fs.open(path, mode)
    case _ => throw new FileNotFoundException()
  }

  override def getHandle(handle: Int) = parts.valuesIterator.map(_.getHandle(handle)).find(_ != null).orNull

  override def close() = parts.values.foreach(_.close())

  // ----------------------------------------------------------------------- //

  override def load(nbt: NBTTagCompound) {
    for ((name, fs) <- parts) {
      fs.load(nbt.getCompoundTag(name))
    }
  }

  override def save(nbt: NBTTagCompound) {
    for ((name, fs) <- parts) {
      nbt.setNewCompoundTag(name, fs.save)
    }
  }

  // ----------------------------------------------------------------------- //

  protected def findFileSystem(path: String): Option[api.fs.FileSystem] = parts.valuesIterator.toSeq.reverse.find(_.exists(path))
}
