pingPongDuplexCheck = require("../src/ping-pong-duplex-check")
highlandDuplex = require("highland-duplex")
MuxDemuxTelegraph = require("mux-demux-telegraph")
__ = require("highland")
test_utils = require './test_utils'
chai = require("chai")
Q = require("q")
R = require("ramda")
expect = chai.expect;
assert = chai.assert;
Kefir = require('kefir')
keyMirror = require('keymirror')
muxDemuxTelegraph = require('mux-demux-telegraph')


getKefirPropCurrentValuePromise = (prop)->
  defer = Q.defer()

  save = (x) ->
    defer.resolve(x)

    prop.offValue(save)
  prop.onValue(save)

  defer.promise


chromeExtensionContexts = keyMirror(
  worker: null
  contentScript: null
  inject: null
  backgroundPage: null
);


listOfPairs = [
  {
    pair: [
        name: chromeExtensionContexts.worker
      ,
        name: chromeExtensionContexts.contentScript
    ]
    timeout: 5000
  },
  {
    pair: [
        name: chromeExtensionContexts.worker
      ,
        name: chromeExtensionContexts.inject
    ],
    timeout: 5000
  },
  {
    pair: [
        name: chromeExtensionContexts.worker
      ,
        name: chromeExtensionContexts.contentScript
    ],
    timeout: 5000
  },
  {
    pair: [
      {name: chromeExtensionContexts.backgroundPage, isNest: true}
    ,
      name: chromeExtensionContexts.worker
    ],
    timeout: 5000
  },
  {
    pair: [
      {name: chromeExtensionContexts.backgroundPage, isNest: true}
    ,
      name: chromeExtensionContexts.contentScript
    ],
    timeout: 5000
  },
  {
    pair: [
      {name: chromeExtensionContexts.backgroundPage, isNest: true}
    ,
      name: chromeExtensionContexts.inject
    ],
    timeout: 5000
  },
  {
    pair: [
      {name: chromeExtensionContexts.contentScript},
      {name: chromeExtensionContexts.inject}
    ],
    timeout: 5000
  }
]

createInjectPostStation = muxDemuxTelegraph.getDuplexLines(backgroundPage:'BGP-I')
createBackgroundPagePostStation = muxDemuxTelegraph.getDuplexLines(inject:'BGP-I')

describe "ping-pong-duplex-check", ->
  it "should mux-demux config spawn all",(done) ->
    @timeout(10000)

    config = new pingPongDuplexCheck.PingPongConfig(
      allContextsList: R.keys(chromeExtensionContexts),
      pairs: listOfPairs,
      whiteList: [],
      blackList: [chromeExtensionContexts.worker, chromeExtensionContexts.contentScript]
    )

    {first:injectDuplex, second:backgroundPageDuplex} = test_utils.makeTwoConnectedDuplexes()

    injectPostStation = createInjectPostStation(injectDuplex)
    backgroundPagePostStation = createBackgroundPagePostStation(backgroundPageDuplex)

    config.spawnAll('inject', injectPostStation)
    backgroundPageHostMap = config.getHostMap('backgroundPage')(backgroundPagePostStation)

    backgroundPageHostMap.inject.spawn('tab-1').start()

    backgroundPageHostMap.inject.aggregateHostInfoProp.throttle(300).toProperty().log('backgroundPageHost Nest')

    setTimeout(
      ()->
        done()
    , 5000)


  it "should 1 mux-demux config",(done) ->
    @timeout(10000)

    config = new pingPongDuplexCheck.PingPongConfig(
      allContextsList: R.keys(chromeExtensionContexts),
      pairs: listOfPairs,
      whiteList: [],
      blackList: [chromeExtensionContexts.worker, chromeExtensionContexts.contentScript]
    )

    {first:injectDuplex, second:backgroundPageDuplex} = test_utils.makeTwoConnectedDuplexes()

    injectPostStation = createInjectPostStation(injectDuplex)
    backgroundPagePostStation = createBackgroundPagePostStation(backgroundPageDuplex)

    injectHostMap = config.getHostMap('inject')(injectPostStation)
    backgroundPageHostMap = config.getHostMap('backgroundPage')(backgroundPagePostStation)

    first = injectHostMap.backgroundPage.spawn()

#    first.timeStream.onValue((v)-> console.log('first.timeStream ' + JSON.stringify(v)))
    first.stateStream.onValue((v)-> console.log('first.stateStream ' + JSON.stringify(v)))

    first.start()
    backgroundPageHostMap.inject.spawn('tab-1').start()

    backgroundPageHostMap.inject.stateStream.toProperty().log('backgroundPageHost Nest stateStream')
#    backgroundPageHostMap.inject.timeStream.toProperty().log('backgroundPageHost Nest timeStream')

    setTimeout(
      ()->
        backgroundPageDuplex.pause()
        setTimeout(
          ()->
            backgroundPageDuplex.resume()
        , 2000)
    , 2000)

    setTimeout(
      ()->
        done()
    , 6000)

  
  it "test config", (done) ->
    @timeout(10000)

    config = new pingPongDuplexCheck.PingPongConfig(
      allContextsList: R.keys(chromeExtensionContexts),
      pairs: listOfPairs,
      whiteList: [],
      blackList: []
    )

    injectHosts = config.getWhiteListedHostsMapByPartner(chromeExtensionContexts.inject)
    backgroundPageHosts = config.getWhiteListedHostsMapByPartner(chromeExtensionContexts.backgroundPage)

    injectHost = injectHosts[chromeExtensionContexts.backgroundPage]
    backgroundPageHost = backgroundPageHosts[chromeExtensionContexts.inject]

    {first:pingDuplex, second:pongDuplex} = test_utils.makeTwoConnectedDuplexes()

    first = injectHost.spawn(pingDuplex)()
    second = backgroundPageHost.spawn(pongDuplex, 'tab-1')

    first.timeStream.onValue((v)-> console.log('first.timeStream ' + JSON.stringify(v)))
    first.stateStream.onValue((v)-> console.log('first.stateStream ' + JSON.stringify(v)))

    first.start()
    second.start()

    setTimeout(
      ()->
        injectHost.destroyHost()
        backgroundPageHost.destroyHost('tab-1')
        first = null
        second = null
        done()
    , 1500)


  it "streamed ping-pong-duplex-check", (done) ->
    @timeout(10000)

    firstContext = 'firstContext'
    secondContext = 'secondContext'
    {first:pongDuplex, second:pingDuplex} = test_utils.makeTwoConnectedDuplexes()

    first = new pingPongDuplexCheck.PingPongSingleHost(firstContext, secondContext)
    .spawn(pongDuplex)()
    second = new pingPongDuplexCheck.PingPongSingleHost(secondContext, firstContext)
    .spawn(pingDuplex)()

    first.timeStream.onValue((v)-> console.log('first.timeStream ' + JSON.stringify(v)))
    first.stateStream.onValue((v)-> console.log('first.timeStream ' + JSON.stringify(v)))
    second.timeStream.onValue((v)-> console.log('second.timeStream ' + JSON.stringify(v)))
    second.stateStream.onValue((v)-> console.log('second.timeStream ' + JSON.stringify(v)))

    first.start()
    second.start()

    setTimeout(
      ()->
        first.kill()
        second.kill()
        done()
    , 2000)

  it "should nest be tested", (done) ->
    @timeout(5000)
    firstContext = 'firstContext'
    secondContext = 'secondContext'

    firstContextNest = new pingPongDuplexCheck.PingPongNestHost(firstContext, secondContext)
    secondContextNest = new pingPongDuplexCheck.PingPongNestHost(secondContext, firstContext)

    p = firstContextNest.aggregateHostInfoProp.map(R.values()).map(R.pluck('state')).toProperty()
    p.log('firstContextNest')
    secondContextNest.aggregateHostInfoProp.map(R.values()).map(R.pluck('state')).log('secondContextNest')

    {first:firstDuplexInstOne, second:secondDuplexInstOne} = test_utils.makeTwoConnectedDuplexes(222, 222)
    {first:firstDuplexInstTwo, second:secondDuplexInstTwo} = test_utils.makeTwoConnectedDuplexes(222, 222)

    firstOne = firstContextNest.spawn(firstDuplexInstOne, 'One')
    firstTwo = firstContextNest.spawn(firstDuplexInstTwo, 'Two')
    secondOne = secondContextNest.spawn(secondDuplexInstOne, 'One')
    secondTwo = secondContextNest.spawn(secondDuplexInstTwo, 'Two')

    firstOne.start()
    secondOne.start()
    firstTwo.start()
    secondTwo.start()

    setTimeout(
      ()->
        getKefirPropCurrentValuePromise(p).then((v)->
          console.log(v)
          done()
        )

    , 2000)


  it "basic ping-pong-duplex-check", (done) ->
    {first:pongDuplex, second:pingDuplex} = test_utils.makeTwoConnectedDuplexes()

    pongDuplex.observe().each((v)->
      console.log('pongDuplex ' + JSON.stringify(v))
    )

    pingDuplex.observe().each((v)->
      console.log('pingDuplex ' + JSON.stringify(v))
    )

    p2 = pingPongDuplexCheck.sendAndCheckPromiseWithLog(pingDuplex, 2000, 'Context 1')
    p1 = pingPongDuplexCheck.receivePromiseWithLog(pongDuplex, 2000, 'Context 2')

    Q.all([p1, p2]).then(()-> done())

  it "check lines", (done)->
    {first:pongDuplex, second:pingDuplex} = test_utils.makeTwoConnectedDuplexes()

    createMyPostStation = MuxDemuxTelegraph.getDuplexLines({worker: 'W', backgroundPage: 'BGP'})
    pingDuplexStation = createMyPostStation(pingDuplex)
    pongDuplexStation = createMyPostStation(pongDuplex)

    pingDuplexStation.worker.observe().each((v)->
      console.log('pingDuplexStation.worker ' + JSON.stringify(v))
    )

    pongDuplexStation.worker.observe().each((v)->
      console.log('pongDuplexStation.worker ' + JSON.stringify(v))
    )

    p1 = pingPongDuplexCheck.receivePromiseWithLog(pongDuplexStation.worker, 2000, 'Context 1')
    p2 = pingPongDuplexCheck.sendAndCheckPromiseWithLog(pingDuplexStation.worker, 2000, 'Context 2')

    Q.all([p1, p2]).then(()-> done())