########################################################################
#                           Filter Api                                 #
########################################################################

import string
import time

import numpy as np
import scipy

import config


class MakeBrightnessFilter:
    def __init__(self, intensity):
        self.intensity = intensity

    def __call__(self, img):
        return (img * self.intensity).astype(np.uint8)


def FlipXFilter(intensity):
    return intensity[:, ::-1, :]


def FlipYFilter(intensity):
    return intensity[::-1, :, :]


class MakeBrightnessImageFilter:
    def __init__(self, name):
        self.filter_img = scipy.misc.imread("filter/" + name + ".png", flatten=True) / 255
        # img = np.transpose(img)

    def __call__(self, img):
        img = img.astype(float)
        for i in range(img.shape[2]):
            img[:, :, i] *= self.filter_img
        return img.astype(np.uint8)


def strings(s):
    allowed_chars = string.ascii_letters + string.digits + "+-*/()."
    i = 0
    outlist = []
    while i < len(s):
        if s[i] not in allowed_chars:
            raise Exception("Unexpected char " + s[i])
        if s[i] not in string.ascii_letters:
            i += 1
            continue
        out = ""
        while i < len(s) and s[i] in string.ascii_letters + string.digits:
            out += s[i]
            i += 1
        outlist.append(out)
    return outlist


def eval_safer(expr, x, y, t):
    symbols = {"x": x, "y": y, "t": t,
               "sin": np.sin, "cos": np.cos, "exp": np.exp, "tan": np.tan}
    strs = strings(expr)
    for s in strs:
        if s not in symbols.keys():
            raise Exception(f"Unexpected symbol: {s}")
    return eval(expr, {}, symbols)


class MakeBrightnessExprFilter:
    def __init__(self, expr: str):
        self.expr = expr.replace(" ", "")
        self.t0 = time.time()
        self.x, self.y = np.meshgrid(np.arange(config.ScreenX), np.arange(config.ScreenY))

        eval_safer(self.expr, 0, 0, 0)  # check expression

    def __call__(self, img):
        t = time.time() - self.t0
        img = img.astype(float)
        filter_ = 0 * self.x + eval_safer(self.expr, self.x, self.y, t)
        filter_ = np.clip(np.nan_to_num(filter_), 0, 1)
        for i in range(img.shape[2]):
            img[:, :, i] *= filter_
        return img.astype(np.uint8)
