from rope.base.oi.type_hinting import interfaces
from rope.base.oi.type_hinting.providers import (
    composite, inheritance, docstrings, numpydocstrings, pep0484_type_comments
)
from rope.base.oi.type_hinting.resolvers import composite as composite_resolvers, types
from rope.base import utils


class TypeHintingFactory(interfaces.ITypeHintingFactory):

    @utils.saveit
    def make_param_provider(self):
        providers = [
            docstrings.ParamProvider(docstrings.DocstringParamParser(), self.make_resolver()),
            docstrings.ParamProvider(numpydocstrings.NumPyDocstringParamParser(), self.make_resolver()),
        ]
        return inheritance.ParamProvider(composite.ParamProvider(*providers))

    @utils.saveit
    def make_return_provider(self):
        providers = [
            docstrings.ReturnProvider(docstrings.DocstringReturnParser(), self.make_resolver()),
        ]
        return inheritance.ReturnProvider(composite.ReturnProvider(*providers))

    @utils.saveit
    def make_assignment_provider(self):
        providers = [
            pep0484_type_comments.AssignmentProvider(self.make_resolver()),
            docstrings.AssignmentProvider(docstrings.DocstringParamParser(), self.make_resolver()),
            docstrings.AssignmentProvider(numpydocstrings.NumPyDocstringParamParser(), self.make_resolver()),
        ]
        return inheritance.AssignmentProvider(composite.AssignmentProvider(*providers))

    @utils.saveit
    def make_resolver(self):
        """
        :rtype: rope.base.oi.type_hinting.resolvers.interfaces.IResolver
        """
        resolvers = [
            types.Resolver(),
        ]
        return composite_resolvers.Resolver(*resolvers)


default_type_hinting_factory = TypeHintingFactory()


class TypeHintingFactoryAccessor(object):

    def __call__(self, project):
        """
        :type project: rope.base.project.Project
        :rtype: rope.base.oi.type_hinting.interfaces.ITypeHintingFactory
        """
        factory_location = project.get_prefs().get(
            'type_hinting_factory',
            'rope.base.oi.type_hinting.factory.default_type_hinting_factory'
        )
        return self._get_factory(factory_location)

    @utils.cached(10)
    def _get_factory(self, factory_location):
        """
        :type factory_location: str
        :rtype: rope.base.oi.type_hinting.interfaces.ITypeHintingFactory
        """
        return utils.resolve(factory_location)

get_type_hinting_factory = TypeHintingFactoryAccessor()
