from django.conf import settings
from django.template import TemplateDoesNotExist
from django.template.loader import find_template_source
from django.core.cache import cache
from subprocess import Popen, PIPE
import sys, os, threading

def _getdefault(name, default=None):
    try:
        default = getattr(settings, name)
    except: pass
    return default

## these should be set in the settings.py file
PHP_BIN           = _getdefault('PHP_BIN', '/usr/bin/php')
PHP_ARGS          = _getdefault('PHP_ARGS', ['-q',])
PHP_IN_SHELL      = _getdefault('PHP_IN_SHELL', True)
PHP_THREAD_IO     = _getdefault('PHP_THREAD_IO', False)
PHP_BUFFER_SIZE   = _getdefault('PHP_BUFFER_SIZE', 1<<12)
PHP_CACHE_SECONDS = _getdefault('PHP_CACHE_SECONDS', None)


## RED_FLAG: Need to modify the actual PHP files to contain the django tags
##           this replacement is a us.pycon.org pmwiki specific HACK
auto_tags=(
    'MAINHEADER',
    'MAINFOOTER',
    'MAINBODY',
)

## *sigh* there are no php comment wrappers around this stuff, well there is a
## start but no end.
custom=(
    ("<!--HeaderText-->",
     """<!--HeaderText--><!--{% block MAINCSSLINK %}--><!--{% endblock %}-->
    """),
    ("<title>", "<title>{% block MAINTITLE %}"),
    ("</title>", "{% endblock %}</title>"),
    ("charset=iso-8859-1",
     "charset=%s" % settings.DEFAULT_CHARSET),
)

tagsets = list(custom)
for tag in auto_tags:
    tagsets.extend((("<!--%s-->" % tag, "<!--{%% block %s %%}-->" %tag),
                    ("<!--/%s-->" %tag, "<!--{% endblock %}-->" )))

tagsets = tuple(tagsets)

class PipeThread(threading.Thread):
    """This is needed for Win32 where the buffer can lock if you
    pass the windows shell buffer size and do not read the output pending
    from the running app"""
    def __init__(self, fin, mode='read'):
        self.fin = fin
        self.sout = ""
        self.mode = mode
        threading.Thread.__init__(self)
    def run(self):
        if self.mode == 'read':
            self.sout = self.fin.read()
        else:
            self.fin.write(self.sout)
    def read(self):
        return self.sout
    def write(self, data):
        self.sout = data
    def close(self):
        ## WARNING! This can cause a crash on windows depending on the FD
        ##          If its an stdout or stderr FD then this will crash
        if self.mode != 'read':
            self.fin.close()
        
def runphp(source, cwd=None):
    php = Popen([PHP_BIN,] + PHP_ARGS, shell=PHP_IN_SHELL,
          bufsize=PHP_BUFFER_SIZE, universal_newlines=True, ## restriction of templates
          stdin=PIPE, stdout=PIPE, #stderr=PIPE,
          cwd=cwd,
          close_fds= sys.platform != 'win32' and True or False)

    ## RED_FLAG: check for returncode after reading stdout and if its an error
    ##           (and we are TEMPLATE_DEBUG), then raise a special error with
    ##           the contents of stderr... wee...
    if not PHP_THREAD_IO:
        (page, phperr) = php.communicate(input=source)
    else:
        phpin = PipeThread(php.stdin, 'write')
        phpin.write(source)
        phpin.start()
        phpout = PipeThread(php.stdout)
        phpout.start()
        phpin.close()
        retcode = php.wait()
        phpout.join(1)
        page = phpout.read()
    return page

def load_template_source(template_name, template_dirs=None):
    """all php template requests must start with 'php:' to keep namespaces
    distinct and as an added security measure
    (and it would be recursive otherwise)"""
    if len(template_name) <= 4 or template_name[:4] != 'php:':
        raise TemplateDoesNotExist, (
            "Not a PHP template request: %s" % template_name)
    if PHP_CACHE_SECONDS:
        page_and_origin_name = cache.get(template_name)
        if page_and_origin_name: return page_and_origin_name
    try:
        dbg = settings.TEMPLATE_DEBUG
        settings.TEMPLATE_DEBUG = True
        source, origin = find_template_source(template_name[4:], template_dirs)
        settings.TEMPLATE_DEBUG = dbg
    except TemplateDoesNotExist:
        raise TemplateDoesNotExist, template_name
    
    dir = os.path.abspath('.')
    if os.path.isfile(origin.name):
        dir = os.path.dirname(origin.name)
    page = runphp(source, cwd=dir)
    
    ## RED_FLAG: this is hackery which should be removed!
    page = page.split('\n\n', 1)[1] ## strip the header
    for phptag, djtag in tagsets:
        page = page.replace(phptag, djtag, 1)

    page_and_origin_name =  (page, origin and origin.name or template_name)
    if PHP_CACHE_SECONDS:
        cache.set(template_name, page_and_origin_name, PHP_CACHE_SECONDS)
    return page_and_origin_name
load_template_source.is_usable=True

