constants = require('./constants')
streams = require('./streams')
ping_pong_host = require('./ping_pong_host')
Kefir = require('kefir')
moment = require('moment')
Q = require('q')
__ = require('highland')
R = require('ramda')

Kefir.DEPRECATION_WARNINGS = false

adjastStringByNumber = R.curry((number, str)->
  res = str
  if(str.length - constants.ADJAST_NUMBER_OF_SPACES < 0)
    res += R.repeat(" ")(constants.ADJAST_NUMBER_OF_SPACES - str.length).join('')
  res
)

adjastString = adjastStringByNumber(constants.ADJAST_NUMBER_OF_SPACES)

receivePromise = (duplex, timeout, payload)->
  defer = Q.defer()
  setTimeout(()->
    defer.reject(constants.NOTHING)
  , timeout)
  observingStream = duplex.fork()
  observingStream.each((val)->
    console.log('receivePromise:' + val)
    observingStream.destroy()
    if(val == constants.PING)
      duplex.write(constants.PONG)
      defer.resolve()
    else
      defer.reject(val)
  )
  defer.promise

receivingForeverLog = (duplex, contextName, partnerContext)->
  contextName = if contextName then contextName else ""
  contextName = adjastString(contextName)

  observingStream = duplex.fork()
  observingStream.each((val)->
    if(val == constants.PING)
      console.log("#{contextName} >> PONG: received PING! from #{partnerContext}")
      duplex.write(constants.PONG)
  )

sendAndCheckPromise = (duplex, timeout)->
  defer = Q.defer()
  setTimeout(()->
    defer.reject(constants.NOTHING)
  , timeout)
  observingStream = duplex.fork()
  observingStream
  .each((val)->
#    console.log('sendAndCheckPromise:' + val)
    observingStream.destroy()
    if(val == constants.PONG)
      defer.resolve()
    else
      defer.reject(val)
  )
  duplex.write(constants.PING)
  defer.promise

receivePromiseWithLog = (duplex, timeout, contextName, partnerContext)->
  fromPartner = if partnerContext then "from #{partnerContext}" else ""
  contextName = if contextName then contextName else ""
  contextName = adjastString(contextName)

  console.log("#{contextName} >> PONG: receiving #{fromPartner} ...")
  receivePromise(duplex, timeout)
  .then(
    ()->
      console.log("#{contextName} >> PONG: received PING! #{fromPartner}")
  ,
    (val)->
      valStr = null
      #      valStr = JSON.stringify(val)
      valToPrint = if valStr then valStr else val
      console.error("#{contextName} >> PONG: failed received PING! #{fromPartner} (but received - #{valToPrint})"))

sendAndCheckPromiseWithLog = (duplex, timeout, contextName, partnerContext)->
  fromPartner = if partnerContext then "from #{partnerContext}" else ""
  contextName = if contextName then contextName else ""
  contextName = adjastString(contextName)

  console.log("#{contextName} >> PING: sending #{fromPartner} ...")
  sendAndCheckPromise(duplex, timeout)
  .then(
    ()->
      console.log("#{contextName} >> PING: send and received PONG! #{fromPartner}")
  ,
    (val)->
      valStr = null
      #      valStr = JSON.stringify(val)
      valToPrint = if valStr then valStr else val
      console.error("#{contextName} >> PING: failed send and received PING! #{fromPartner} (but received - #{valToPrint})"))

roundWithLog = (duplex, timeout, contextName, partnerContext)->
  sendAndCheckPromiseWithLog(duplex, timeout, contextName, partnerContext)
  .then(()->
    receivePromiseWithLog(duplex, timeout, contextName, partnerContext)
  )

reverseRoundWithLog = (duplex, timeout, contextName, partnerContext)->
  receivePromiseWithLog(duplex, timeout, contextName, partnerContext)
  .then(()->
    setTimeout(()->
      sendAndCheckPromiseWithLog(duplex, timeout, contextName, partnerContext)
    , 1000)
  )

heartBeatClient = (duplex, timeout, contextName, partnerContext, beatTimeout)->
  sendAndCheckPromiseWithLog(duplex, timeout, contextName, partnerContext)

  setInterval(()->
    sendAndCheckPromiseWithLog(duplex, timeout, contextName, partnerContext)
  , beatTimeout)

heartBeatServer = (duplex, timeout, contextName, partnerContext)->
  alive = true
  observingStream = null
  kill = ()->
    if(alive)
      alive = false
      console.log("#{contextName} >> killed for #{partnerContext}")
      if observingStream
        observingStream.destroy()

  receivingForeverLog(duplex, timeout, contextName, partnerContext)
  kill

module.exports =
  receivePromise: receivePromise
  sendAndCheckPromise: sendAndCheckPromise
  receivePromiseWithLog: receivePromiseWithLog
  sendAndCheckPromiseWithLog: sendAndCheckPromiseWithLog
  roundWithLog: roundWithLog
  reverseRoundWithLog: reverseRoundWithLog
  heartBeatClient: heartBeatClient
  heartBeatServer: heartBeatServer
  sendingAndCheckingStream: streams.sendingAndCheckingStream
  receivingStream: streams.receivingStream
  PingPongSingleHost: ping_pong_host.PingPongSingleHost
  PingPongNestHost: ping_pong_host.PingPongNestHost
  PingPongConfig: ping_pong_host.PingPongConfig