package lc.core

import scala.io.Source.fromFile
import org.json4s.{DefaultFormats, JValue, JObject, JField, JString}
import org.json4s.jackson.JsonMethods.{parse, render, pretty}
import org.json4s.JsonDSL._
import java.io.{FileNotFoundException, File, PrintWriter}

object Config {

  implicit val formats: DefaultFormats.type = DefaultFormats

  private val configFilePath = "data/config.json"
  private val configString =
    try {
      val configFile = fromFile(configFilePath)
      val configFileContent = configFile.mkString
      configFile.close
      configFileContent
    } catch {
      case _: FileNotFoundException =>
        val configFileContent = getDefaultConfig()
        val configFile = new PrintWriter(new File(configFilePath))
        configFile.write(configFileContent)
        configFile.close
        configFileContent
    }

  private val configJson = parse(configString)

  val port: Int = (configJson \ AttributesEnum.PORT.toString).extract[Int]
  val throttle: Int = (configJson \ AttributesEnum.THROTTLE.toString).extract[Int]
  val seed: Int = (configJson \ AttributesEnum.RANDOM_SEED.toString).extract[Int]
  val captchaExpiryTimeLimit: Int = (configJson \ AttributesEnum.CAPTCHA_EXPIRY_TIME_LIMIT.toString).extract[Int]
  val threadDelay: Int = (configJson \ AttributesEnum.THREAD_DELAY.toString).extract[Int]

  private val captchaConfigJson = (configJson \ "captchas")
  val captchaConfigTransform: JValue = captchaConfigJson transformField {
    case JField("config", JObject(config)) => ("config", JString(config.toString))
  }
  val captchaConfig: List[CaptchaConfig] = captchaConfigTransform.extract[List[CaptchaConfig]]
  val allowedLevels: Set[String] = getAllValues(configJson, ParametersEnum.ALLOWEDLEVELS.toString)
  val allowedMedia: Set[String] = getAllValues(configJson, ParametersEnum.ALLOWEDMEDIA.toString)
  val allowedInputType: Set[String] = getAllValues(configJson, ParametersEnum.ALLOWEDINPUTTYPE.toString)

  private def getAllValues(config: JValue, param: String): Set[String] = {
    val configValues = (config \\ param)
    val result = for {
      JObject(child) <- configValues
      JField(param) <- child
    } yield (param)

    var valueSet = Set[String]()
    for (valueList <- result) {
      for (value <- valueList._2.children) {
        valueSet += value.values.toString
      }
    }
    valueSet
  }

  private def getDefaultConfig(): String = {
    val defaultConfigMap = 
      (AttributesEnum.RANDOM_SEED.toString -> 20) ~
      (AttributesEnum.PORT.toString -> 8888) ~
      (AttributesEnum.CAPTCHA_EXPIRY_TIME_LIMIT.toString -> 5) ~
      (AttributesEnum.THROTTLE.toString -> 10) ~
      (AttributesEnum.THREAD_DELAY.toString -> 2) ~
      ("captchas" -> List(
        (
          (AttributesEnum.NAME.toString -> "FilterChallenge") ~
          (ParametersEnum.ALLOWEDLEVELS.toString -> List("medium", "hard")) ~
          (ParametersEnum.ALLOWEDMEDIA.toString -> List("image/png")) ~
          (ParametersEnum.ALLOWEDINPUTTYPE.toString -> List("text")) ~
          (AttributesEnum.CONFIG.toString -> JObject())
        ),
        (
          (AttributesEnum.NAME.toString -> "GifCaptcha") ~
          (ParametersEnum.ALLOWEDLEVELS.toString -> List("hard")) ~
          (ParametersEnum.ALLOWEDMEDIA.toString -> List("image/gif")) ~
          (ParametersEnum.ALLOWEDINPUTTYPE.toString -> List("text")) ~
          (AttributesEnum.CONFIG.toString -> JObject())
        ),
        (
          (AttributesEnum.NAME.toString -> "ShadowTextCaptcha") ~
          (ParametersEnum.ALLOWEDLEVELS.toString -> List("easy")) ~
          (ParametersEnum.ALLOWEDMEDIA.toString -> List("image/png")) ~
          (ParametersEnum.ALLOWEDINPUTTYPE.toString -> List("text")) ~
          (AttributesEnum.CONFIG.toString -> JObject())
        ),
        (
          (AttributesEnum.NAME.toString -> "RainDropsCaptcha") ~
          (ParametersEnum.ALLOWEDLEVELS.toString -> List("easy", "medium")) ~
          (ParametersEnum.ALLOWEDMEDIA.toString -> List("image/gif")) ~
          (ParametersEnum.ALLOWEDINPUTTYPE.toString -> List("text")) ~
          (AttributesEnum.CONFIG.toString -> JObject())
        )
      ))

    pretty(render(defaultConfigMap))
  }

}
