constants = require('./constants')
Kefir = require('kefir')
__ = require('highland')
moment = require('moment')

isPong = (val)->
  val && val['msg'] == constants.PONG && val['timestamp']

isPing = (val)->
  val && val['msg'] == constants.PING && val['timestamp']

createPong = (timestamp, payload)->
  res = msg: constants.PONG, timestamp: timestamp
  if(payload)
    res.payload = payload
  res

createPing = (payload)->
  res = msg: constants.PING, timestamp: moment().valueOf()
  if(payload)
    res.payload = payload
  res

receivingStream = (duplex, timeout, payload)->
  lastPingSpeedEmmiter = Kefir.emitter()
  lastPingSpeedProp = lastPingSpeedEmmiter.toProperty(()-> 0)
  isConnectedEmmiter = Kefir.emitter()
  isConnectedProp = isConnectedEmmiter.skipDuplicates().toProperty(()-> false)
#  isConnectedProp.log('receivingStream isConnectedProp')
  noValueEmmiter = Kefir.emitter()
  observingStream = null
  save1 = null
  noValueStream = null

  start = ()->
    noValueStream = noValueEmmiter.debounce(timeout)
    noValueEmmiter.emit({})

    save1  = ()->
      isConnectedEmmiter.emit(false)

    noValueStream.onValue(save1)

    observingStream = duplex.fork()
    observingStream.each((val)->
      if(isPing(val))
        lastPingSpeedEmmiter.emit(moment().valueOf() - val.timestamp)
        isConnectedEmmiter.emit(true)
        noValueEmmiter.emit({})
        duplex.write(createPong(val.timestamp, payload))
    )

  start: start,
  isConnectedProp: isConnectedProp,
  lastPingSpeedProp: lastPingSpeedProp,
  kill: ()->
    if(observingStream)
      observingStream.destroy()

    if(save1)
      noValueStream.offValue(save1)

    observingStream = null
    noValueEmmiter = null
    isConnectedEmmiter = null
    isConnectedProp = null
    lastPingSpeedProp = null

sendingAndCheckingStream = (duplex, timeout, payload)->
  lastPingAndPongSpeedEmmiter = Kefir.emitter()
  lastPingAndPongSpeedProp = lastPingAndPongSpeedEmmiter.toProperty(()-> 0)
  isConnectedEmmiter = Kefir.emitter()
  isConnectedProp = isConnectedEmmiter.skipDuplicates().toProperty(()-> false)
#  isConnectedProp.log('sendingAndChecking isConnectedProp')
  noValueEmmiter = Kefir.emitter()
  observingStream = null
  save1 = null
  save2 = null
  noValueStream = null

  start = ()->
    toWrite = __()
    toWrite.pipe(duplex)
    noValueStream = noValueEmmiter.debounce(timeout)
    noValueEmmiter.emit({})

    save1  = ()->

      isConnectedEmmiter.emit(false)

    save2  = ()->
      toWrite.write(createPing(payload))

    noValueStream.onValue(save1)

    isConnectedEmmiter.onValue(save2)

    observingStream = duplex.fork()
    observingStream
    .each((val)->
      if(isPong(val))
        lastPingAndPongSpeedEmmiter.emit(moment().valueOf() - val.timestamp)
        isConnectedEmmiter.emit(true)
        noValueEmmiter.emit({})
    )
    toWrite.write(createPing(payload))


  start: start,
  isConnectedProp: isConnectedProp,
  lastPingAndPongSpeedProp: lastPingAndPongSpeedProp,
  kill: ()->
    if(toWrite)
      toWrite.destroy()

    if(observingStream)
      observingStream.destroy()

    if(save1)
      noValueStream.offValue(save1)

    if(save2)
      isConnectedEmmiter.offValue(save2)

    observingStream = null
    noValueEmmiter = null
    isConnectedEmmiter = null
    isConnectedProp = null
    toWrite = null

module.exports =
  receivingStream: receivingStream
  sendingAndCheckingStream: sendingAndCheckingStream