from django.core import template
from django.core import template_loader
from django.core.template.loader import TemplateDoesNotExist
from django.core.template import Node, NodeList, Template, Context
from django.core.template import resolve_variable
from django.core.template import TemplateSyntaxError, VariableDoesNotExist
from django.core.template.defaultfilters import slugify

register = template.Library()

class AssignToNode(Node):
    def __init__(self, varname, varvalue_expr):
        self.varname = varname
        self.varvalue_expr = varvalue_expr

    def render(self, context):
        try:
            value = self.varvalue_expr.resolve(context)
        except VariableDoesNotExist:
            return ''
        context[self.varname] = value
        return ''

class UnionSetNode(Node):
    def __init__(self, varname, set_expr_list):
        self.varname = varname
        self.set_expr_list = set_expr_list

    def render(self, context):
        try:
            res = set(self.set_expr_list[0].resolve(context))
            if not len(res): return ''
            set_list_iter=(set(x.resolve(context))
                           for x in self.set_expr_list[1:])
        except VariableDoesNotExist:
            return ''
        for next in set_list_iter:
            res &= next
            if not len(res): return ''
        context[self.varname] = res
        return ''

class TagHistNode(Node):
    def __init__(self, style, varname, anslist_expr, limit_expr):
        self.style = style
        self.varname = varname
        self.anslist_expr = anslist_expr
        self.limit_expr = limit_expr
    def render(self, context):
        try:
            anslist = self.anslist_expr.resolve(context)
            limit = int(self.limit_expr.resolve(context))
        except (ValueError, VariableDoesNotExist):
            return ''
        hist = {}
        for ans in anslist:
            tags = []
            try:
                if self.style == 'common': tags = ans.gen_tags()
                else:                      tags = ans.gen_uncommon_tags()
            except AttributeError:
                continue
            for tag in tags: hist[tag] = hist.get(tag, 0) + 1
        hist = hist.items()
        hist.sort(key=lambda x: x[1], reverse=True)
        if limit: hist = hist[:limit]
        context[self.varname] = [ {'tag': x[0], 'count': x[1]}
                                    for x in hist if x[1] > 1 ]
        return ''

@register.filter
def questiontemplate(answer, prefix=""):
    return prefix + answer.qtype.lower()

@register.filter(name="min")
def domin(arg1, arg2):
    return min(arg1, arg2)

@register.filter
def submited_stats(answerlist, total):
    uniq = len(set(x.submission_id for x in answerlist))
    return "%d submissions (%.1f%%)" % (uniq, float(uniq*100)/ total)

@register.filter
def dict_val_list(list_of_dicts, key):
    """{{ list_of_dicts|dict_val_list:"key" }}
                ==> [ x["key"] for x in list_of_dicts ]
    """
    try:
        return [ x.get(key, '') for x in list_of_dicts ]
    except:
        return []


@register.tag
def assign_to(parser, token):
    """{% assign_to varname expression %} => varname = {{ expression }}
    """
    bits = token.contents.split()
    if len(bits) != 3:
        raise TemplateSyntaxError("assign_to takes two arguments")
    tag, varname, varvalue_expr = bits
    if varname != slugify(varname) and not varname.isdigit():
        raise TemplateSyntaxError(
            "first argument to assign_to must a valid syntax variable name")
    return AssignToNode(varname, parser.compile_filter(varvalue_expr))

@register.tag
def tag_histogram(parser, token):
    """{% tag_historgram {common, uncommon} answer_list as varname [limit #] %}

    varname has the format of:
        ( { 'tag': <tag>, 'count': # }, ... )
    """

    bits = token.contents.split()
    if len(bits) == 5:
        limitkwd='limit'
        limit_expr=None
        tag, style, askwd, varname, anslist_expr = bits
    elif len(bits) == 7:
        tag, style, anslist_expr,  askwd, varname, limitkwd, limit_expr = bits
    else:
        raise TemplateSyntaxError("assign_to takes 5 or 7 arguments")
    if style not in ("common", "uncommon"):
        raise TemplateSyntaxError(
            "first argument to assign_to must be 'common' or 'uncommon'")
    if varname != slugify(varname) and not varname.isdigit():
        raise TemplateSyntaxError(
            "fifth argument to assign_to must a valid syntax variable name")
    if askwd != "as" or limitkwd != 'limit':
        raise TemplateSyntaxError("malformed tag_histogram tag")
    return TagHistNode(style, varname, parser.compile_filter(anslist_expr),
                       parser.compile_filter(limit_expr))

@register.tag
def union_set(parser, token):
    """{% union_set set1 set2 set3 ... setN as varname %}
            ==> varname = set1 & set2 & set3 & ... & setN
    """
    bits = token.contents.split()
    if len(bits) < 5:
        raise TemplateSyntaxError(
            "union_set must be given two or more arguments")
    set_expr_list = map(parser.compile_filter, bits[1:-2])
    varname = bits[-1]
    if bits[-2] != 'as':
        raise TemplateSyntaxError("malformed union_set tag")
    if varname != slugify(varname) and not varname.isdigit():
        raise TemplateSyntaxError(
            "variable to assign_to must a valid syntax variable name")
    return UnionSetNode(varname, set_expr_list)

