/****************************************************************/
/**** PA6 Prelude: Case Classes *********************************/ 
/****************************************************************/

/* In Ocaml: 
   type jval = JStr of string 
             | JNum of int 
             | JObj of (string, jval) Map.t
             | JArr of jval list
*/ 

sealed abstract class JVal {
  // Can put methods in the class itself
  // [Sealed]: compiler knows these are the ONLY subclasses of JVal
  //           hence [this] MUST match one of these 4 patterns
  // Thus, ML ADT ~ OO Subtyping with pattern matching!
  override def toString : String = 
    this match {
      case JStr(s) => "\"%s\"".format(s)
      case JNum(n) => n.toString 
      case JObj(m) => m.map(p => p._1 + " : " + p._2.toString)
                       .mkString("{", ", ", "}")
      case JArr(a) => a.map(_.toString)
                       .mkString("{", ", ", "}")
    }
}

case class JStr(s: String)            extends JVal
case class JNum(n: BigDecimal)        extends JVal
case class JObj(o: Map[String, JVal]) extends JVal
case class JArr(a: List[JVal])        extends JVal

object JsonTest {

  val jv1 = JArr(List(JNum(1), JNum(2), JNum(3), JNum(4)))

  val jv2 = JArr(List(JObj(Map( "word" -> JStr("one")
                              , "num" -> JNum(1)))
                    , JObj(Map( "word" -> JStr("two")
                              , "num" -> JNum(2)))
                    , JObj(Map( "word" -> JStr("three")
                              , "num" -> JNum(3)))))

  // Can instead put functions over [JVal] in other places 
  def toString(jv: JVal) : String = 
    jv match {
      case JStr(s) => "\"%s\"".format(s)
      case JNum(n) => n.toString 
      case JObj(m) => m.map(p => p._1 + " : " + toString(p._2))
                       .mkString("{", ", ", "}")
      case JArr(a) => a.map(toString(_))
                       .mkString("{", ", ", "}")
    }
}
