package net.liftweb.util;

/*
 * Copyright 2008 WorldWide Conferencing, LLC
 *
 * 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.
 */

import _root_.scala.util.parsing.combinator.{Parsers, ImplicitConversions}
import Helpers._

object JSONParser extends SafeSeqParser with ImplicitConversions {
  implicit def strToInput(in: String): Input = new _root_.scala.util.parsing.input.CharArrayReader(in.toCharArray)
  type Elem = Char

  def parse(in: String): Box[Any] = theValue(in) match {
    case Success(v, _) => Full(v)
    case x => Empty
  }

  lazy val whitespace = elem(' ') | elem('\t') | elem('\n') | elem('\r')

  lazy val spaces = rep(whitespace)

  lazy val jsonObject: Parser[Map[String, Any]] = ( spaces ~ '{' ~ spaces ~> members <~ spaces ~ '}' ~ spaces ^^ {case xs =>
        Map(xs :_*)
    } )  |
  spaces ~'{' ~ spaces ~ '}' ~ spaces  ^^ {case _ => Map.empty}

  lazy val members = rep1sep(pair, spaces ~ ',' ~ spaces)

  lazy val pair: Parser[(String, Any)] = (string | pairId) ~ spaces ~ ':' ~ spaces ~ theValue ^^ {case s ~ _ ~ _ ~ _ ~ v => (s,v)}

  def pairChar(in: Char): Boolean = in.isLetter || in.isDigit || in == '_'

  lazy val pairId: Parser[String] = rep1(elem("pairChar", pairChar)) ^^ {case s => s.mkString}

  lazy val string: Parser[String] = ('\'' ~> rep(not('\'') ~> achar) <~ '\'' ^^ {case xs => xs.mkString("")}) |
  ('"' ~> rep(not('"') ~> achar) <~ '"' ^^ {case xs => xs.mkString("")})

  lazy val achar = ('\\' ~> ('"' ^^ {case _ => '"'} |
                             '\\' ^^ {case _ => '\\'} |
                             '/' ^^ {case _ => '/'} |
                             'b' ^^ {case _ => '\b'} |
                             'n' ^^ {case _ => '\n'} |
                             'r' ^^ {case _ => '\r'} |
                             't' ^^ {case _ => '\t'} |
                             'u' ~> repN(4, hexDigit) ^^ {case dg => Integer.parseInt(dg.mkString(""), 16).toChar})) | (elem("any char", c => c != '"' && c >= ' '))

  lazy val number: Parser[Double] =  intFracExp | intFrac | intExp |  (anInt ^^ {case n => n.toDouble})

  lazy val exp = e ~ digits ^^ {case x ~ d => d.mkString("").toInt * x}

  lazy val e = ('e' ~ '-' ^^ {case _ => -1}) | ('e' ~ '+' ^^ {case _ => 1}) | ('e' ^^ {case _ => 1}) |
  ('E' ~ '-' ^^ {case _ => -1}) | ('E' ~ '+' ^^ {case _ => 1}) | ('E' ^^ {case _ => 1})

  lazy val intFracExp: Parser[Double] = anInt ~ frac ~ exp ^^ {case i ~ f ~ exp => ((i.toString+"."+f+"e"+exp).toDouble)}

  lazy val intFrac = anInt ~ frac ^^ {case i ~ f => ((i.toString+"."+f).toDouble)}

  lazy val intExp = anInt ~ exp ^^ {case i ~ e => ((i.toString+"e"+e).toDouble)}

  lazy val anInt = (digit19 ~ digits ^^ {case x ~ xs => (x :: xs).mkString("").toLong}) |
  (digit ^^ {case x => x.toString.toLong}) | ('-' ~ digit19 ~ digits ^^ {case x ~ xs => (x :: xs).mkString("").toLong * -1L}) |
  ('-' ~ digit ^^ {case x => x.toString.toLong * -1L})

  lazy val digit19 = elem("digit", c => c >= '1' && c <= '9')

  lazy val digit = elem("digit", c => c >= '0' && c <= '9')

  lazy val hexDigit = elem("hex digit", c => (c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F'))

  lazy val digits = rep1(digit)

  lazy val frac = '.' ~ digits

  lazy val theValue: Parser[Any] = string | number | jsonObject | array | istrue | isfalse | isnull

  lazy val array: Parser[List[Any]] = spaces ~ '[' ~ spaces ~> elements <~ spaces ~ ']' ~ spaces ^^ {case e => e}

  lazy val elements = repsep(theValue, spaces ~ ',' ~ spaces)

  lazy val istrue: Parser[Boolean] = acceptSeq("true") ^^ {case _ => true}
  lazy val isfalse: Parser[Boolean] = acceptSeq("false") ^^ {case _ => false}
  lazy val isnull = acceptSeq("null") ^^ {case _ => Empty}
}