from turbogears import    controllers, error_handler, expose, flash, \
                        redirect, widgets, validate, validators, url, view, identity
import os
import cherrypy
import gzip
import logging

import cherrypy
from cherrypy.lib.cptools import serveFile

from  cherrypy.lib.cptools import mimetypes

#
# define allowed directories here (relative to your project root)
#
dirs={ "script" : "ffwde/static/script" }

log = logging.getLogger( "ffwde.controllers.gz" )
mimetypes.init()
# add your own mime types
# e.g. mimetypes.types_map['.ico']='image/x-icon'

class GzipController( controllers.Controller ):
    """A controller that can serve precompressed gzip files. 
    The idea is to precompress a few well-known files and instead of spending
    CPU time on gzipping all output."""

    def can_gzip(self):
        """Examines the cherrypy request headers to find out whether a 
           gzipped response should be send."""
        acceptable = cherrypy.request.headers.elements('Accept-Encoding')
        if not acceptable:
            # If no Accept-Encoding field is present in a request,
            # the server MAY assume that the client will accept any
            # content coding. In this case, if "identity" is one of
            # the available content-codings, then the server SHOULD use
            # the "identity" content-coding, unless it has additional
            # information that a different content-coding is meaningful
            # to the client.
            return False
        
        for coding in acceptable:
            if coding.value == 'identity' and coding.qvalue != 0:
                return False
            if coding.value in ('gzip', 'x-gzip') and coding.qvalue > 0:
                return True        
        return False
        
    
    @expose()
    def default(self, *args, **kw):
        
        # validate args and build real paths for the normal and the gzip file
        dir = None
        if len(args) == 2:
            dir = dirs[args[0]]
                
        if not dir:
            raise cherrypy.HTTPError(404)
        
        dirpath = os.path.join(os.getcwd(), dir)
        path = os.path.join( dirpath, args[1])
        
        if os.path.dirname( os.path.normpath(path)) != os.path.normpath( dirpath):
            log.debug("%s / %s" % ( 
                os.path.dirname( os.path.normpath(path)), 
                os.path.normpath( os.getcwd())) )
            raise cherrypy.HTTPError(403)

        if not os.path.exists(path):
            log.debug("%s not found" % path)
            raise cherrypy.HTTPError(404)
                                
        gzpath = os.path.join( dirpath , args[1]+".gz")
        
        # should we use gzip encoding?
        do_gzip=self.can_gzip()
        
        # if the gz file does not exist or the normal file was modified
        if do_gzip and ( not os.path.exists(gzpath) or 
              os.path.getmtime(path) >= os.path.getmtime(gzpath) ):
                                    
            log.info(""" creating gzip of %s """ % path )
            gzfile = None
            file = None
            try:
                gzfile = gzip.GzipFile(gzpath, "wb")
                file = open(path, "rb")
                data = file.read()
                gzfile.write( data)
            finally:
                if gzfile:
                    gzfile.close();
                if file:
                    file.close();
        
        # get the mime type of the normal file
        pos = path.rfind(".")
        ext = pos > 0 and path[pos:] or ""        
        mimeType = mimetypes.types_map.get(ext, "text/plain")                               
        
        # fix for bug http://trac.turbogears.org/turbogears/ticket/879
        # don't use cache
        # you can comment this out if you apply the 
        # patch to your turbogears installation
        del cherrypy.request.headers['If-Modified-Since']
        
        response = cherrypy.response              
        if do_gzip:
            # add Accept-Encoding to varying headers if not present there
            varies = response.headers.get("Vary", "")
            varies = [x.strip() for x in varies.split(",") if x.strip()]
            if "Accept-Encoding" not in varies:
                varies.append("Accept-Encoding")
            response.headers['Vary'] = ", ".join(varies)
            # .. and set content encoding to gzip
            response.headers['Content-Encoding'] = 'gzip'
            
            return serveFile(gzpath, mimeType)
        else:
            # serve normal file
            return serveFile(path, mimeType)
