var fs = require('fs')
var mktemp = require('mktemp')
var rimraf = require('rimraf')
var synchronized = require('synchronized')


exports.Builder = Builder
function Builder (options) {
  var self = this

  this.reader = options.reader
  this.compilerCollection = options.compilerCollection

  this.tmpDir = mktemp.createDirSync('broccoli-XXXXXX.tmp')
  process.on('exit', function () {
    self.cleanupTmpDir()
  })

  this.cacheDir = this.tmpDir + '/cache.tmp'
  fs.mkdirSync(this.cacheDir)

  // Debounce logic; should probably be extracted
  this.postBuildLock = {}
  this.buildScheduled = false
  this.lockReleaseTimer = null
  this.lockReleaseFunction = null
  this.lockReleaseFirstScheduledAt = null
}

// outputDir is optional
Builder.prototype.build = function (outputDir, callback) {
  var self = this

  var debounceDelay = 0
  if (outputDir == null) {
    // We are watching and serving; refactor this logic
    debounceDelay = 100
  }

  function scheduleLockReleaseTimer () {
    if (!self.lockReleaseFirstScheduledAt) self.lockReleaseFirstScheduledAt = Date.now()
    self.lockReleaseTimer = setTimeout(self.lockReleaseFunction, debounceDelay)
  }

  if (self.lockReleaseTimer && Date.now() < self.lockReleaseFirstScheduledAt + 1000) {
    // Reschedule running timer because we keep getting events, but never put
    // off more than 1000 milliseconds in total
    clearTimeout(self.lockReleaseTimer)
    scheduleLockReleaseTimer()
  }

  if (this.buildScheduled) return
  this.buildScheduled = true

  synchronized(this, function (done) {
    self.buildScheduled = false

    var startTime = Date.now()

    self.buildError = null

    self.cleanupBuildProducts() // remove last build's directories
    if (outputDir == null) {
      self.outputTmpDir = mktemp.createDirSync(self.tmpDir + '/output-XXXXXX.tmp')
      outputDir = self.outputTmpDir
    }

    var readerDest = mktemp.createDirSync(self.tmpDir + '/reader-XXXXXX.tmp')
    self.reader._setup({
      tmpDir: self.tmpDir, // should be a subdirectory so we can clean
      cacheDir: self.cacheDir
    })
    self.reader.read(readerDest, function (err) {
      if (err) {
        finish(err)
        return
      }
      self.compilerCollection._setup({
        tmpDir: self.tmpDir, // should be a subdirectory so we can clean
        cacheDir: self.cacheDir
      })
      self.compilerCollection.run(readerDest, outputDir, function (err) {
        finish(err)
      })
    })

    function finish (err) {
      if (err) self.buildError = err
      console.log('Built ' + (err ? 'with error ' : '') + '(' + (Date.now() - startTime) + ' ms)')
      releaseAfterDelay()
    }

    function releaseAfterDelay () {
      self.lockReleaseFunction = function () {
        self.lockReleaseTimer = null
        self.lockReleaseFunction = null
        self.lockReleaseFirstScheduledAt = null
        done()
        if (callback != null) callback()
      }
      scheduleLockReleaseTimer()
    }
  })
}

Builder.prototype.cleanupTmpDir = function (exitCode) {
  if (this.tmpDir != null) {
    rimraf.sync(this.tmpDir)
  }
}

Builder.prototype.cleanupBuildProducts = function () {
  var self = this
  // This needs updating for component-based architecture
  ;['preprocessDest', 'outputTmpDir'].forEach(function (field) {
    if (self[field] != null) {
      rimraf(self[field], function (err) {
        if (err) throw err
      })
      self[field] = null
    }
  })
}

@@ -1857,24 +1857,103 @@
 TmpDir%0A    %7D
+%0A    self.buildTmpDir = mktemp.createDirSync(self.tmpDir + '/build-XXXXXX.tmp')
 %0A%0A    var re
@@ -1981,33 +1981,38 @@
 ateDirSync(self.
-t
+buildT
 mpDir + '/reader
@@ -2073,59 +2073,20 @@
 elf.
-tmpDir, // should be a subdirectory so we can clean
+buildTmpDir,
 %0A   
@@ -2293,59 +2293,20 @@
 elf.
-tmpDir, // should be a subdirectory so we can clean
+buildTmpDir,
 %0A   
@@ -3156,139 +3156,89 @@
 var 
-self = this%0A  // This needs updating for component-based architecture%0A  ;%5B'preprocessDest', 'outputTmpDir'%5D.forEach(function (field
+fields = %5B'buildTmpDir', 'outputTmpDir'%5D%0A  for (var i = 0; i %3C fields.length; i++
 ) %7B%0A
@@ -3245,26 +3245,30 @@
     if (
-self
+this
 %5Bfield
+s%5Bi%5D
 %5D != nul
@@ -3288,91 +3288,51 @@
 mraf
-(self%5Bfield%5D, function (err) %7B%0A        if (err) throw err%0A      %7D)%0A      self
+.sync(this%5Bfields%5Bi%5D%5D)%0A      this
 %5Bfield
+s%5Bi%5D
 %5D = 
@@ -3338,19 +3338,18 @@
  null%0A    %7D%0A
+
   %7D
-)
 %0A%7D%0A

Clean up intermediate build products properly