from Products.CMFCore.utils import getToolByName
from Products.CacheSetup.config import log, CACHE_TOOL_ID
from patch_utils import wrap_method, call_pattern
from cmf_utils import _checkConditionalGET, _setCacheHeaders
try:
    from Products.PageTemplates.DeferExpr import LazyWrapper
except:
    LazyWrapper = None

#### patch FSPageTemplate.pt_render

from Products.CMFCore.FSPageTemplate import FSPageTemplate

def FSPT_pt_render(self, source=0, extra_context={}):
    pcs = getToolByName(self, CACHE_TOOL_ID, None)
    # if portal_cache_settings not in place, fall back to the old method
    if pcs is None:
        return call_pattern(self, 'pt_render', '__CacheSetup_FSPageTemplate_%s__', source, extra_context)

    self._updateFromFS()  # Make sure the template has been loaded.

    # We may have a template that renders other templates internally,
    # we need to ensure that only the outermost templates headers are applied
    inner_template = False
    if not source:
        request = self.REQUEST
        response = request.RESPONSE
        # Check to see if we have already started rendering an outer template:
        if response.getHeader('x-cache-rules-applied') is not None:
            inner_template = True
            rule = header_set = None
        else:
            object = self.getParentNode()
            view = self.getId()
            member = pcs.getMember()
            (rule, header_set) = pcs.getRuleAndHeaderSet(request, object, view, member)
            if header_set is not None:
                expr_context = rule._getExpressionContext(request, object, view, member, keywords=extra_context)
            else:
                expr_context = None
            # Set a header to indicate that we searched for header rules
            # for the primary template
            response['x-cache-rules-applied'] = 'yes'

            # If we have a conditional get, set status 304 and return
            # no content
            if _checkConditionalGET(self, extra_context, rule, header_set, expr_context):
                return ''

    result = FSPageTemplate.inheritedAttribute('pt_render')(
        self, source, extra_context
        )
    if not source and not inner_template:
        _setCacheHeaders(self, extra_context, rule, header_set, expr_context)
    return result

#### patch PageTemplate.pt_render

from Products.PageTemplates.Expressions import getEngine
from TAL.TALInterpreter import TALInterpreter
from Products.PageTemplates.PageTemplate import PageTemplateTracebackSupplement, PTRuntimeError

def PT_pt_render(self, source=0, extra_context={}):
    """Render this Page Template"""
    pcs = getToolByName(self, CACHE_TOOL_ID, None)
    # if portal_cache_settings not in place, fall back to the old method
    if pcs is None:
        return call_pattern(self, 'pt_render', '__CacheSetup_PageTemplate_%s__', source, extra_context)

    if not self._v_cooked:
        self._cook()

    __traceback_supplement__ = (PageTemplateTracebackSupplement, self)

    if self._v_errors:
        e = str(self._v_errors)
        raise PTRuntimeError, (
            'Page Template %s has errors: %s' % (self.id, e))

    # We may have a template that renders other templates internally,
    # we need to ensure that only the outermost templates headers are applied
    inner_template = False
    if not source:
        # If we have a conditional get, set status 304 and return
        # no content
        request = self.REQUEST
        response = request.RESPONSE
        # Check to see if we have already started rendering an outer template:
        if response.getHeader('x-cache-rules-applied') is not None:
            inner_template = True
            rule = header_set = None
        else:
            object = self.getParentNode()
            view = self.getId()
            member = pcs.getMember()
            (rule, header_set) = pcs.getRuleAndHeaderSet(request, object, view, member)
            if header_set is not None:
                expr_context = rule._getExpressionContext(request, object, view, member, keywords=extra_context)
            else:
                expr_context = None
            # Set a header to indicate that we searched for header rules
            # for the primary template
            response['x-cache-rules-applied'] = 'yes'

            if _checkConditionalGET(self, extra_context, rule, header_set, expr_context):
                return ''

    output = self.StringIO()
    c = self.pt_getContext()
    c.update(extra_context)

    context = getEngine().getContext(c)
    TALInterpreter(self._v_program, self._v_macros,
                   context,
                   output,
                   tal=not source, strictinsert=0)()

    # clean up - XXX
    # try to eliminate circular references - this may be overkill
    if LazyWrapper is not None:
        context._compiler = None
        context.contexts = None
        context.repeat_vars = None
        for k,v in context.global_vars.items():
            if isinstance(v, LazyWrapper):
                v._expr = None
                v._econtext = None
                v._result = None
        if context.vars:
            while len(context.vars):
                context.vars._pop()
        context.global_vars.clear()
        context.global_vars = None
        context.local_vars.clear()
        context.local_vars = None
        context.vars = None
        context._scope_stack = None
    
    result = output.getvalue()
    if not source and not inner_template:
        _setCacheHeaders(self, extra_context, rule, header_set, expr_context)
    return result


def run():
    log('Applying CMF patches...')
    from Products.PageTemplates.PageTemplate import PageTemplate
    wrap_method(PageTemplate, 'pt_render', PT_pt_render, '__CacheSetup_PageTemplate_%s__')
    from Products.CMFCore.FSPageTemplate import FSPageTemplate
    wrap_method(FSPageTemplate, 'pt_render', FSPT_pt_render, '__CacheSetup_FSPageTemplate_%s__')
    log('CMF Patches applied.')

