import traceback
import uuid
import ssl

import tornado.stack_context
from Crypto.Hash import SHA512
from greenlet import greenlet

from imc import auth

gr_idmap = {}
ret_idmap = {}
gr_main = greenlet.getcurrent()

def switch_top():
    global gr_main

    assert greenlet.getcurrent() != gr_main

    old_iden = auth.current_iden
    old_contexts = tornado.stack_context._state.contexts
    auth.current_iden = None

    result =  gr_main.switch(None)

    tornado.stack_context._state.contexts = old_contexts
    auth.current_iden = old_iden

    return result

def caller(f):
    def wrapper(*args,**kwargs):
        global gr_main
        global gr_idmap
        global ret_idmap

        def _call(*args,**kwargs):
            ret = f(*args,**kwargs)
            retids = gr_idmap[grid]
            for retid in retids:
                del ret_idmap[retid] 

            del gr_idmap[grid]

            return (True,ret)
        
        try:
            gr = greenlet(_call)
            grid = id(gr)
            gr_idmap[grid] = set()
            old_iden = auth.current_iden
            old_contexts = tornado.stack_context._state.contexts

            result = gr.switch(*args,**kwargs)

            tornado.stack_context._state.contexts = old_contexts
            auth.current_iden = old_iden

            if result == None:
                return (False,None)

            if gr.dead == False:
                gr.parent = gr_main

            return result

        except TypeError as err:
            traceback.print_stack()
            print(err)
            return (False,'Eparameter')

        except Exception as err:
            traceback.print_stack()
            print(err)
            return (False,'Einternal')

    return wrapper

def get_retid():
    global gr_idmap
    global ret_idmap

    gr = greenlet.getcurrent()
    grid = id(gr)
    retid = SHA512.new(uuid.uuid1().bytes + ssl.RAND_bytes(64)).hexdigest()

    gr_idmap[grid].add(retid)
    ret_idmap[retid] = gr

    return retid

def ret(retid,value = None,err = None):
    global gr_main
    global gr_idmap
    global ret_idmap

    assert greenlet.getcurrent() == gr_main

    try:
        gr = ret_idmap.pop(retid)
        gr_idmap[id(gr)].remove(retid)

    except KeyError:
        return

    try:
        old_iden = auth.current_iden
        old_contexts = tornado.stack_context._state.contexts

        if err == None:
            gr.switch(value)
        
        else:
            gr.throw(err)

        tornado.stack_context._state.contexts = old_contexts
        auth.current_iden = old_iden

    except TypeError as err:
        traceback.print_stack()
        print(err)