from config import log
from patch_utils import wrap_method, call

from TAL.TALDefs import isCurrentVersion, getProgramVersion, getProgramMode, METALError

from Products.CMFCore.utils import getToolByName
from config import CACHE_TOOL_ID, ENABLE_MACRO_CACHE

_marker = []


def do_useMacro(self, (macroName, macroExpr, compiledSlots, block)):
    #return call(self, 'do_useMacro', (macroName, macroExpr, compiledSlots, block))

    if not self.metal:
        self.interpret(block)
        return

    macro = self.engine.evaluateMacro(macroExpr)

    context = self.engine.contexts['context']
    cache_tool = getToolByName(context, CACHE_TOOL_ID, None)
    cacheable_macro = False
    if cache_tool and cache_tool.getEnableMacroCaching():
        source_file = None
        macro_name = None
        for key, value in macro:
            if macro_name and source_file:
                break
            if key == 'setSourceFile':
                source_file = value
            elif key == 'beginScope':
                for k,v in value.items():
                    if k.endswith('define-macro'):
                        macro_name = v
            elif key == 'startTag':
                tag_attr = value[1]
                for attr in tag_attr:
                    if attr[0].endswith('define-macro'):
                        macro_name = attr[1]
        if source_file and macro_name:
            macro_name = '%s/macros/%s' % (source_file, macro_name)
            macro_cache = cache_tool.getMacros()
            cacheable_macro = macro_cache.isCacheableMacro(macro_name)
            #print macro_name, cacheable_macro
            #if not cacheable_macro:
            #    log('not cacheable: %s' % macro_name)

    if cacheable_macro:
        # stuff for the cache key
        cget = self.engine.contexts.get
        user = cget('user')
        request = cget('request')
        traverse_subpath = cget('traverse_subpath')
        template = cget('template')
        context = cget('context')
        container = cget('container')
        macro_cache_key = macro_cache.getKey(macro_name, request, context, container, user, template, traverse_subpath)
        if macro_cache_key is not None:
            text = macro_cache.getMacroText(macro_name, macro_cache_key, _marker)
            if text is not _marker:
                #log('%s, key=%s: cache hit' % (macroName, macro_cache_key))
                self.stream.write(text)
                return
            #log('%s, key=%s: cache miss' % (macroName, macro_cache_key))
            start_index = len(self.stream.getvalue())
        
    if macro is self.Default:
        macro = block
    else:
        if not isCurrentVersion(macro):
            raise METALError("macro %s has incompatible version %s" %
                             (`macroName`, `getProgramVersion(macro)`),
                             self.position)
        mode = getProgramMode(macro)
        if mode != (self.html and "html" or "xml"):
            raise METALError("macro %s has incompatible mode %s" %
                             (`macroName`, `mode`), self.position)
    self.pushMacro(macroName, compiledSlots)
    prev_source = self.sourceFile
    self.interpret(macro)

    if cacheable_macro and macro_cache_key is not None:
        macro_text = self.stream.getvalue()[start_index:]
        #log('saving to cache:' + str(macro_text))
        macro_cache.setMacroText(macro_name, macro_cache_key, macro_text)
    
    if self.sourceFile != prev_source:
        self.engine.setSourceFile(prev_source)
        self.sourceFile = prev_source
    self.popMacro()

def run():
    if ENABLE_MACRO_CACHE:
        log('Applying TAL interpreter patches...')
        from TAL.TALInterpreter import TALInterpreter
        wrap_method(TALInterpreter, 'do_useMacro', do_useMacro)
        TALInterpreter.bytecode_handlers["useMacro"] = do_useMacro
        TALInterpreter.bytecode_handlers_tal["useMacro"] = do_useMacro
        log('TAL interpreter patches applied.')
    else:
        log('Macro cache disabled.  Edit CacheSetup/config.py to enable.')

