import numpy as np
import PIL.Image, PIL.ImageDraw, PIL.ImageFilter, PIL.ImageFont, PIL.ImageOps
from ..image import image_rectangle, rescale
from ..psychopy import psychopy_rectangle


def contrast_psychopy(window, parameters=None, **kwargs):
    """Create a PsychoPy stimulus of the Simultaneous Contrast illusion.
    
    
    Simultaneous contrast, identified by Michel Eugène Chevreul, refers to the
    manner in which the colors of two different objects affect each other.

    Parameters
    ----------
    window : object
        The window object in which the stimulus will be rendered.
    parameters : dict
        Parameters of the Simultaneous Contrast illusion generated by `contrast_parameters()`.
    **kwargs
        Additional arguments passed into `contrast_parameters()`.
    
    Returns
    -------
    In-place modification of the PsychoPy window (No explicit return).

    Examples
    ---------
    >>> import pyllusion
    >>> from psychopy import visual, event

    >>> # Create parameters
    >>> parameters = pyllusion.contrast_parameters(difference=0, illusion_strength=-50)

    >>> # Initiate Window
    >>> window = visual.Window(size=[800, 600], winType='pygame',
                               color=parameters["Background_Top_RGB"],
                               colorSpace='rgb255')
    
    >>> # Display illusion
    >>> pyllusion.contrast_psychopy(window=window, parameters=parameters)
    
    >>> # Refresh and close window    
    >>> window.flip()
    >>> event.waitKeys()  # Press any key to close
    >>> window.close()

    """
    # Create white canvas and get drawing context
    if parameters is None:
        parameters = contrast_parameters(**kwargs)

    # Background lower
    psychopy_rectangle(window, x=0, y=-0.5, size_height=1, size_width=2, color=parameters["Background_Bottom_RGB"],
                       outline_color=parameters["Background_Bottom_RGB"],
                       fillColorSpace='rgb255', lineColorSpace='rgb255')

    psychopy_rectangle(window, x=0, y=0.5, size_height=0.5, size_width=1, color=parameters["Rectangle_Top_RGB"],
                       outline_color=parameters["Rectangle_Top_RGB"],
                       fillColorSpace='rgb255', lineColorSpace='rgb255')

    psychopy_rectangle(window, x=0, y=-0.5, size_height=0.5, size_width=1, color=parameters["Rectangle_Bottom_RGB"],
                       outline_color=parameters["Rectangle_Bottom_RGB"],
                       fillColorSpace='rgb255', lineColorSpace='rgb255')


def contrast_image(parameters=None, width=800, height=600, **kwargs):
    """Create a PIL image of the Simultaneous Contrast illusion.
    
    
    Simultaneous contrast, identified by Michel Eugène Chevreul, refers to the
    manner in which the colors of two different objects affect each other.

    Parameters
    ----------
    parameters : dict
        Parameters of the Simultaneous Contrast illusion generated by `contrast_parameters()`.
    width : int
        Width of the returned image.
    height : int
        Height of the returned image.
    **kwargs
        Additional arguments passed into `contrast_parameters()`.

    Returns
    -------
    Image
        Image of the Simultaneous Contrast illusion, defaults to 800 x 600 pixels.
        Can be resized
        (`resize()`, See https://pillow.readthedocs.io/en/stable/reference/Image.html#PIL.Image.Image.resize)
        and saved in different file formats
        (`save()` See https://pillow.readthedocs.io/en/stable/handbook/image-file-formats.html).

    Examples
    ---------
    >>> import pyllusion
    >>>
    >>> parameters = pyllusion.contrast_parameters(illusion_strength=-50, difference=0)
    >>> pyllusion.contrast_image(parameters)
    """
    # Create white canvas and get drawing context
    if parameters is None:
        parameters = contrast_parameters(**kwargs)

    # Background upper
    image = PIL.Image.new(
        "RGB", (width, height), color=parameters["Background_Top_RGB"]
    )
    # Background lower
    image = image_rectangle(
        image=image,
        y=-0.5,
        size_height=1,
        size_width=2,
        color=parameters["Background_Bottom_RGB"],
        antialias=False,
    )

    image = image_rectangle(
        image=image,
        y=0.5,
        size_height=0.5,
        size_width=1,
        color=parameters["Rectangle_Top_RGB"],
        antialias=False,
    )
    image = image_rectangle(
        image=image,
        y=-0.5,
        size_height=0.5,
        size_width=1,
        color=parameters["Rectangle_Bottom_RGB"],
        antialias=False,
    )

    return image


def contrast_parameters(illusion_strength=0, difference=0):
    """Compute Parameters for Simultaneous Contrast illusion.

    Parameters
    ----------
    illusion_strength : float
        The strength of the background, i.e., contrasting colours, in biasing the perception of inner rectangles of different grey shades.
        Specifically, the difference in background colours, where large values create greater contrast in the two
        grey backgrounds. A positive sign reflects a darker upper background relative to the lower
        background, and a negative sign reflects a darker lower background relative to the upper.
    difference : float
        The objective difference of the grey shades of the two inner rectangles. Large positive signs reflect a 
        darker lower rectangle relative to the upper rectangle, and negative signs reflect a darker upper
        rectangle relative to the lower rectangle.
        
    Returns
    -------
    dict
        Dictionary of parameters of the Simultaneous Contrast illusion.
    """
    colors, rgb = _contrast_parameters(
        difference=difference, illusion_strength=illusion_strength
    )

    parameters = {
        "Illusion": "Contrast",
        "Illusion_Strength": illusion_strength,
        "Difference": difference,
        "Illusion_Type": "Congruent" if illusion_strength > 0 else "Incongruent",
        "Rectangle_Top": colors[0],
        "Rectangle_Bottom": colors[1],
        "Background_Top": colors[2],
        "Background_Bottom": colors[3],
        "Rectangle_Top_RGB": rgb[0],
        "Rectangle_Bottom_RGB": rgb[1],
        "Background_Top_RGB": rgb[2],
        "Background_Bottom_RGB": rgb[3],
    }

    return parameters


def _contrast_parameters(difference=0, illusion_strength=0):
    # Targets
    # A difference of 1 means a difference of 1%
    up = 0.5 + difference / 200
    low = 0.5 - difference / 200

    # Backgrounds
    # A illusion_strength of 1 means a difference of 1%
    mod = np.sign(difference) if difference != 0 else 1
    background_up = 0.5 - mod * illusion_strength / 200
    background_low = 0.5 + mod * illusion_strength / 200

    # Adjustments in case of same contrast between rectangle and background
    if background_up == up:
        if background_up > background_low:
            background_up += 0.01
        elif background_up < background_low:
            background_up -= 0.01
        else:
            background_up -= 0.01
            background_low -= 0.01
    if background_low == low:
        if background_low > background_up:
            background_low += 0.01
        elif background_low < background_up:
            background_low -= 0.01
        else:
            background_low -= 0.01
            background_up -= 0.01

    # Transform to RGB tuples
    background_up_rgb = tuple(
        np.rint(rescale([background_up] * 3, scale=[0, 1], to=[0, 256])).astype(int)
    )
    background_low_rgb = tuple(
        np.rint(rescale([background_low] * 3, scale=[0, 1], to=[0, 256])).astype(int)
    )
    up_rgb = tuple(np.rint(rescale([up] * 3, scale=[0, 1], to=[0, 256])).astype(int))
    low_rgb = tuple(np.rint(rescale([low] * 3, scale=[0, 1], to=[0, 256])).astype(int))

    return (
        (up, low, background_up, background_low),
        (up_rgb, low_rgb, background_up_rgb, background_low_rgb),
    )
