import types, os, tempfile
from pprint import pprint
from django.dispatch import dispatcher
from django.core import signals
from django.core.handlers.base import BaseHandler
from django.http import HttpRequest
from django.utils.datastructures import MergeDict
from dj_kunde.contrib.view_test.context_keeper import get_context, set_context

HEADER = "*" * 60

class TestRunner(object):
    def __init__(self):
        self.handler = BaseHandler()
        self.handler.load_middleware()
        self.cookies = {}

    def get_response(self, path, method, GET, POST):
        request = HttpRequest()
        request.path = path
        request.META['REQUEST_METHOD'] = method
        request.META['REMOTE_ADDR'] = '127.0.0.1'
        request.COOKIES = self.cookies
        request.GET = GET
        request.POST = POST
        request.REQUEST = MergeDict(GET, POST)
        set_context(None)

        dispatcher.send(signal=signals.request_started)
        response = self.handler.get_response(path, request)
        for middleware_method in self.handler._response_middleware:
            response = middleware_method(request, response)
        dispatcher.send(signal=signals.request_finished)

        context = get_context()
        self.cookies.update(dict([(k,v.value) for k,v in response.cookies.iteritems()]))
        return (response, context)

    def check_context(self, response, ist, soll):
        # remove all the keys that are not in soll:
        if ist == None:
            ist = {}
        #ist = dict([(k,v) for k,v in ist.items() if k in soll])
        if ist == soll:
            return True
        else:
            remove_ellipsis(ist, soll)
            if ist==soll:
                return True
            else:
                print "response is:"
                self.print_html(response)
                print HEADER
                print "context differs:"
                print HEADER
                print_diffs("context",ist,soll)
                #print "IST"
                #pprint (ist)
                #print "\nSOLL"
                #pprint (soll)
                return False

    def check_response(self, ist, soll):
        result = ist.status_code == soll["status_code"] and set(ist.cookies.keys()) == set(soll["cookies"].keys())
        if not result:
            print "response is:"
            self.print_html(ist)
            print HEADER
            if ist.status_code != soll["status_code"]:
                print "* status_code ist=%d, soll=%d" % (ist.status_code, soll["status_code"])
            if set(ist.cookies.keys()) != set(soll["cookies"].keys()):
                print "* cookies ist=%r, soll=%r" % (ist.cookies, soll["cookies"])
            print HEADER
        return result

    def print_html(self, response):
        fd, filename = tempfile.mkstemp('.html','viewtest-',text=True)
        f = os.fdopen(fd,"w")
        print "... writing html output to %s" % filename
        f.write(str(response))
        f.close()
        #print HEADER
        #f = popen("w3m -dump -T text/html","w")
        #if f:
            #f.write(str(response))
            #f.close()


def print_diffs(prefix, ist, soll):
    if ist==None and soll!=None:
        print "* %s: missing" % prefix
    else:
        for key, soll_value in soll.iteritems():
            ist_value = ist.get(key,None)
            if ist_value != soll_value:
                if isinstance(soll_value,dict) and isinstance(ist_value,dict):
                    print_diffs(prefix+"."+key, ist_value, soll_value)
                else:
                    print "* %s.%s:  ist=%r,\n* %s  soll=%r" % (prefix, key, ist_value, " " * (len(prefix)+len(key)+1), soll_value)
        for key, value in ist.iteritems():
            if key not in soll:
                print "* %s.%s: additional, ist=%r" % (prefix, key, value)


def remove_ellipsis(ist, soll):
    """if soll is a dict, remove all items with a value of `Ellipsis`, from both ist and soll.
    If soll is a list, remove all elemens that equal `Ellipsis`, from both ist and soll.
    Then apply this recursively to all elements of ist/soll

    >>> remove_ellipsis([1,2,3], [4,5, Ellipsis])
    ([1, 2], [4, 5])

    >>> remove_ellipsis([],[])
    ([], [])

    >>> remove_ellipsis([{"bla":1, "blo":"asdasd"}], [Ellipsis])
    ([], [])

    >>> remove_ellipsis({"eins":1, "zwei":2}, {"drei": 3, "eins": Ellipsis})
    ({'zwei': 2}, {'drei': 3})

    >>> remove_ellipsis([1,2,{3:3, 4:4, 5:[6,7]}], [0,1,{5:[Ellipsis,2]}])
    ([1, 2, {3: 3, 4: 4, 5: [7]}], [0, 1, {5: [2]}])

    >>> remove_ellipsis(1,Ellipsis)
    (1, Ellipsis)

    >>> remove_ellipsis(None, [Ellipsis])
    (None, [Ellipsis])
    """
    if isinstance(soll, dict) and isinstance(ist, dict):
        for key, value in soll.items():
            if value == Ellipsis:
                del soll[key]
                try:
                    del ist[key]
                except KeyError:
                    pass
            else:
                if isinstance(value, (dict,types.ListType, dict)) and  key in ist:
                    remove_ellipsis(ist[key], value)
    elif isinstance(soll, types.ListType) and isinstance(ist, types.ListType):
        deleteables = []
        for i, value in enumerate(soll):
            if value == Ellipsis:
                deleteables.insert(0,i)
            else:
                if isinstance(value, (dict,types.ListType, dict)):
                    remove_ellipsis(ist[i], value)
        for i in deleteables:
            del soll[i]
            del ist[i]
    return (ist,soll)

def _test():
    import doctest
    doctest.testmod()

if __name__ == "__main__":
    _test()

