import os, sys

import cherrypy
from mako.template import Template
from mako.lookup import TemplateLookup
import simplejson
import datetime, decimal
from StringIO import StringIO
from invirt.config import structs as config
import invirt.database
from webcommon import State


class MakoHandler(cherrypy.dispatch.LateParamPageHandler):
    """Callable which processes a dictionary, returning the rendered body."""
    
    def __init__(self, template, next_handler,
                 content_type='text/html; charset=utf-8'):
        self.template = template
        self.next_handler = next_handler
        self.content_type = content_type
    
    def __call__(self):
        env = globals().copy()
        env.update(self.next_handler())
        cherrypy.response.headers['Content-Type'] = self.content_type
        return self.template.render(**env)
        

class MakoLoader(object):
    
    def __init__(self):
        self.lookups = {}

    def get_lookup(self, directories, module_directory=None,
                     collection_size=-1, imports=[], **kwargs):
        # Find the appropriate template lookup.
        key = (tuple(directories), module_directory)
        try:
            lookup = self.lookups[key]
        except KeyError:
            lookup = TemplateLookup(directories=directories,
                                    module_directory=module_directory,
                                    collection_size=collection_size,
                                    default_filters=['decode.utf8'],
                                    input_encoding='utf-8',
                                    output_encoding='utf-8',
                                    imports=imports,
                                    )
            self.lookups[key] = lookup
        return lookup

    def __call__(self, filename, directories, module_directory=None,
                 collection_size=-1, content_type='text/html; charset=utf-8',
                 imports=[]):
        cherrypy.request.lookup = lookup = self.get_lookup(
            directories, module_directory, collection_size, imports)
        cherrypy.request.template = t = lookup.get_template(filename)
        cherrypy.request.handler = MakoHandler(
            t, cherrypy.request.handler, content_type)

cherrypy.tools.mako = cherrypy.Tool('on_start_resource', MakoLoader())


def revertStandardError():
    """Move stderr to stdout, and return the contents of the old stderr."""
    errio = sys.stderr
    if not isinstance(errio, StringIO):
        return ''
    sys.stderr = sys.stdout
    errio.seek(0)
    return errio.read()


def catchStderr():
    old_handler = cherrypy.request.handler
    def wrapper(*args, **kwargs):
        sys.stderr = StringIO()
        ret = old_handler(*args, **kwargs)
        e = revertStandardError()
        if e:
            if isinstance(ret, dict):
                ret["error_text"] = e
        return ret
    if old_handler:
        cherrypy.request.handler = wrapper

cherrypy.tools.catch_stderr = cherrypy.Tool('before_handler', catchStderr)


class JSONEncoder(simplejson.JSONEncoder):
	def default(self, obj):
		if isinstance(obj, datetime.datetime):
			return str(obj)
		elif isinstance(obj, decimal.Decimal):
			return float(obj)
		else:
			return simplejson.JSONEncoder.default(self, obj)


def jsonify_tool_callback(*args, **kwargs):
    if not cherrypy.request.cached:
        response = cherrypy.response
        response.headers['Content-Type'] = 'text/javascript'
        response.body = JSONEncoder().iterencode(response.body)

cherrypy.tools.jsonify = cherrypy.Tool('before_finalize',
                                       jsonify_tool_callback, priority=30)


def require_login():
    """If the user isn't logged in, raise 403 with an error."""
    if cherrypy.request.login is False:
        raise cherrypy.HTTPError(403,
            "You are not authorized to access that resource")

cherrypy.tools.require_login = cherrypy.Tool('on_start_resource',
                                             require_login, priority=150)


def require_POST():
    """If the request isn't a POST request, raise 405 Method Not Allowed"""
    if cherrypy.request.method != "POST":
        raise cherrypy.HTTPError(405,
                                 "You must submit this request with POST")
    if not cherrypy.request.headers.get('Referer', '').startswith('https://' + config.web.hostname):
        raise cherrypy.HTTPError(403, "This form is only usable when submitted from another page on this site. If you receive this message in error, check your browser's Referer settings.")

cherrypy.tools.require_POST = cherrypy.Tool('on_start_resource',
                                            require_POST, priority=150)


def remote_user_login():
    """Get remote user from SSL or GSSAPI, and store in request object.

Get the current user based on environment variables set by SSL or
GSSAPI, and store it in the attribute cherrpy.request.login.

Per the CherryPy API (http://www.cherrypy.org/wiki/RequestObject#login),
the attribute is set to the username on successful login, to False on
failed login, and is left at None if the user attempted no authentication.
"""
    environ = cherrypy.request.wsgi_environ
    user = environ.get('REMOTE_USER')
    if user is None:
        return
    if environ.get('AUTH_TYPE') == 'Negotiate':
        # Convert the krb5 principal into a krb4 username
        if not user.endswith('@%s' % config.kerberos.realm):
            cherrypy.request.login = False # failed to log in
        else:
            cherrypy.request.login = user.split('@')[0].replace('/', '.')
    else:
        cherrypy.request.login = user

cherrypy.tools.remote_user_login = cherrypy.Tool('on_start_resource',
                                                 remote_user_login, priority=50)


def invirtwebstate_init():
    """Initialize the cherrypy.request.state object from Invirt"""
    if not hasattr(cherrypy.request, "state"):
        cherrypy.request.state = State(cherrypy.request.login)

cherrypy.tools.invirtwebstate = cherrypy.Tool('on_start_resource',
                                              invirtwebstate_init, priority=100)


cherrypy.tools.clear_db_cache = cherrypy.Tool('on_start_resource', invirt.database.clear_cache)


class View(object):
    _cp_config = {'tools.mako.directories':
                      [os.path.join(os.path.dirname(__file__),'templates')]}
