# comtypes._meta helper module
from ctypes import POINTER, c_void_p, cast
import comtypes

################################################################
# metaclass for CoClass (in comtypes/__init__.py)

def _wrap_coclass(self):
    # We are an IUnknown pointer, represented as a c_void_p instance,
    # but we really want this interface:
    itf = self._com_interfaces_[0]
    punk = cast(self, POINTER(itf))
    return punk.QueryInterface(itf)

#
# The mro() of a POINTER(App) type, where class App is a subclass of CoClass:
#
#  POINTER(App)
#   App
#    CoClass
#     c_void_p
#      _SimpleCData
#       _CData
#        object

class _coclass_meta(type):
    # metaclass for CoClass
    #
    # If a CoClass subclass is created, create a POINTER(...) type for
    # that class, with bases <coclass> and c_void_p.  Also, the
    # POINTER(...) type gets a __ctypes_from_outparam__ method which
    # will QueryInterface for the default interface: the first one on
    # the coclass' _com_interfaces_ list.
    def __new__(cls, name, bases, namespace):
        klass = type.__new__(cls, name, bases, namespace)
        if bases == (object,):
            return klass
        # XXX We should insist that a _reg_clsid_ is present.
        if "_reg_clsid_" in namespace:
            clsid = namespace["_reg_clsid_"]
            comtypes.com_coclass_registry[str(clsid)] = klass
        PTR = _coclass_pointer_meta("POINTER(%s)" % klass.__name__,
                                  (klass, c_void_p),
                                  {"__ctypes_from_outparam__": _wrap_coclass})
        from ctypes import _pointer_type_cache
        _pointer_type_cache[klass] = PTR
        
        return klass

# will not work if we change the order of the two base classes!
class _coclass_pointer_meta(type(c_void_p), _coclass_meta):
    pass

