'use strict'

var LocalState = require('./states/local')

var Host = module.exports = function(id, opts) {
  this.id = id
  this.opts = opts || {}
  if (!this.opts.state) this.opts.state = new LocalState({})

  this.state = this.opts.state.create(id)
}

var GossipTransform = require('./transform')
Host.prototype.exchange = function() {
  return new GossipTransform(this)
}

var GossipStream = require('./stream')
Host.prototype.createStream = function(opts) {
  return new GossipStream(this, opts)
}

// = {(r,max(μp(r))) | r ∈ P}
Host.prototype.getDigest = function() {
  var digest = []

  // randomize
  this.state.digest().forEach(function(s) {
    digest.splice(randomBetween(0, digest.length), 0, s)
  })

  return digest
}

// = {(r,k,v,n) | μp(r)(k) = (v,n) ∧ n > max(μq(r))}
Host.prototype.getDeltas = function(digest, result) {
  var deltas = Object.create(null), ids = []
  if (!result) result = []

  digest.forEach(function(s) {
    if (!this.state.knows(s.r)) return

    deltas[s.r] = this.state.history(s.r, s.n)
    ids.splice(randomBetween(0, ids.length), 0, s.r)
  }, this)

  if (!ids.length) return []

  // scuttle-breadth ordering - tries to be fair to all participants
  // (1) order deltas from same participant inversely by their version number
  // (2) rank deltas from the same participant incrementally (order by rank)
  // (3) shuffle deltas of the same rank
  if (this.opts.ordering === 'breadth') {
    while (ids.length) {
      for (var i = ids.length - 1; i >= 0; --i) {
        var id = ids[i]
        result.push(deltas[id].shift())
        if (!deltas[id].length) ids.splice(ids.indexOf(id), 1)
      }
    }
  }

  // scuttle-depth ordering
  // (1) order deltas from same participant inversely by their version number
  // (2) order by count of available deltas
  // (3) shuffle participants with the same number of available deltas
  else {
    ids.sort(function(lhs, rhs) {
      return deltas[rhs].length - deltas[lhs].length
    })
    ids.forEach(function(id) {
      deltas[id].forEach(function(delta) {
        result.push(delta)
      })
    }, this)
  }

  return result
}

Host.prototype.get = function(key) {
  return this.state.get(key)
}

Host.prototype.set = function(key, value) {
  this.state.set(key, value)
}

function randomBetween(min, max) {
  return Math.floor(Math.random() * (max - min + 1)) + min
}
@@ -323,24 +323,28 @@
  = function(
+opts
 ) %7B%0A  return
@@ -368,16 +368,22 @@
 orm(this
+, opts
 )%0A%7D%0A%0Avar
@@ -887,16 +887,40 @@
 ids = %5B%5D
+, includeUnknown = false
 %0A  if (!
@@ -967,24 +967,62 @@
 nction(s) %7B%0A
+    includeUnknown = s.done === true%0A%0A
     if (!thi
@@ -1149,16 +1149,16 @@
 0, s.r)%0A
-
   %7D, thi
@@ -1161,16 +1161,247 @@
  this)%0A%0A
+  if (includeUnknown) %7B%0A    this.state.digest().forEach(function(s) %7B%0A      if (ids.indexOf(s.r) %3E -1) return%0A%0A      deltas%5Bs.r%5D = this.state.history(s.r, 0)%0A      ids.splice(randomBetween(0, ids.length), 0, s.r)%0A    %7D, this)%0A  %7D%0A%0A
   if (!i

include unknown deltas directly