package li.cil.oc.server.fs

import java.io
import li.cil.oc.api.fs.Mode
import net.minecraft.nbt.NBTTagCompound
import org.apache.commons.io.FileUtils
import scala.collection.mutable

trait Buffered extends OutputStreamFileSystem {
  protected def fileRoot: io.File

  private val deletions = mutable.Map.empty[String, Long]

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

  override def delete(path: String) = {
    if (super.delete(path)) {
      deletions += path -> System.currentTimeMillis()
      true
    }
    else false
  }

  override def rename(from: String, to: String) = {
    if (super.rename(from, to)) {
      deletions += from -> System.currentTimeMillis()
      true
    }
    else false
  }

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

  override def load(nbt: NBTTagCompound) = {
    def recurse(path: String, directory: io.File) {
      makeDirectory(path)
      for (child <- directory.listFiles()) {
        val childPath = path + child.getName
        val childFile = new io.File(directory, child.getName)
        if (child.isDirectory) {
          recurse(childPath + "/", childFile)
        }
        else if (!exists(childPath) || !isDirectory(childPath)) {
          openOutputStream(childPath, Mode.Write) match {
            case Some(stream) =>
              val in = new io.FileInputStream(childFile).getChannel
              val out = java.nio.channels.Channels.newChannel(stream)
              in.transferTo(0, Long.MaxValue, out)
              in.close()
              out.close()
              setLastModified(childPath, childFile.lastModified())
            case _ => // File is open for writing.
          }
        }
      }
      setLastModified(path, directory.lastModified())
    }
    recurse("", fileRoot)

    super.load(nbt)
  }

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

    for ((path, time) <- deletions) {
      val file = new io.File(fileRoot, path)
      if (FileUtils.isFileOlder(file, time))
        FileUtils.deleteQuietly(file)
    }
    deletions.clear()

    def recurse(path: String) {
      val directory = new io.File(fileRoot, path)
      directory.mkdirs()
      for (child <- list(path)) {
        val childPath = path + child
        if (isDirectory(childPath))
          recurse(childPath)
        else {
          val childFile = new io.File(fileRoot, childPath)
          val time = lastModified(childPath)
          if (time == 0 || !childFile.exists() || FileUtils.isFileOlder(childFile, time)) {
            FileUtils.deleteQuietly(childFile)
            childFile.createNewFile()
            val out = new io.FileOutputStream(childFile).getChannel
            val in = java.nio.channels.Channels.newChannel(openInputStream(childPath).get)
            out.transferFrom(in, 0, Long.MaxValue)
            out.close()
            in.close()
            childFile.setLastModified(time)
          }
        }
      }
      directory.setLastModified(lastModified(path))
    }
    recurse("")
  }
}
