---
--- This lua module provides auto importing of .net classes into a named package.
--- Makes for super easy use of LuaInterface glue
---
--- example:
---   Threading = CLRPackage("System", "System.Threading")
---   Threading.Thread.Sleep(100)
---
--- Extensions:
--- import() is a version of CLRPackage() which puts the package into a list which is used by a global __index lookup,
--- and thus works rather like C#'s using statement. It also recognizes the case where one is importing a local
--- assembly, which must end with an explicit .dll extension.

--- Alternatively, luanet.namespace can be used for convenience without polluting the global namespace:
---   local sys,sysi = luanet.namespace {'System','System.IO'}
--    sys.Console.WriteLine("we are at {0}",sysi.Directory.GetCurrentDirectory())

-- LuaInterface hosted with stock Lua interpreter will need to explicitly require this...
if not luanet then
    require "luanet"
end

local import_type, load_assembly = luanet.import_type, luanet.load_assembly

local mt = {
    --- Lookup a previously unfound class and add it to our table
    __index = function(package, classname)
        local class = rawget(package, classname)
        if class == nil then
            class = import_type(package.packageName .. "." .. classname)
            package[classname] = class -- keep what we found around, so it will be shared
        end
        return class
    end,
}

function luanet.namespace(ns)
    if type(ns) == "table" then
        local res = {}
        for i = 1, #ns do
            res[i] = luanet.namespace(ns[i])
        end
        return unpack(res)
    end
    -- FIXME - table.packageName could instead be a private index (see Lua 13.4.4)
    local t = { packageName = ns }
    setmetatable(t, mt)
    return t
end

local globalMT, packages

local function set_global_mt()
    packages = {}
    globalMT = {
        __index = function(T, classname)
            for i, package in ipairs(packages) do
                local class = package[classname]
                if class then
                    _G[classname] = class
                    return class
                end
            end
        end,
    }
    setmetatable(_G, globalMT)
end

--- Create a new Package class
function CLRPackage(assemblyName, packageName)
    -- a sensible default...
    packageName = packageName or assemblyName
    local ok = pcall(load_assembly, assemblyName) -- Make sure our assembly is loaded
    return luanet.namespace(packageName)
end

--[[
function import (assemblyName, packageName)
    if not globalMT then
        set_global_mt()
    end
    if not packageName then
		local i = assemblyName:find('%.dll$')
		if i then packageName = assemblyName:sub(1,i-1)
		else packageName = assemblyName end
	end
    local t = CLRPackage(assemblyName,packageName)
	table.insert(packages,t)
	return t
end
--]]

function luanet.make_array(tp, tbl)
    local arr = tp[#tbl]
    for i, v in ipairs(tbl) do
        arr:SetValue(v, i - 1)
    end
    return arr
end

function luanet.each(o)
    local e = o:GetEnumerator()
    return function()
        if e:MoveNext() then
            return e.Current
        end
    end
end
