import sys
import config

for path in config.path:
    sys.path.append(path)

import os
import sapt

__author__ = 'Guido Wesdorp'
__version__ = '0.1'
__copyright__ = '(c) 2006 Guido Wesdorp'

class ZPTool(object):
    """Stand-alone ZPT engine
    
        builds an environment using a dict of provided vars (for headers,
        environment variables, etc) and some additional stuff:

          * scripts - a special object that provides access to scripts in
            the 'scripts' subdirectory of the environment directory

          * templates - a special object that provides access to other
            pagetemplates, read from the 'templates' subdirectory of
            the environment directory

        The object should be instantiated with the path to the environment
        directory as its only argument, and has one basic method that is
        interesting, which is 'render'. See the docstring of that
        method for a description.
    """

    def __init__(self, envdir):
        if envdir.endswith(os.path.sep):
            envdir = envdir[:-1]
        self._envdir = envdir

    def render(self, path, env):
        """Render a template

            path is an absolute path to the pagetemplate to render

            env is a dictionary that is used as the environment for the
            pagetemplate, and should be filled with environment data
            (HTTP headers, environment variables, etc) from the calling code
        """
        if not os.path.exists(path):
            return self.render_404(path, env)
        elif os.path.isdir(path):
            return self.render_dir(path, env)
        elif not os.path.isfile(path):
            raise SystemError, '%s is not a normal file' % path
        env.update(globals())
        env['modules'] = Importer(self._envdir, env)
        env['templates'] = TemplateProvider(self._envdir, env)
        env['meta'] = {
            'author': __author__,
            'version': __version__,
            'copyright': __copyright__,
            'app': 'ZPTool %s, %s' % (__version__, __copyright__),
        }
        fp = open(path)
        try:
            data = fp.read()
        finally:
            fp.close()
        p = sapt.PageTemplate()
        p.pt_edit(data, None)
        return p.pt_render(env)

    def render_error_page(self, exc, e, tb, env):
        import traceback
        path = os.path.join(self._envdir, 'templates', 'exception.zpt')
        traceback = ''.join(traceback.format_tb(tb))
        env.update({'exception': exc, 'value': e, 'traceback': traceback})
        return self.render(path, env)
    
    def render_404(self, path, env):
        path = os.path.join(self._envdir, 'templates', '404.zpt')
        return self.render(path, env)

    def render_dir(self, path, env):
        if not config.default_indexes:
            # XXX should do render_dir_listing_not_allowed() or something
            return self.render_404(path, env) 
        for default in config.default_indexes:
            newpath = os.path.join(path, default)
            if os.path.isfile(newpath):
                return self.render(newpath)
        # no custom index, render the default one
        path = os.path.join(self._envdir, 'templates', 'index.zpt')
        return self.render(path, env) 

class Importer(object):
    """Access point to scripts in the 'scripts' subdirectory of the envdir

        scripts are imported with the env variable provided to the template
    """
    def __init__(self, envdir, env):
        self._envdir = envdir
        self._env = env

    def __getitem__(self, name):
        """return the return value of a script"""
        g = Container()
        g.update(globals().copy())
        g['env'] = self._env
        fpath = '%s.py' % (os.path.join(self._envdir, name),)
        if not os.path.isfile(fpath):
            return __import__(name, g, {})
        # XXX note that we do a shallow copy here, might be interesting for
        # caches and such, but also a bit scary... we may want to change this
        execfile(fpath, g)
        return g

    __getattr__ = __getitem__

class TemplateProvider(object):
    """Access points to page templates (for macros) in the 'templates' env dir

        templates get the same env dict passed to them as the main template
    """
    def __init__(self, envdir, env):
        self._envdir = envdir
        self._env = env

    def __getitem__(self, name):
        """return the unrendered page template"""
        for ext in config.handler_extensions:
            fpath = '%s%s' % (os.path.join(self._envdir, name), ext)
            if os.path.exists(fpath):
                break
        fp = open(fpath)
        try:
            data = fp.read()
        finally:
            fp.close()
        p = sapt.PageTemplate()
        p.pt_edit(data, None)
        return p

    __getattr__ = __getitem__

class Container(dict):
    def __getattr__(self, name):
        try:
            return self[name]
        except KeyError:
            raise NameError, name

