#!/usr/bin/python


import os
if os.environ.has_key('REQUEST_METHOD'):
    # we're running as a CGI script
    import cgitb; cgitb.enable()
    running_as_cgi = True
else:
    running_as_cgi = False            



from parser import *


class Macro(object):
    def __init__(self, fn): self.fn = fn

class Splice(object):
    def __init__(self, elems): self.elems = elems


def evaluate(item, variables={}):
    if type(item) is list:
        head = evaluate(item[0], variables)
        if len(item) == 1: return head
        
        if isinstance(head, Macro):
            return head.fn(item[1:])
        
        args = []
        keys = {}
        
        for el in item[1:]:
            if type(el) is list and el[0] == '=':
                eq, name, value = el
                keys[name] = evaluate(value, variables)
            else:
                val = evaluate(el, variables)
                if not isinstance(val, Splice):
                    args.append(val)
                else:
                    args.extend(val.elems)
                
        return head(*args, **keys)
        
    elif type(item) is str:
        name = item.replace(':', '_')
        module = item.split(':')[0]
        
        if variables.has_key(item):
            return variables[item]
        elif lib.has_key(item):
            return lib[item]
        elif globals().has_key(name):
            return globals()[name]
        elif globals().has_key('module_' + module):
            return globals()['module_' + module](item[len(module)+1:])
            
        assert False, "not implemented: '%s'" % item
        
    elif isinstance(item, Str):
        return item.s
        
    else:
        assert False, "not evaluatable: %r" % item
        

       
def flatten_list(l):
    if type(l) not in (tuple, list): return [l]
    r = []
    for el in l: r.extend(flatten_list(el))
    return r


class HTML:
    def __init__(self, contents=[], before="", after=""):
        contents = flatten_list(contents)
    
        self.before = before
        self.contents = contents
        self.after = after
        
    def __call__(self, *args):
        args = flatten_list(args)
        return HTML(self.contents + args, self.before, self.after)
        
    def __add__(self, o): return str(self) + o
    def __radd__(self, o): return o + str(self)
        
    def __str__(self):
        return self.before + ''.join([str(el) for el in self.contents]) + self.after
        
    def __repr__(self):
        return "<HTML: {%s}>" % str(self)
        
        

def compile_fn(params, body):
    def fn(*args):
        if params and params[-1].startswith('*'):
            assert len(args) >= len(params) - 1
            d = dict(zip(params[:-1], args))
            d[params[-1][1:]] = args[len(params)-1:]
        else:
            assert len(args) == len(params)
            d = dict(zip(params, args))
            
        return evaluate(body, d)
    
    return fn
        


lib = {'+': lambda *args: reduce((lambda a,b: a+b), args),
       '==': lambda a,b: a == b}

c_id = lambda x: x
c_map = lambda fn, l: map(fn, l)
c_list = lambda *args: list(args)
c_splice = lambda l: Splice(l)
c_not = lambda x: not x
c_html = lambda *args: HTML(args)

def c_if(*args):
    assert len(args) >= 2
    for i in range(1, len(args), 2):
        if args[i-1]: return args[i]
    if len(args) % 2: return args[-1]
    return []
    
def c_join(l, sep=" "):
    if type(sep) is tuple: sep = list(sep)
    if type(sep) != list: sep = [sep]
    if not l: return l
    r = l[0]
    for el in l[1:]:
        r.extend(sep)
        r.extend(el)
    return r
    
def c_fn(decl):
    return compile_fn(decl[:-1], decl[-1])
c_fn = Macro(c_fn)
    
libcode = """
(= (ui:item label *args) "\(h:b label): \(c:html args)")

(= (hx:l *args) "\(c:html args)\(h:br)")
(= (hx:link uri *label) (h:a (= h:href uri) (c:html label)))
(= (hx:email email) (hx:link "mailto:\(email)" email))
(= (hx:uri uri) (hx:link uri uri))

(= (ui:statusURI story) 
   "http://baensuniverse.com/cgi-bin/status.py?id=\(story.bu:secretID)")
   
(= (c:for list fn) (c:map fn list))
"""

for eq, sig, body in parse_file(libcode):
    assert eq == '='
    assert type(sig) is list
    
    name = sig[0]
    params = sig[1:]
    
    lib[name] = compile_fn(params, body)

def module_h(tag):
    if tag in ['hr', 'br']: return HTML("<%s>\n" % tag)
    if tag in ['p', 'div', 'h1']: nl = '\n\n'
    else: nl = ""

    def result(*args, **keys):
        params = ''.join([' %s="%s"' % (k[2:],keys[k]) 
                          for k in keys if k.startswith('h:')])
        return HTML(args, "<%s%s>" % (tag, params), "</%s>%s" % (tag,nl))

    return result
    
def module_bu(var):
    if var == 'statuschanges': return lambda obj: [1,2,3]
    return lambda obj: "{%s}" % var


#print evaluate(parse_file("""
## (ui:item "Author" "Benja Fallenstein (\(hx:email #"benja.fallenstein@gmail.com"))")
#(c:map (c:fn x "\(x)...") (c:list "foo" "bar" "baz"))
#"""))

if running_as_cgi:
    print "Content-type: text/html"
    print

print evaluate(parse_file(open('status3.toy').read()),
               {'story': "{STORY}"})
