from __future__ import with_statement
from __future__ import absolute_import
import sys
from threading import Condition, Thread
from functools import wraps


def percentile(d, q):
    import numpy
    try:  # requires np >= 1.5.0, uses weighted percentile
        return numpy.percentile(d, q)
    except AttributeError:
        sd = numpy.sort(d.ravel())
        return [sd[int(v / 100. * d.size)] for v in q]


def synchronized(tlockname):
    """A decorator to place an instance based lock around a method """

    def _synched(func):
        @wraps(func)
        def _synchronizer(self, *args, **kwargs):
            tlock = getattr(self, tlockname)
            tlock.acquire()
            try:
                return func(self, *args, **kwargs)
            finally:
                tlock.release()
        return _synchronizer
    return _synched


class Future:
    def __init__(self, func, args=[], callback=None, data=None):
        self.__done = 0
        self.__result = None
        self.__status = 'working'
        self.__excpt = None
        self.__callback = callback
        self.data = data

        # Notify on this Condition when result is ready
        self.__C = Condition()

        # Run the actual function in a separate thread
        self.__T = Thread(target=self.Wrapper, args=[func] + list(args))
        self.__T.setName("FutureThread")
        self.__T.start()

    def __repr__(self):
        return '<Future at ' + hex(id(self)) + ':' + self.__status + '>'

    def get(self):
        self.__C.acquire()
        while self.__done == 0:
            self.__C.wait()
        self.__C.release()
        if self.__excpt:
            if sys.version_info.major == 2:
                code = dedent(
                """
                raise self.__excpt[0], self.__excpt[1], self.__excpt[2]
                """
                )
                eval(compile(code, __file__, 'exec'))
            else:
                raise self.__excpt[0](self.__excpt[1]).with_traceback()
        return self.__result

    def Wrapper(self, func, *args):
        # Run the actual function, and let us housekeep around it
        self.__C.acquire()
        try:
            self.__result = func(*args)
        except:
            self.__result = "Exception raised within Future"
            self.__excpt = sys.exc_info()

        self.__done = 1
        self.__status = repr(self.__result)
        self.__C.notify()
        self.__C.release()
        if self.__callback:
            self.__callback(self)


def log_invocation(fn='/tmp/reflex_invoc.log'):
    """ log invocation of actor for easy reexecution with:
        eval $(cat /tmp/reflex_invoc.log)
    """
    import os
    try:
        with open(fn, 'w') as f:
            path = ""
            s = " ".join(['"%s"' % x for x in sys.argv])
            try:
                import reflex
                path = '"%s:"' % os.path.dirname(reflex.__file__)
            except ImportError:
                pass
            f.write('PYTHONPATH=%s$PYTHONPATH python -W all %s\n' % (path, s))
    except:
        pass
