'''
Factory object
==============

The factory can be used to automatically import any class from a module,
by specifying the module to import instead of the class instance.

The class list and available modules are automatically generated by setup.py.

Example for registering a class/module::

    >>> from kivy.factory import Factory
    >>> Factory.register('Widget', module='kivy.uix.widget')
    >>> Factory.register('Vector', module='kivy.vector')

Example of using the Factory::

    >>> from kivy.factory import Factory
    >>> widget = Factory.Widget(pos=(456,456))
    >>> vector = Factory.Vector(9, 2)

Example using a class name::

    >>> from kivy.factory import Factory
    >>> Factory.register('MyWidget', cls=MyWidget)
'''

__all__ = ('Factory', 'FactoryException')

from kivy.logger import Logger


class FactoryException(Exception):
    pass


class FactoryBase(object):

    def __init__(self):
        super(FactoryBase, self).__init__()
        self.classes = {}

    def is_template(self, classname):
        '''Return True is the classname is a template from
        :class:`~kivy.lang.Builder`.

        .. versionadded:: 1.0.5
        '''
        if classname in self.classes:
            return self.classes[classname]['is_template']
        else:
            return False

    def register(self, classname, cls=None, module=None, is_template=False,
            baseclasses=None, filename=None):
        '''Register a new classname refering to a real class or
        class definition in a module.

        .. versionchanged:: 1.7.0
            :data:`baseclasses` and :data:`filename` added

        .. versionchanged:: 1.0.5
            :data:`is_template` have been added in 1.0.5.
        '''
        if cls is None and module is None and baseclasses is None:
            raise ValueError(
                'You must specify either cls= or module= or baseclasses =')
        if classname in self.classes:
            return
        self.classes[classname] = {
            'module': module,
            'cls': cls,
            'is_template': is_template,
            'baseclasses': baseclasses,
            'filename': filename}

    def unregister_from_filename(self, filename):
        '''Unregister all the factory object related to the filename passed in
        the parameter.

        .. versionadded:: 1.7.0
        '''
        to_remove = [x for x in self.classes
                if self.classes[x]['filename'] == filename]
        for name in to_remove:
            del self.classes[name]

    def __getattr__(self, name):
        classes = self.classes
        if name not in classes:
            raise FactoryException('Unknown class <%s>' % name)

        item = classes[name]
        cls = item['cls']

        # No class to return, import the module
        if cls is None:
            if item['module']:
                module = __import__(name=item['module'], fromlist='.')
                if not hasattr(module, name):
                    raise FactoryException(
                        'No class named <%s> in module <%s>' % (
                        name, item['module']))
                cls = item['cls'] = getattr(module, name)

            elif item['baseclasses']:
                rootwidgets = []
                for basecls in item['baseclasses'].split('+'):
                    rootwidgets.append(Factory.get(basecls))
                cls = item['cls'] = type(name, tuple(rootwidgets), {})

            else:
                raise FactoryException('No information to create the class')

        return cls

    get = __getattr__


#: Factory instance to use for getting new classes
Factory = FactoryBase()

# Now import the file with all registers
# automatically generated by build_factory
import kivy.factory_registers
Logger.info('Factory: %d symbols loaded' % len(Factory.classes))

if __name__ == '__main__':
    Factory.register('Vector', module='kivy.vector')
    Factory.register('Widget', module='kivy.uix.widget')

