import osg2
import sys

class AllocationError(Exception):
   pass

class CoredNode(object):
   """ Mixin object for a Cored node type """
   coreType = None
   __slots__ = ['mNode', 'mCore']

   def create(cls):
      obj = cls()
      obj.mNode = osg2.Node.create()
      obj.mCore = cls.coreType.create()
      obj.mNode.setCore(obj.mCore)
      return obj
   create = classmethod(create)

   def __new__(cls, *args, **kwargs):
      # A cored not can only be created from within the cored_node module and from code named 'create'.
      if __name__ != sys._getframe(1).f_globals['__name__'] or \
         'create' != sys._getframe(1).f_code.co_name:
         raise AllocationError("Can't construct a '%s' directly, use %s.create()"%(cls.__name__, cls.__name__))
      return object.__new__(cls, *args, **kwargs)

   def __init__(self):
      self.mNode = None
      self.mCore = None
      
   def core(self):
      assert self.mNode.getCore() == self.mCore
      return self.mCore
   
   def node(self):
      return self.mNode

   def __getattr__(self, name):
      return getattr(self.mCore, name)

   # Use code to find the calling frame.
   def __setattr__(self, name, value):
      # Figure out the module that we were called from. If it is not
      # the current module, then throw an exception.
      if __name__ != sys._getframe(1).f_globals['__name__']:
         raise AttributeError("Can't set attributes on a %s."%self.__class__.__name__)
      object.__setattr__(self, name, value)

   def setCore(self, newCore):
      self.mCore = newCore
      self.mNode.setCore(newCore)

def addNodeCoreTypes():
   node_cores = [(n,c) for (n,c) in osg2.__dict__.iteritems() if isinstance(c,type) \
                     and (osg2.NodeCore in c.__mro__) and not c.__name__.endswith("Base")]
   
   for (name,ctype) in node_cores:
      cored_node_classname = "%sNode"%name
      cored_node_class = type(cored_node_classname, (CoredNode,), {'coreType':ctype,})            

      osg2.__dict__[cored_node_classname] = cored_node_class
