/*
   Copyright 2010 Aaron J. Radke

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

       http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.
*/
package cc.drx

import scala.collection.JavaConverters._

object Json {

  def apply(input:Input):Json = apply(input.asString)
  def apply(json:String):Json = {
    val engine = new javax.script.ScriptEngineManager().getEngineByName("js")
    val eval = engine.eval(s"Java.asJSONCompatible($json)")
    new Json(eval)
  }
}

class Json private (private val eval:Any) extends StringMap{
  override def toString =
    (getInt orElse getLong orElse getDouble orElse getSeq orElse getMap orElse getString)
      .map{_.toString}
      .getOrElse("None")

  def getMap:Option[Map[String,Json]] = Some(eval).collect{
    case kvs:java.util.Map[_,_] =>  Java.toScala(kvs).map{case (k,v) => (k.toString -> new Json(v))}.toMap
  }
  def getSeq:Option[Seq[Json]] = Some(eval).collect{
    case xs:java.util.List[_] =>  Java.toScala(xs).toVector.map{v => new Json(v)}
  }
  def getDouble:Option[Double] = Some(eval).collect{ case x:Double => x }
  def getInt:Option[Int] = Some(eval).collect{ case x:Int => x }
  def getLong:Option[Long] = Some(eval).collect{ case x:Long => x}
  def getBoolean:Option[Boolean] = Some(eval).collect{ case x:Boolean => x }

  /**always return a string so string parse will work*/
  def getString:Option[String] = Some(eval).collect{
    case x:String => x
    case x:Int => x.toString
    case x:Long => x.toString
    case x:Double => x.toString
    case xs:List[_] => xs.map{_.toString}.mkString(",")
    case xs:Map[_,_] => xs.map{case (k,v) => "$k:$v"}.mkString(" ")
  }

  // def isNull:Boolean = !(eval != null)

  def keys:List[String] = getMap.map{_.keys.toList} getOrElse List()  //meet the string map contract

  //---derivative helper features
  //
  def getJson(key:Int):Option[Json] = getSeq flatMap {_ lift key}
  def getJson(key:String):Option[Json] = getMap flatMap {_ get key}

  def getMap(key:String):Option[Map[String,Json]] = getJson(key) flatMap {_.getMap}
  def getSeq(key:String):Option[Seq[Json]] = getJson(key) flatMap {_.getSeq}
  def getInt(key:String):Option[Int] = getJson(key) flatMap {_.getInt}
  def getLong(key:String):Option[Long] = getJson(key) flatMap {_.getLong}
  def getDouble(key:String):Option[Double] = getJson(key) flatMap {_.getDouble}
  def getString(key:String):Option[String] = getJson(key) flatMap {_.getString}
  // def getStringStrict(key:String):Option[String] = get(key) flatMap {_.getStringStrict}
  def getBoolean(key:String):Option[Boolean] = getJson(key) flatMap {_.getBoolean}


  def asMap:Map[String,Json] = getMap getOrElse Map()
  def asSeq:Seq[Json] = getSeq getOrElse Seq()

  def isMap:Boolean = getMap.isDefined
  def isSeq:Boolean = getSeq.isDefined
  def isInt:Boolean = getInt.isDefined
  def isLong:Boolean = getInt.isDefined
  def isDouble:Boolean = getDouble.isDefined
  def isString:Boolean = eval.isInstanceOf[String] //getStringStrict.isDefined
  def isBoolean:Boolean = getBoolean.isDefined
}