/* Copyright 2009-2014 EPFL, Lausanne */

package leon
package utils

import java.io.File
import java.nio.file._
import scala.collection.JavaConversions._
import java.security.MessageDigest
import java.math.BigInteger

case class FilesWatcher(ctx: LeonContext, files: Seq[File]) {
  val toWatch = files.map(_.getAbsoluteFile).toSet
  val dirs    = toWatch.map(_.getParentFile)

  def onChange(body: => Unit): Unit = {
    val watcher = FileSystems.getDefault.newWatchService()
    dirs foreach (_.toPath.register(watcher, StandardWatchEventKinds.ENTRY_MODIFY))
    var lastHashes = toWatch.map(md5file)

    body
    ctx.reporter.info("Waiting for source changes...")

    while (true) {
      val key = watcher.take()

      val events = key.pollEvents()

      if (events.exists{_.context match {
        case (p: Path) => toWatch(p.toFile.getAbsoluteFile)
        case e => false
      }}) {
        val currentHashes = toWatch.map(md5file)
        if (currentHashes != lastHashes) {
          lastHashes = currentHashes

          ctx.reporter.info("Detected new version!")
          body
          ctx.reporter.info("Waiting for source changes...")
        }
      }

      key.reset()
    }
  }

  def md5file(f: File): String = {
    val buffer = new Array[Byte](1024)
    val md = MessageDigest.getInstance("MD5")
    try {
      val is = Files.newInputStream(f.toPath)
      var read = 0
      do {
        read = is.read(buffer)
        if (read > 0) {
          md.update(buffer, 0, read)
        }
      } while (read != -1)
      is.close()

      val bytes = md.digest()
      ("%0" + (bytes.length << 1) + "X").format(new BigInteger(1, bytes));
    } catch {
      case _: RuntimeException =>
        ""
    }
  }
}