import types
from WebUtils import Funcs

class Component:

    """
    Subclasses of Component are the public view of components,
    i.e., the objects that go in the CPage.components class
    variable.

    However, CPage instances actually work with ServletComponents,
    which are bound to a specific servlet.  So in most cases this
    factory class need only be used like::

        class SomeServletComponent(ServletComponent):
            # real work...

        class SomeComponent(Component):
            _componentClass = SomeServletComponent
    """

    def __init__(self, *args, **kw):
        self._args = args
        self._kw = kw

    def componentFor(self, servlet):
        return self.createComponent()

    def createComponent(self):
        return self._componentClass(*self._args, **self._kw)


class ServletComponent:

    """
    The real workhorse of the component system are the subclasses
    of ServletComponent.  These are where you implement your
    component functionality.

    To hook your class to the servlet, you may wish to define:

    ``*Event`` methods:
        Events are fired by name, such as the 'awake' event (fired
        in the awake() method).  Appending 'Event' gives the
        method name for that event, e.g., 'awakeEvent'.  If the
        class defines an appropriate method, then it will be
        automatically called.  Different events take different
        arguments.
    
    ``_servletMethods``:
        Class variable, a list.  The names of methods which should
        be added to the servlet.  E.g.::

            class BrowserServletComponent(ServletComponent):
                _servletMethods = ['browser']

            def browser(self):
                req = self.servlet().request()
                # ... code that returns an object encapsulating
                # browser compatibility information ...

        Then servlets that used this component would have a method
        ``self.browser()``.  self is still bound to the
        ServletComponent instance, not the servlet instance.

    ``_handleExceptions``:
        Class variable, a list.  The exceptions (or names of
        exceptions) that this class handles.  E.g.::

            class SearchOn404(ServletComponent):
                _handleExceptions = ['HTTPNotFound']

                def exceptionHTTPNotFoundEvent(self, exc):
                    self.servlet().write('...')
            # returning 'break' means the exception has been
                    # handled and needn't be propagated up:
                    return 'break'

        Actual exceptions are simply fired as 'exception' events,
        i.e., calling exceptionEvent.  By using _handleExceptions
        you can catch subclasses of these exceptions, and ignore
        other exceptions.

    In your methods, you'll probably want to take advantage of
    these useful methods:

    ``servlet()``:
        The servlet this instance is bound to.
    ``optionalMethod(methodName, defaultValue, *args, **kw)``:
        Call the method `methodName` of our servlet, with
        *args and **kw.  If the servlet does not define such
        a method, return `defaultValue`.        
    """

    def __init__(self):
        if self.handleExceptions():
            self.exceptionEvent = self.handleExceptionEvent

    _servletMethods = []
    _handleExceptions = []

    def addComponentTo(self, servlet):
        """
        Modify the servlet in some way specific to this component.
        General hook for the component to add functionality
        and methods to the servlet.  By default it adds any
        methods named in the _servletMethods attribute to the
        servlet.
        """
        self._servlet = servlet
        hide_vars = getattr(servlet, '__traceback_supplement_hide_vars__', None)
        for method in self._servletMethods:
            if not getattr(servlet, method, None):
                setattr(servlet, method, getattr(self, method))
                if (hide_vars is not None and method not in hide_vars
                    and isinstance(getattr(self, method), types.MethodType)):
                    hide_vars.append(method)

    def actions(self):
        return []

    def servlet(self):
        return self._servlet

    def optionalMethod(self, methodName, default, *args, **kw):
        """
        Calls a method of the servlet.  If the method does
        not exist, returns default.
        """
        try:
            meth = getattr(self._servlet, methodName)
        except AttributeError:
            return default
        else:
            return meth(*args, **kw)

    def handleExceptions(self):
        return self._handleExceptions

    def handleExceptionEvent(self, exc):
        for handled in self.handleExceptions():
            if type(handled) is type(""):
                handled = eval(handled)
            if isinstance(exc, handled):
                meth = getattr(self, '%sEvent' % handled.__name__)
                if not meth(exc):
                    return 0
        return 1

    def htmlEncode(self, v):
        return Funcs.htmlEncode(v)

    def urlEncode(self, v):
        return Funcs.urlEncode(v)

    # Some null, standard event handlers:
    awakeEvent = None
    sleepEvent = None
    exceptionEvent = None
