--[[----------------------------------------------------------------------------

KSJsonDecoder.lua

--------------------------------------------------------------------------------

ADOBE CONFIDENTIAL
------------------
Copyright 2011 Adobe Systems Incorporated
All Rights Reserved.

NOTICE: All information contained herein is, and remain the property of
Adobe Systems Incorporated and its suppliers, if any. The intellectual and
technical concepts contained herein are proprietary to Adobe Systems
Incorporated and its suppliers and may be covered by U.S. and Foreign
Patents, patents in process, and are protected by trade secret or copyright
law.  Dissemination of this information or reproduction of this material is
strictly forbidden unless prior written permission is obtained from
Adobe Systems Incorporated.

--------------------------------------------------------------------------------

Q&D JSON decoder for interacting with prototype ps.com server.

------------------------------------------------------------------------------]]

	-- core.lua
local KSData = require 'KSData'

local rapidjson = require 'rapidjson'

local json_null = rapidjson.null

local logger = require 'WFLogger'( 'KSJsonDecoder' )

--============================================================================--

-- @mark KSJsonDecoder namespace

--[[!
	\class KSJsonDecoder

	\brief This namespace provides translation utilities between JSON-encoded
		REST request contents and the Lua structures used by the LUA API calls.
		These functions are typically used as the translation functions for
		\c mapE or \c mapB calls.
]]--

local KSJsonDecoder = {
	json_null = json_null
}

--------------------------------------------------------------------------------

local type = type
local tostring = tostring

--------------------------------------------------------------------------------

local function postProcessJsonDecodeResult( t, stripNull )
	
	if type( t ) ~= 'table' then
		return t
	end
	
	-- rapidjson adds metatables to each lua table to denote whether it was
	-- a JSON array - [] - or a JSON object - {}. This isn't a distinction 
	-- we make much in the Oz data model where JSON objects are the default and
	-- arrays are rare and never empty. So we will strip out this metatable data 
	-- so that the resulting table is serializable within the client. However, 
	-- this does mean that we can't roundtrip empty JSON arrays with the 
	-- KSJSonDecoder/KSJsonEncoder.  Instead JSON [] -> lua {} -> JSON {}. Given 
	-- the way that the Oz data model is structured, this is currently reasonable.
	setmetatable( t, nil )
	
	for k, v in pairs( t ) do
		
		if stripNull and v == json_null then
			t[ k ] = nil
		elseif type( v ) == 'table' then
			postProcessJsonDecodeResult( v, stripNull )
		end
	
	end
	
	return t

end
	
--------------------------------------------------------------------------------

local function decode( json, stripNull, ignoreEmpty )
	
	-- Convert KSData to string if needed.
	if KSData.isData( json ) then
		json = tostring( json )
	end
	
	-- Still not a string? Just pass through unaffected.
	if type( json ) ~= 'string' then
		return json
	end
	
	if #json == 0 then
		if not ignoreEmpty then
			debug.print( "attempted to parse empty JSON string at ", debug.traceback() )
		end
		return {}
	end
	
	-- Strip the while(1) silliness if it's present.
	json = json:gsub( "^%s*while%s*%(%s*1%s*%)%s*%{%}%s*", "", 1 )
	
	logger:tracef( "Decoding:\n %s", json )
	-- Convert to Lua.
	local result = rapidjson.decode( json )
	
	postProcessJsonDecodeResult( result, stripNull )
	
	if not result then
		error( "invalid JSON data" )
	end
	
	return result
	
end

--------------------------------------------------------------------------------
--[[!
	\brief Translates a JSON string into the equivalent Lua data structure.
	
	\param json  The JSON string.
	\param ignoreEmpty (boolean) When true, ignores empty string. Otherwise,
		prints a debugging warning when it encounters an empty string in the
		source JSON string.

     \return The Lua table.
]]--

function KSJsonDecoder.decodeWebSafeJson( --[[string]] json,
										  --[[boolean]] ignoreEmpty )

	return decode( json, --[[stripNull]] false, ignoreEmpty )
	
end

--------------------------------------------------------------------------------
--[[!
	\brief Translates a JSON string into the equivalent Lua data structure,
		converting any JSON NULL values to Lua nil.
	
	\param json (string) The JSON string.
	
	\return The Lua table.
]]--

function KSJsonDecoder.decodeWebSafeJson_stripNull( --[[string]] json )

	return decode( json, --[[stripNull]] true, --[[ignoreEmpty]] false )
	
end

--------------------------------------------------------------------------------
--[[!
	\brief Translates a JSON string into the equivalent Lua data structure,
		converting any JSON NULL values to Lua nil, but ignoring empty body text.
	
	Empty body text can happen when an HTTP request encounters errors.
	
	\param json The JSON string.
	
	\return The Lua table.
]]--

function KSJsonDecoder.decodeWebSafeJson_stripNull_ignoreEmpty( --[[string]] json )

	return decode( json, --[[stripNull]] true, --[[ignoreEmpty]] true )
	
end

--------------------------------------------------------------------------------
--[[!
	\brief This mapping function can be used as the only argument to
		\c :mapP on an \c WFHttpClient.getP  or \c WFHttpClient.putP result.
		It transforms the HTTP body from a JSON string into the equivalent Lua
		data structure, converting any JSON NULL values to Lua nil.
	
	\param status A status value that identifies the type of the promise result,
		"error" or "data". If the status is "error", it is passed through along
		with its parameters.
	\param json The JSON string.
	\param ... Addtional parameters of an error result to pass through.
	
	\return The status value, the Lua table, and additional pass-through parameters
		for an error.
]]--

function KSJsonDecoder.decodeWebSafeJson_stripNullP( --[[string]] status,
													 --[[string]] json,
													 ... )

	if status == 'data' then

		local result = decode( json, --[[stripNull]] true, --[[ignoreEmpty]] false )
		
		return 'data', result, ...

	else
		
		return status, json, ...
	
	end
	
end

--------------------------------------------------------------------------------
--[[!
	\brief This mapping function can be used as the only argument to \c :mapP
		on an \c WFHttpClient.getP or \c WFHttpClient.putP result. It transforms
		the HTTP body from a JSON string into the equivalent Lua data structure,
		converting any JSON NULL values to Lua nil, but ignoring empty body text.
	
	Empty body text can happen when an HTTP request encounters errors.
	
	\param status A status value that identifies the type of the promise result,
		"error" or "data". If the status is "error", it is passed through along
		with its parameters.
	\param json The JSON string.
	\param ... Addtional parameters of an error result to pass through.
	
	\return The status value, the Lua table, and additional pass-through
		parameters for an error.
]]--

function KSJsonDecoder.decodeWebSafeJson_stripNull_ignoreEmptyP( --[[string]] status,
																 --[[string]] json,
																 ... )

	if status == 'data' then

		local result = decode( json, --[[stripNull]] true, --[[ignoreEmpty]] true )
		
		return 'data', result, ...
	
	else
	
		return status, json, ...
	
	end
	
end

--------------------------------------------------------------------------------

return KSJsonDecoder
