package org.zalando.benchmarks

import akka.actor._
import akka.pattern.{ask, pipe}
import akka.routing.BalancingPool
import akka.util.Timeout
import org.zalando.benchmarks.ComputationFollowedByAsyncPublishing._

import scala.concurrent.{Await, Future}
import scala.concurrent.duration._

class Actors(system: ActorSystem) {
  def benchmark(coreFactor: Int): Unit = {
    import system.dispatcher
    implicit val timeout = Timeout(1 hour)

    // Route computations through a balanced pool of (cpu bound) computation workers.
    val router = system actorOf BalancingPool(numWorkers(coreFactor)).props(Props[ComputeActor])

    try {
      // Collect the results, sum them up and print the sum.
      printResult(Await.result(Future.traverse(1 to numTasks map Job) { job =>
        (router ? job).mapTo[PublishResult]
      }, 1 hour))

    } finally {
      // Shut down the actors.
      router ! PoisonPill
    }
  }
}

// Actor responsible for the computation, and for delegating to the publishing actor(s).
class ComputeActor extends Actor {
  val publisher = context actorOf Props[PublishActor]

  def receive = {
    case job: Job =>
      // tell the publisher about who sent us the job, and the job results
      val s = sender()
      publisher ! (s, Computer compute job)
  }
}

// Actor responsible for publishing, and for sending the response back.
class PublishActor extends Actor {
  import context.dispatcher

  def receive = {
    case (s: ActorRef, r: JobResult) =>
      // just pipe the result back to the original sender
      Publisher.publish(r, context.system) pipeTo s
  }
}