cluster = require("cluster")
winston = require("winston")
http = require("http")
util = require("util")
_ = require("underscore")

logger = new (winston.Logger)({
    transports: [
      new (winston.transports.Console)({'timestamp':true})
    ]
})

logger.info "PrWrkr##{cluster.worker.id}: online, waiting for input"

# holds timeout
reporter = {}

# cluster id - holds id of master
clusterId = ""


# handle message, specifically "targeting"
cluster.worker.on(
  "message"
  (msg) -> 
    switch msg.subject
      when "targeting"
        logger.info "PrWorker##{cluster.worker.id}: targeting received (http://#{msg.server}:#{msg.port} w #{msg.user}:#{msg.password}), hitting #{msg.targeting.length} targets, pause set to #{msg.pause}"
        
        http.globalAgent.maxSockets = 1024

        clusterId = msg.clusterId
        # start hitting targets

        hit(msg.server, msg.port, msg.user, msg.password, msg.targeting, msg.pause)
        # report back to master
        reporter = report(msg.reportinterval || 10000)
)


# report is an array containing all results which have not yet been shipped to master
hits = []
hitcount = 0

hit = (server, port, user, password, targets, pause = 10) ->


  # select a target, targets most used functions
  r = Math.random()*100
  #logger.debug "r at #{r}, looking at", targets

  target = _.find(targets, ((t) -> r < t.acc_percentage))

  # if no target can be found then reschedule a hit
  if not target? 
    setTimeout(
        ->
          hit(server, port, user, password, targets, pause)
        pause # this should be 0 for prod
    )
    return

  #logger.debug "#{r} hitting", target
  #logger.debug "PrWrkr##{cluster.worker.id}: hit #{hitcount++} at http://#{user}:#{password}@#{server}:#{port}/#{target.path}"
  # record time
  t0 = Date.now()

  # do request
  req = http.get({
      host: server
      port: port
      headers: target?.headers
      auth: "#{user}:#{password}"
      # method: "GET" # default
      path: "/#{target?.path}"
    }, 
    (res) ->

      logger.debug "PrWrkr##{cluster.worker.id}: hit w status #{res.statusCode}"
      
      # etag
      target?.headers =
        "If-None-Match" : res.headers["etag"] || target?.headers?["If-None-Match"]
        "Connection": "Keep-Alive"

      hits.push({
        type: "response"
        path: target?.path
        status: res.statusCode
        at: Date.now()
        time: Date.now() - t0
        worker: cluster?.worker?.id
        cluster: clusterId
      })

      # continue
      #process.nextTick( -> hit(server, port, user, password, targets))
      setTimeout(
        ->
          hit(server, port, user, password, targets, pause)
        pause # this should be 0 for prod
      )
  )
  req.on(
    "error",
    (e) -> 
      #logger.debug "PrWrkr##{cluster.worker.id}: miss!"
      hits.push({
        type: "error"
        msg: e.message
        path: target?.path
        time: Date.now() - t0
      })
      # continue
      setTimeout(
        -> hit(server, port, user, password, targets, pause)
        pause
      )
  )
  req.end()


report = (interval) ->

  if hits.length > 0

    logger.debug "PrWrkr##{cluster.worker.id}: sending report to master (#{hits.length} hits)"

    try 
      # send report to cluster master
      cluster.worker.send(
        subject: "report"
        report:
          hits: hits || []
      )
    
      # reset hits
      hits = []
    catch err
      logger.error "PrWrkr##{cluster.worker.id}: Could not report to master."

  # if interval > 0 then start loop
  if interval > 0 then reporter = setTimeout((->report(interval)),interval)


# handle disconnect
cluster.worker.on(
  "disconnect",
  ->
    # cancel existing report
    clearTimeout(reporter)
    # report remaining
    report(0)
    logger.info "report, disconnect"
    process.exit(0)
)





