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

KSJsonDecoderTestWorker.lua

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

ADOBE CONFIDENTIAL
------------------
Copyright 2018 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.

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

	-- WF.core.self_test
	-- 

local WFRequestDispatcher = require 'WFRequestDispatcher'
local WFFunctionUtils = require "WFFunctionUtils" 
local WFSQLiteDatabase = require 'WFSQLiteDatabase'
local WFSQLiteDatabaseSchema = require 'WFSQLiteDatabaseSchema'

local Rx = require 'Rx'

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

local _schema = WFSQLiteDatabaseSchema( '1.0' )

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

local function makeMemoryDbWithFatalErrorHook()
	-- Constructing a WFSQLiteDatabase causes the underlying
	-- WFSQLiteDatabaseImp to be passed to WFSQLiteCorruptionLogger.addDb
	-- which in turn calls wfSQLiteDatabaseImp:setFatalErrorHook( f ) and
	-- that results in a native code call
	-- 
	--   errHookRef = luaL_ref( L, LUA_REGISTRYINDEX );
	--   
	--   
	--  which does
	-- 
	--  LUA_REGISTRY[ errHookRef ] = f

	return WFSQLiteDatabase( ":memory:", _schema )

end

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

local _luaRefUsageDatas = {}

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

local _luaRefUsageDataRetainId

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

local function retainLuaRefUsageData( luaRefUsageData )

	_luaRefUsageDataRetainId = ( _luaRefUsageDataRetainId or 0 ) + 1

	_luaRefUsageDatas[ _luaRefUsageDataRetainId ] = luaRefUsageData

	return _luaRefUsageDataRetainId

end

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

local function releaseLuaRefUsageData( luaRefUsageDataRetainId )

	local luaRefUsageData = _luaRefUsageDatas[ luaRefUsageDataRetainId ]

	_luaRefUsageDatas[ luaRefUsageDataRetainId ] = nil

	return luaRefUsageData

end

--------------------------------------------------------------------------------
-- useNLuaRefs causes 
-- 
--   luaL_ref( L, LUA_REGISTRYINDEX );
--   
-- to be called at least n times and ensures that the lua refs returned are
-- not sent to luaL_unref() until cleanUpLuaRefUsages() is called. 
-- 
-- useNLuaRefs returns an opaqueId. If opaqueId is sent back to this thread
-- via 
-- 
-- 	cleanUpLuaRefUsages( opaqueId )
-- 	
-- the code will clean up any data allocated on this thread by this call
-- to useNLuaRefs(...)

local function useNLuaRefs( n, luaRefUsageData )

	if n == 0 then

		local luaRefUsageDataRetainId = 
				retainLuaRefUsageData( luaRefUsageData )

		-- return the opaque id
		return luaRefUsageDataRetainId

	end

	-- otherwise set up a db with a fatal error hook.
	-- 
	-- Setting up the db has the side effect of calling
	-- 
	--   int hookIndex = luaL_ref( L, LUA_REGISTRYINDEX );
	--   
	-- and storing the fatal error hook lua function in the global registry index at
	-- hookIndex.
	-- 
	-- if lua_unref is not called, luaL_ref(..) emits an increasing sequence of ints 
	-- 
	-- Therfore we are effectively doing
	-- 
	-- _registry[ 1 ] = function() end
	-- ...
	-- _registry[ i ] = function() end
	-- ...
	-- _registry[ n ] = function() end
	-- 
	luaRefUsageData = luaRefUsageData or {}

	luaRefUsageData[ #luaRefUsageData + 1 ] = makeMemoryDbWithFatalErrorHook()

	-- now recurse
	return useNLuaRefs( n - 1, luaRefUsageData )

end

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

local function closeDb( db )

	db:close()

end

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

local function cleanUpLuaRefUsages( luaRefUsageDataRetainId )

	-- look up the data associated with luaRefUsageDataRetainId
	local luaRefUsageData = releaseLuaRefUsageData( luaRefUsageDataRetainId )

	if type( luaRefUsageData ) == "table" then

		-- clean it up by closing all fo the dbs with fatal hooks

		WFFunctionUtils.map( closeDb, luaRefUsageData )

	end

	return Rx.Observable.empty()

end

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

local KSJsonDecoder

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

local function testStripNull()

	if not KSJsonDecoder then

		-- Now load KSJsonDecoder for the first time on this thread. This will
		-- then load rapidjson which in turn will call
		-- 
		--		values::nullref = luaL_ref(L, LUA_REGISTRYINDEX); // [rapidjson]
		--		
		-- If we passed X to useNLuaRefs, then values::nullRef is probably going to 
		-- be set to X + 1.
		--		
		-- However, values::nullref is a static C++ variable, so now values::nullref
		-- is set to X + 1 on all threads that use rapidjson!

		KSJsonDecoder = require "KSJsonDecoder"

	end

	-- if another thread has clobbered this threads original values::nullref, then
	-- this thread has lost its ability to identify json_null and strip will fail.
	
	local original = '{ "nonNull": true, "null": null }'
	
	local decoded = KSJsonDecoder.decodeWebSafeJson_stripNull( original )
	
	if decoded.null ~= nil then

		error( string.format( "decoded.null was '%s', not nil as expected",
								decoded.null ) )

	end

	return Rx.Observable.empty()

end

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

local function initMessageProcessor()

	WFRequestDispatcher.onMessage( 'useNLuaRefs', useNLuaRefs )

	WFRequestDispatcher.onMessage( 'cleanUpLuaRefUsages', cleanUpLuaRefUsages )

	WFRequestDispatcher.onMessage( 'testStripNull', testStripNull )

end

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

return {

	initMessageProcessor = initMessageProcessor

}
