#  Licensed to the Apache Software Foundation (ASF) under one
#  or more contributor license agreements.  See the NOTICE file
#  distributed with this work for additional information
#  regarding copyright ownership.  The ASF licenses this file
#  to you 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.

# JSON data conversion functions

try:
    import json
except:
    from django.utils import simplejson as json

from StringIO import StringIO
from util import *
from elemutil import *

# Return true if a list represents a JS array
def isJSArray(l):
    if isNull(l):
        return True
    v = car(l)
    if isSymbol(v):
        return False
    if isList(v):
        if not isNull(v) and isSymbol(car(v)):
            return False
    return True

# Converts JSON properties to values
def jsPropertiesToValues(propertiesSoFar, o, i):
    if isNull(i):
        return propertiesSoFar
    p = car(i)
    jsv = o[p]
    v = jsValToValue(jsv)

    if isinstance(p, basestring):
        n = str(p)
        if n[0:1] == "@":
            return jsPropertiesToValues(cons((attribute, "'" + n[1:], v), propertiesSoFar), o, cdr(i))
        if isList(v) and not isJSArray(v):
            return jsPropertiesToValues(cons(cons(element, cons("'" + n, v)), propertiesSoFar), o, cdr(i))
        return jsPropertiesToValues(cons((element, "'" + n, v), propertiesSoFar), o, cdr(i))
    return jsPropertiesToValues(cons(v, propertiesSoFar), o, cdr(i))

# Converts a JSON val to a value
def jsValToValue(jsv):
    if jsv is None:
        return None
    if isinstance(jsv, dict):
        return jsPropertiesToValues((), jsv, tuple(jsv.keys()))
    if isList(jsv):
        return jsPropertiesToValues((), jsv, tuple(reversed(range(0, len(jsv)))))
    if isinstance(jsv, basestring):
        return str(jsv)
    return jsv

# Return true if a list of strings contains a JSON document
def isJSON(l):
    if isNull(l):
        return False
    s = car(l)[0:1]
    return s == "[" or s == "{"

# Convert a list of strings representing a JSON document to a list of values
def readJSON(l):
    s = StringIO()
    writeStrings(l, s)
    val = json.loads(s.getvalue())
    return jsValToValue(val)

# Convert a list of values to JSON array elements
def valuesToJSElements(a, l, i):
    if isNull(l):
        return a
    pv = valueToJSVal(car(l))
    a[i] = pv
    return valuesToJSElements(a, cdr(l), i + 1)
    
# Convert a value to a JSON value
def valueToJSVal(v):
    if not isList(v):
        return v
    if isJSArray(v):
        return valuesToJSElements(list(range(0, len(v))), v, 0)
    return valuesToJSProperties({}, v)

# Convert a list of values to JSON properties
def valuesToJSProperties(o, l):
    if isNull(l):
        return o
    token = car(l)
    if isTaggedList(token, attribute):
        pv = valueToJSVal(attributeValue(token))
        o["@" + attributeName(token)[1:]] = pv
    elif isTaggedList(token, element):
        if elementHasValue(token):
            pv = valueToJSVal(elementValue(token))
            o[elementName(token)[1:]] = pv
        else:
            child = {}
            o[elementName(token)[1:]] = child
            valuesToJSProperties(child, elementChildren(token))
    return valuesToJSProperties(o, cdr(l))

# Convert a list of values to a list of strings representing a JSON document
def writeJSON(l):
    if isJSArray(l):
        jsv = valuesToJSElements(list(range(0, len(l))), l, 0)
    else:
        jsv = valuesToJSProperties({}, l)
    s = json.dumps(jsv, separators=(',',':'))
    return (s,)

# Convert a list + params to a JSON-RPC request
def jsonRequest(id, func, params):
    r = (("'id", id), ("'method", func), ("'params", params))
    return writeJSON(valuesToElements(r))

# Convert a value to a JSON-RPC result
def jsonResult(id, val):
    return writeJSON(valuesToElements((("'id", id), ("'result", val))))

# Convert a JSON-RPC result to a value
def jsonResultValue(s):
    jsres = readJSON(s)
    res = elementsToValues(jsres)
    val = cadr(assoc("'result", res))
    if isList(val) and not isJSArray(val):
        return (val,)
    return val

# Return a portable function name from a JSON-RPC function name
def funcName(f):
    if f.startswith("."):
        return f[1:]
    if f.startswith("system."):
        return f[7:]
    if f.startswith("Service."):
        return f[8:]
    return f

