from zope.component import getUtility
from zope.interface import implements

from repoze.bfg.interfaces import IContextURL
from repoze.bfg.interfaces import IRequest
from repoze.bfg.interfaces import ISettings
from repoze.bfg.traversal import model_path

from karl.utils import get_setting

class OfflineContextURL(object):
    """
    This is an alternate implementation of IContextURL that doesn't depend on
    an HTTP request.  The motivation for this is we want to be able to run
    code that generates url's via model_url calls but we're not in the context
    of an HTTP request.  The canonical example here is sending an email alert
    that is generated by mail-in.  The email alert uses model_url to create
    links to send to the user via email but because we're calling it from
    mail-in, which is a console app, we don't have a request.
    
    Instead of relying on a request object to figure out our base url, we
    look up an ISettings utility and ask it for a setting called
    'offline_app_url', which is the base url for this Karl instance.
    """
    implements(IContextURL)
    
    def __init__(self, model, request):
        self.model = model
        # Ignores request
        
    def virtual_root(self):
        # Not called by model_url
        # XXX Should implement anyway, but not entirely sure what the expected
        #     return value is.  Is it a full url, or relative to the host?
        raise NotImplementedError
    
    def __call__(self):
        app_url = get_setting(self.model, 'offline_app_url')
        if not app_url:
            raise ValueError('offline_app_url is not set')
        if app_url.endswith('/'):
            app_url = app_url[:-1]
        path = model_path(self.model)
        return app_url + path
    
class OfflineRequest(object):
    """
    Use this in places where you don't have an HTTP request to get alternate
    registration of IContextURL.
    """
    implements(IRequest)