package ru.yandex.atom.service

import com.typesafe.config.Config
import akka.actor.{Props, Actor}
import java.util.concurrent.Executors
import concurrent._
import java.io.{File, PrintWriter}
import scala.util.control.NonFatal
import ru.yandex.atom.utils.actor.AtomActorLogging
import scala.io.Source

/**
 * @author avhaliullin
 */
trait GeminiActorBinaryComponentImpl extends GeminiActorComponent {
  component =>

  class GeminiActor(config: GeminiConfig) extends Actor with AtomActorLogging {

    import GeminiRequest._
    import GeminiResponse._

    implicit val pool = ExecutionContext.fromExecutor(Executors.newFixedThreadPool(config.poolSize))

    var requests: Long = 0

    override def receive = {
      case req@Ask(id, tpe, urls) =>
        val client = sender
        future {
          requests += 1
          val fileName = config.workDir + "/gemini-urls-" + self.path.name + requests

          val pw = new PrintWriter(fileName)
          urls.foreach(pw.println)
          pw.close()

          // workers взял на глаз
          val workers = math.max(1, math.min(50, urls.size / 10))
          try {
            val proc = Runtime.getRuntime.exec(Array(
              config.clientPath,
              "-4",
              "-u", config.userName,
              "-w", workers.toString,
              "-t", tpe.value,
              "-f", fileName,
              "--format", "text",
              "--quota", config.rpsLimit.toString))

            val result = Source.fromInputStream(proc.getInputStream).getLines().map {
              line =>
                val splits = line.split("\t")
                if (splits.size < 2) {
                  log.error("Bad line in gemini client response: " + line)
                  "" -> ""
                } else {
                  splits(0) -> splits(1)
                }
            }.toMap

            client ! Answer(req, result)
          } catch {
            case NonFatal(e) =>
              log.error(e, "Failed to invoke gemini client")
              client ! FailureResponse(req, e)
          } finally {
            new File(fileName).delete()
          }
        }
    }
  }

  object GeminiActor {
    def props(config: GeminiConfig) = Props(classOf[GeminiActor], component, config)
  }

  object GeminiConfig {
    implicit def apply(config: Config): GeminiConfig = new GeminiConfig(
      clientPath = config.getString("clientPath"),
      userName = config.getString("userName"),
      rpsLimit = config.getInt("rpsLimit"),
      poolSize = config.getInt("poolSize"),
      workDir = config.getString("workDir")
    )
  }

  case class GeminiConfig(clientPath: String, userName: String, rpsLimit: Int, poolSize: Int,
                          workDir: String)

}
