'use strict'

const NOOP_RECURSOR = {
  size: 0,
  next () { return null }
}
exports.NOOP_RECURSOR = NOOP_RECURSOR

function fork (recursor) {
  const buffer = []

  return {
    shared () {
      const next = recursor()
      if (next !== null) buffer.push(next)
      return next
    },

    recursor () {
      if (buffer.length > 0) return buffer.shift()
      return recursor()
    }
  }
}
exports.fork = fork

function map (recursor, mapFn) {
  return () => {
    const next = recursor()
    if (next === null) return null

    return mapFn(next)
  }
}
exports.map = map

function replay (state, create) {
  if (state === NOOP_RECURSOR) return {state, recursor: state}

  if (!state) {
    state = Object.assign({
      buffer: [],
      done: false
    }, create())
  }

  let done = false
  let index = 0
  const next = () => {
    if (done) return null

    let retval = state.buffer[index]
    if (retval === undefined) {
      retval = state.buffer[index] = state.next()
    }

    index++
    if (retval === null) {
      done = true
    }
    return retval
  }

  return {state, recursor: {next, size: state.size}}
}
exports.replay = replay

function sequence (first, second) {
  let fromFirst = true
  return () => {
    if (fromFirst) {
      const next = first()
      if (next) return next

      fromFirst = false
    }

    return second()
  }
}
exports.sequence = sequence

function unshift (recursor, value) {
  return () => {
    if (value) {
      const next = value
      value = null
      return next
    }

    return recursor()
  }
}
exports.unshift = unshift

'use strict'

const NOOP_RECURSOR = {
  size: 0,
  next () { return null }
}
exports.NOOP_RECURSOR = NOOP_RECURSOR

function fork (recursor) {
  const buffer = []

  return {
    shared () {
      const next = recursor()
      if (next !== null) buffer.push(next)
      return next
    },

    recursor () {
      if (buffer.length > 0) return buffer.shift()
      return recursor()
    }
  }
}
exports.fork = fork

function map (recursor, mapFn) {
  return () => {
    const next = recursor()
    if (next === null) return null

    return mapFn(next)
  }
}
exports.map = map

function replay (state, create) {
  if (!state) {
    const recursor = create()
    if (recursor === NOOP_RECURSOR) {
      state = recursor
    } else {
      state = Object.assign({
        buffer: [],
        done: false
      }, recursor)
    }
  }

  if (state === NOOP_RECURSOR) return {state, recursor: state}

  let done = false
  let index = 0
  const next = () => {
    if (done) return null

    let retval = state.buffer[index]
    if (retval === undefined) {
      retval = state.buffer[index] = state.next()
    }

    index++
    if (retval === null) {
      done = true
    }
    return retval
  }

  return {state, recursor: {next, size: state.size}}
}
exports.replay = replay

function sequence (first, second) {
  let fromFirst = true
  return () => {
    if (fromFirst) {
      const next = first()
      if (next) return next

      fromFirst = false
    }

    return second()
  }
}
exports.sequence = sequence

function unshift (recursor, value) {
  return () => {
    if (value) {
      const next = value
      value = null
      return next
    }

    return recursor()
  }
}
exports.unshift = unshift

Fix noop detection in replay() recursor util