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

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

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=""):
    """display should be a baz_eval expression."""

    if mode != 'full' and mode is not None:
        propname = mode
    else:
        propname = u'product'
    
    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)
    
    markup += foreach_markup(
        "recursive_get(%s, 'stuff', 'exclude+', folded=False)" % (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', 'exclude!', folded=False)" % (u.ename),
        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'

    markup += '<</withheaders>>\n'
    return markup

def print_stuff(user, name=None, mode=None, type='.pdf'):
    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='full',
                    display=display)
    else:
        if (not restricted.is_omniscient()
            and user != restricted.logged_in_username()):
            raise restricted.not_you()
        if user[0].isupper():
            owner = structure.get_element_with(dict(name=user))
            if owner is None:
                owner = structure.get_element(user)
        else:
            owner = structure.get_element_raw(dict(username=user))
        markup = markup_for_user(owner, color, ancestor, group,
                                 mode=mode, display=display)

    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:
            ret = conversion.convert_markup(markup, type,
                                            global_metadata,
                                            cacheable_as='/print/%s/%s/%s'
                                            % (user, name, mode))
        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):
        if user is None:
            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)
        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
