import os, sys
import tg, pylons
import subprocess
from webob.exc import HTTPNotFound

from bazbase import conversion, translators, structure
from bazki import getting, auth
from bazki.translators import url
from gameki import restricted, util

def foreach_markup(list_expr, color=None, ancestor=None, group=None,
                   owned=None, let='', propname=u'product', order_by=None):
    if let is True:
        varness = "o,ownr"
        let = "owner=ownr"
    else:
        varness = "o"
    # <<foreach e>><<cache e.product owner=owner/>><</foreach>>
    markup  = "<<foreach %s %s (o.name != '') hasattr(o, '%s')" % (list_expr, varness, propname)
    if color is not None:
        markup += " (o.color == %s)" % repr(color)
    if ancestor is not None and ancestor.ename != u"Object":
        markup += " is_a(o, %s)" % ancestor.ename
    if group is not None:
        markup += " (o.group == %s)" % group.ename
    if owned is not None:
        markup += " (o.owned == %s)" % owned
    if order_by is not None:
        markup += " orderBy=%s" % order_by
    markup += ">>\n"
    markup += "  <<cache o.%s %s/>>\n" % (propname, let)
    markup += "<</foreach>>\n\n"
    return markup

def markup_for_user(u, color=None, ancestor=None, group=None,
                    mode=None, display="", no_headers=False):
    """display should be a baz_eval expression."""

    loopover = None
    if mode != 'full' and mode is not None:
        if structure.get_prop(mode).flavor == 'references':
            if not u.has_propval(mode):
                return ''
            loopover = mode
            propname = u'product'
        else:
            propname = mode
    else:
        propname = u'product'

    markup = '<<let current=%s>>\n' % (u.ename)
    if not no_headers:
        markup += '<<withheaders %s.name %s>>\n' % (u.ename, display)

    if (u.has_propval(propname)
        and (ancestor is None or ancestor.is_ancestor_of(u))
        and group is None and color is None):
        markup += '<<cache %s.%s/>>\n\n' % (u.ename, propname)

    rg_element = u.ename
    exclude_base = 'exclude!'
    if loopover is not None:
        markup += u'<<foreach %s.%s e>>\n' % (u.ename, loopover)
        rg_element = u'e'
        exclude_base = 'exclude'
    
    markup += foreach_markup(
        "recursive_get(%s, 'stuff', '%s', %s, folded=False)" % (rg_element,
                                                                exclude_base,
                                                                u.ename),
        color, ancestor, group, owned=True, let=True,
        propname=propname)
    # For stuff that's not owned, don't set owner at all, so all
    # instances of the same item must be identical and get cached in the
    # same slot.
    markup += foreach_markup(
        "recursive_get(%s, 'stuff', '%s', folded=False)" % (rg_element,
                                                            exclude_base),
        color, ancestor, group, owned=False, propname=propname)
    
    if (propname == u'product' and
        (color is not None
         or (ancestor is not None and ancestor.get_parent() is None)
         or (ancestor is None and group is None))):
        markup += "<<if hasattr(%s, 'badge')" % u.ename
        if color is not None:
            markup += " and %s.badgecolor == %s" % (u.ename, repr(color))
        markup += ">>\n"
        markup += "  <<cache %s.badge />>\n" % u.ename
        markup += "<</if>>\n\n"

    if mode == 'fullq': # !!!
        markup += '<<secret>>\n'

        markup += foreach_markup(
            "recursive_get(%s, 'stuff', 'invert', folded=False)" % (u.ename),
            color, ancestor, group, owned=True, let="owner=%s" % u.ename,
            propname=propname)

        markup += foreach_markup(
            "recursive_get(%s, 'stuff', 'invert', folded=False)" % (u.ename),
            color, ancestor, group, owned=False, propname=propname)
        
        markup += '<</secret>>\n'

    if loopover is not None:
        markup += '<</foreach>>\n'
    if not no_headers:
        markup += '<</withheaders>>\n'
    markup += '<</let>>\n'
    return markup

def print_stuff(user, name=None, mode=None, type='.pdf', no_headers=False):
    if name is None or name == 'all':
        name = u'Object'

    packet_group = structure.get_element(u'PacketGroup')
    if name[0].isupper():
        ancestor = structure.get_element(name)
        if packet_group.is_ancestor_of(ancestor):
            group = ancestor
            ancestor = None
            display = "%s.name" % group.ename
        else:
            group = None
            if ancestor.get_parent() is None:
                display = '"Packet"'
            elif ancestor.get_prop('group'):
                display = '%s.group.name' % ancestor.ename
            else:
                display = '%s.type' % ancestor.ename
        color = None
    else:
        color = name
        ancestor = None
        group = None
        display = '"%s"' % color.capitalize()

    if mode is not None:
        if not restricted.is_omniscient():
            raise restricted.omniscient_only()

    if type == 'default':
        type = getting.DEFAULT_DEFAULT

    if user == 'compendium':
        if not restricted.is_omniscient():
            raise restricted.omniscient_only()
        if mode is None:
            mode = u'product'
        markup = foreach_markup(
            ancestor.ename if ancestor is not None else "Owner",
            color, group=group, propname=mode, order_by=u'name')
    elif user == 'production':
        if not restricted.is_omniscient():
            raise restricted.omniscient_only()
        users = structure.get_element(u'Character').get_descendants()
        users.sort(key=lambda u: conversion.render(u, u'name'))
        places = structure.get_element(u'Place').get_descendants()
        places.sort(key=lambda p: conversion.render(p, u'name'))
        users += places
        markup = ""
        for u in users:
            # TODO(xavid): combine with getting this for key above
            if conversion.render(u, u'name'):
                markup += markup_for_user(
                    u, color, ancestor, group, mode=mode or 'full',
                    display=display, no_headers=no_headers)
    else:
        if (not restricted.is_omniscient()
            and user != restricted.logged_in_username()):
            raise restricted.not_you()
        owner = util.get_user(user)
        markup = markup_for_user(owner, color, ancestor, group,
                                 mode=mode, display=display,
                                 no_headers=no_headers)

    if mode is None:
        pdfurl = url('/print/%s/%s.pdf' % (user, name))
    else:
        pdfurl = url('/print/%s/%s/%s.pdf' % (user, name, mode))

    global_metadata = {'title': u'Gameki Printing',
                       'pdfurl': pdfurl}
    #print >>sys.stderr, markup
    if type == '.raw':
        ret = markup
        ctype = 'text/plain'
    else:
        try:
            flags = ''
            if no_headers:
                flags += 'h'
            ret = conversion.convert_markup(markup, type,
                                            global_metadata,
                                            cacheable_as='/print/%s/%s/%s/%s'
                                            % (user, name, mode, flags))
        except conversion.ConversionFailedException,e:
            raise HTTPNotFound(e)
        ctype, enc = translators.guess_type(name+type)

    if not isinstance(ret,str):
        # TG doesn't like buffers or unicode.
        ret = str(ret)

    return ret, ctype

class Printer(tg.TGController):

    @tg.expose(template="mako:gameki.templates.printing")
    def _default(self, user=None, name=None, mode=None, type=None,
                 print_as=None, print_mode=None, no_headers=False):
        if user is None:
            if not pylons.request.url.endswith('/'):
                tg.redirect(pylons.request.url+'/')
            return {}

        if type is not None:
            pass
        elif name is None:
            user, type = getting.filename_split(user)
        elif mode is None:
            name, type = getting.filename_split(name)
            name = name.replace('_', ' ')
            name = unicode(name, 'utf-8')
        else:
            mode, type = getting.filename_split(mode)

        if not name:
            name = None
        if not mode:
            mode = None

        ret, ctype = print_stuff(user, name, mode, type, no_headers=no_headers)
        if ctype:
            try:
                pylons.response.headers['Content-type'] = ctype + '; charset=utf-8'
            except TypeError:
                pass
        return ret

# Printing to lpr translator
def lpr(im):
    printer = im.metadata()['filters']['P']
    username = im.metadata()['filters']['U']
    mode = im.metadata()['filters']['Z']
    
    command_line = ['lpr', '-P%s' % printer, '-U%s' % username]
    if mode == 'duplex':
        command_line.append('-o')
        command_line.append('sides=two-sided-long-edge')
    elif mode == 'fronts':
        command_line.append('-o')
        command_line.append('page-set=odd')
    elif mode == 'backs':
        command_line.append('-o')
        command_line.append('page-set=even')
    else:
        assert False, mode
    # This is either a .pdf produced by LaTeX and thus already on disk
    # or data loaded from cache, which will be in memory.
    if im.data is None:
        command_line.append(im.asPath())
        indata = ''
    else:
        indata = im.asData()
    
    p = subprocess.Popen(command_line,
                         stdin=subprocess.PIPE,
                         stdout=subprocess.PIPE, stderr=subprocess.PIPE)
    out, err = p.communicate(indata)
    im.setData('lpr returned: %s\n' % p.returncode + out + err, 'lpr')
    im.deps.makeUncacheable()

translators.TRANSLATORS.setdefault('lpr', {})['.pdf'] = lpr
