-- Developed by MAHN.quifi, 2010
local table = table
local error = error
local setmetatable = setmetatable
local dofile = dofile
local tostring = tostring
local type = type
local error = error
local stringbyte = string.byte

module "luad"

local here = _M

local function uniName (file)
	-- unify file name
	local name = file
	
	local count
	
	name = name:gsub( "%\\", "%/" )		-- "\" to "/"
	
	repeat		-- remove "xxx/../"
		name, count = name:gsub( "[^%/]+%/%.%.%/", "" )
	until count == 0
	
	repeat		-- remove "./"
		name, count = name:gsub( "%/%.%/", "%/" )
	until count == 0
	
	name = name:gsub( "^.%/", "" )		-- remove "." at start 
	name = name:gsub( "%/%.$", "" )		-- remove "." at end
	
	return name
end
here.uniName = uniName

-- filename should be unified
local function pathPart (file)
	local i = #file
	local char = stringbyte("/")
	while i>=1 and stringbyte(file, i)~=char do
		i = i-1
	end
	
	if i >= 1 then
		return file:sub( 1, i )
	else
		return ""		-- current path
	end
end

-- currentPaths
local currentPaths = {""}

function getCurPath ()
	return currentPaths[#currentPaths]
end

-- rootPath
local rootPath = ""

function setRootPath (path)
	local realPath = uniName(path)
	if #realPath > 0 and realPath:sub(#realPath) ~= "/" then
		realPath = realPath.."/"
	end
	rootPath = realPath
	currentPaths[1] = rootPath
end

function getRootPath ()
	return rootPath
end

-- defaultLoader
local defaultLoader = dofile
function setDefaultLoader (loader)
	defaultLoader = loader
end

-- do action to file in current path
function incur (filename, action, ...)
	local filepath = uniName( currentPaths[#currentPaths]..filename )
	currentPaths[#currentPaths+1] = pathPart( filepath )
	
	local res1, res2, res3
	if type(action) == "string" then
		res1, res2, res3 = here[action](filepath , ...)
	else
		res1, res2, res3 = (action or defaultLoader)(filepath , ...)
	end
	
	currentPaths[#currentPaths] = nil
	return res1, res2, res3
end

-- do action to file in root path
function inroot (filename, action, ...)
	local filepath = uniName( rootPath..filename )
	currentPaths[#currentPaths+1] = pathPart( filepath )
	
	local res1, res2, res3
	if type(action) == "string" then
		res1, res2, res3 = here[action](filepath , ...)
	else
		res1, res2, res3 = (action or defaultLoader)(filepath , ...)
	end
	
	currentPaths[#currentPaths] = nil
	return res1, res2, res3
end

-- shortcut to incur
function __call (self, file, ...)
	return incur (file, ...)
end

-- require
local usedmodules = {}
function require (moduleName, filename, action, ...)
	local result = usedmodules[moduleName]
	if result then
		return result
	elseif filename then
		if type(action) == "string" then
			here[action]( filename, ... )
		else
			(action or incur)( filename, ... )
		end
		local theModule = usedmodules[moduleName]
		if not theModule then
			error( "Module not found: "..tostring(moduleName), 2 )
		end
		return theModule
	else
		error( "Module not found: "..tostring(moduleName), 2 )
	end
end

function register (name, module)
	if type(name) ~= "string" then
		error( "Module name must be string", 2 )
	end
	usedmodules[name] = module
	return module
end

return register( "luad", setmetatable( here, here ) )
