"use strict"
var stream = require("readable-stream")
var EventEmitter = require("events").EventEmitter
var util = require("util")
var delegate = require("delegates")

function noteChange (trackerGroup) {
  return function (name) {
    trackerGroup.emit('change', name || trackerGroup.name);
  }
}

var TrackerGroup = exports.TrackerGroup = function (name) {
  EventEmitter.call(this)
  this.name = name
  this.trackGroup = []
  this.totalWeight = 0
  this.noteChange = noteChange(this)
}
util.inherits(TrackerGroup, EventEmitter)

TrackerGroup.prototype.completed = function () {
  if (this.trackGroup.length==0) return 0
  var valPerWeight = 1 / this.totalWeight
  var completed = 0
  for (var i = 0, len = this.trackGroup.length; i < len; i++){
    var group = this.trackGroup[i];
    completed += valPerWeight * group.weight *  group.completed()
  }
  return completed
}

TrackerGroup.prototype.addUnit = function (unit, weight, noChange) {
  unit.weight = weight || 1
  this.totalWeight += unit.weight
  this.trackGroup.push(unit)
  // Bubble events back up
  unit.on("change", this.noteChange)
  if (! noChange) this.emit("change", this.name)
  return unit
}

TrackerGroup.prototype.newGroup = function (name, weight) {
  return this.addUnit(new TrackerGroup(name), weight)
}

TrackerGroup.prototype.newItem = function (name, todo, weight) {
  return this.addUnit(new Tracker(name, todo), weight)
}

TrackerGroup.prototype.newStream = function (name, todo, weight) {
  return this.addUnit(new TrackerStream(name, todo), weight)
}

TrackerGroup.prototype.finish = function () {
  if (! this.trackGroup.length) this.addUnit(new Tracker(), 1, true)
  for (var i = 0, len = this.trackGroup.length; i < len; i++) {
    var group = this.trackGroup[i]
    group.removeListener("change", this.noteChange)
    group.finish()
  }
  this.emit("change", this.name)
}

var buffer = "                                  "
TrackerGroup.prototype.debug = function (depth) {
  depth = depth || 0
  var indent = depth ? buffer.substr(0,depth) : ""
  var output = indent + (this.name||"top") + ": " + this.completed() + "\n"
  this.trackGroup.forEach(function(T) {
    if (T instanceof TrackerGroup) {
      output += T.debug(depth + 1)
    }
    else {
      output += indent + " " + T.name + ": " + T.completed() + "\n"
    }
  })
  return output
}

var Tracker = exports.Tracker = function (name,todo) {
  EventEmitter.call(this)
  this.name = name
  this.workDone =  0
  this.workTodo = todo || 0
}
util.inherits(Tracker, EventEmitter)

Tracker.prototype.completed = function () {
  return this.workTodo === 0 ? 0 : this.workDone / this.workTodo
}

Tracker.prototype.addWork = function (work) {
  this.workTodo += work
  this.emit("change", this.name)
}

Tracker.prototype.completeWork = function (work) {
  this.workDone += work
  if (this.workDone > this.workTodo) this.workDone = this.workTodo
  this.emit("change", this.name)
}

Tracker.prototype.finish = function () {
  this.workTodo = this.workDone = 1
  this.emit("change", this.name)
}


var TrackerStream = exports.TrackerStream = function (name, size, options) {
  stream.Transform.call(this, options)
  this.tracker = new Tracker(name, size)
  this.name = name
  var self = this
  this.tracker.on("change", function (name) { self.emit("change", name) })
}
util.inherits(TrackerStream, stream.Transform)

TrackerStream.prototype._transform = function (data, encoding, cb) {
  this.tracker.completeWork(data.length ? data.length : 1)
  this.push(data)
  cb()
}

TrackerStream.prototype._flush = function (cb) {
  this.tracker.finish()
  cb()
}

delegate(TrackerStream.prototype, "tracker")
  .method("completed")
  .method("addWork")
