--require 'luanet'
require 'CLRPackage'
import 'System'
import 'System.Reflection'
local get_flags = luanet.enum(BindingFlags,'GetProperty,IgnoreCase,Public')
local put_flags = luanet.enum(BindingFlags,'SetProperty,IgnoreCase,Public')
local call_flags = luanet.enum(BindingFlags,'InvokeMethod,IgnoreCase,Public')

local function A(a)
  return luanet.make_array(Object,a)
end

local empty = A{}
local com_wrapper
local T = luanet.ctype(__ComObject)


local function maybe_wrap(res)
    if type(res) == 'userdata' then
        if res:GetType() == T then return com_wrapper(res) end
    end
    return res
end


local function caller(obj,key)
  local T = obj:GetType()
  return setmetatable({},{
    __call = function(t,o,...)
        return maybe_wrap(T:InvokeMember(key,call_flags,nil,obj,A{...}))
   end
  })
end

function com_wrapper(obj)
  local T = obj:GetType()
  return setmetatable({},{
    __index = function(self,key)
        local ok,res = pcall(T.InvokeMember,T,key,get_flags,nil,obj,empty)
        if not ok then
            res = tostring(res)
            if res:match 'Member not found' then
                return caller(obj,key) --local c =
--~                 rawset(self,key,c)
--~                 return c
            else
                error("cannot find "..key,2)
            end
        else
            return maybe_wrap(res)
        end
    end;
    __newindex = function(self,key,value)
        T:InvokeMember(key,put_flags,nil,A{value})
    end
  })
end

com = {}

function com.CreateObject(progid)
    local ft = Type.GetTypeFromProgID(progid)
    local f = Activator.CreateInstance(ft)
    return com_wrapper(f)
end

com.wrap = maybe_wrap

return com
