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

KSLuaUtils.lua

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

ADOBE CONFIDENTIAL
------------------
Copyright 2010 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.logging
local WFLogger = require 'WFLogger'

	-- WF.core.lua
local KSLuaErrors = require 'KSLuaErrors'


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

local logger = WFLogger 'KSLuaUtils'
--	:setLevel 'debug'

local WFDebugTraceback_traceback = require 'WFDebugTraceback'.traceback

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

local string_format = string.format

local debug = debug
local debug_print = debug.print


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

-- @mark KSLuaUtils namespace

local KSLuaUtils = {}

--------------------------------------------------------------------------------
-- Test the value to see whether it is true. If it is, just return it. If not,
-- then signal an error with a message constructed by running through string.format.
-- This has the benefit of avoiding constructing the error message until we
-- actually need it.

function KSLuaUtils.assertf( value, msg, ... )

	if value then
		return value
	end
	
	error( string_format( msg, ... ), 0 )

end

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

-- A version of assert with format and traceback support.

function KSLuaUtils.assertft( value, msg, ... )

	if value then
		return value
	end
	
	if select( '#', ... ) then
		msg = string_format( msg, ... )
	end
	
	debug_print( "Error: " .. msg )
	debug_print( WFDebugTraceback_traceback { "KSReactive.lua", "KSLuaUtils.lua", "WFTimer.lua", "in function 'pcall'", "KSPcallLoops.lua", "KSReactiveTimer.lua" } )
	error( msg, 2 )
	
end

--------------------------------------------------------------------------------
-- Implementation for the colon message send sugar. Useful if you need to send
-- a message that changes at runtime.

function KSLuaUtils.send( obj, msg, ... )

	return obj[ msg ]( obj, ... )

end

local send = KSLuaUtils.send

--------------------------------------------------------------------------------
-- Second half of dpcall. See below. Spits an error out to debug.print and then
-- proceeds as normal.

local function dpcall_finish( ident, success, ... )

	if not success and not KSLuaErrors.isCancelError( ( ... ) ) then
		logger:errorf( "Error in '%s': %s", ident, ... )
	end

	return success, ...
	
end

--------------------------------------------------------------------------------
-- The same as pcall, but in the event of a failure, it logs a message to that
-- effect before returning.

function KSLuaUtils.dpcall( ident, fn, ... )

	if not fn then
		local message = string_format( "No function for '%s'", tostring( ident ) )
		logger:error( message )
		return false, message
	end

	return dpcall_finish( ident, pcall( fn, ... ) )

end

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

local dpcall = KSLuaUtils.dpcall

--------------------------------------------------------------------------------
-- A protected version of KSLuaUtils.send which wraps the send operation in a
-- pcall.

function KSLuaUtils.psend( obj, msg, ... )

	return dpcall( msg, send, obj, msg, ... )

end

--------------------------------------------------------------------------------
-- Require a module only if it exists on package.path (to avoid the verbose error
-- output even a pcall()ed require emits on error)
--
-- Note that the calling semantics of requireIfExists() match pcall( require, ... )
-- rather than plain require( ... )
--
-- Returns: false, empty dict when module file does not exist
--          true, module dict when module file exists

function KSLuaUtils.requireIfExists( moduleName )

	local callerName = debug.getinfo(2,'S').source or "<>"
	local hasModuleName = false
	local moduleNamePath = ""
	local module = {}
	local path = package.path .. ";" .. package.cpath

	-- Replace ? in the package.path with the module name
	path = path:gsub("%?", moduleName)
	-- Split the package path into its components on the ; separator
	local paths = {}
	path:gsub("([^;]+)", function(c) paths[#paths+1] = c end)

	for _, file in ipairs(paths) do
		local f = io.open(file, "r")

		if f then
			hasModuleName = true
			moduleNamePath = file

			io.close(f)
		end
	end
	
	if not hasModuleName then
		local preloadedModule = rawget( package.preload, moduleName )
		
		if preloadedModule then
			hasModuleName = true
		end
	end

	-- Reproduce the calling semantics of pcall( require, ... ) even though the
	-- call should succeed at this point (the file might have an error in it)
	if hasModuleName then
		hasModuleName, module = pcall( require, moduleName )
	end

	if not hasModuleName then
		module = {}
		logger:infof( "%s: optional %s not found", callerName, moduleName )
	else
		logger:infof( "%s: %s loaded from %s", callerName, moduleName, moduleNamePath )
	end

	return hasModuleName, module
end

return KSLuaUtils
