import PIL.Image, PIL.ImageDraw, PIL.ImageFilter, PIL.ImageFont, PIL.ImageOps
from ..image import image_line
from ..image.utilities import _coord_line
from ..psychopy import psychopy_line
from .ponzo import _ponzo_parameters_topbottom


def mullerlyer_psychopy(window, parameters=None, **kwargs):
    """
    Create a PsychoPy stimulus of the Müller-Lyer illusion.


    The Müller-Lyer illusion is an optical illusion causing the participant to
    perceive two segments as being of different length depending on the shape of
    the arrows.

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

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

    >>> # Create parameters
    >>> parameters = ill.mullerlyer_parameters(illusion_strength=30, difference=0)

    >>> # Initiate Window
    >>> window = visual.Window(size=[800, 600], winType='pygame', color="white")
    
    >>> # Display illusion
    >>> ill.mullerlyer_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 = mullerlyer_parameters(**kwargs)

    # Loop lines
    for which in ["TopLeft", "TopRight", "BottomLeft", "BottomRight"]:
        # Draw distractor lines
        for side in ["1", "2"]:
            psychopy_line(window,
                          x1=parameters["Distractor_" + which + side + "_x1"],
                          y1=parameters["Distractor_" + which + side + "_y1"],
                          x2=parameters["Distractor_" + which + side + "_x2"],
                          y2=parameters["Distractor_" + which + side + "_y2"],
                          color="black", size=5)
    
    for position in ["Bottom", "Top"]:
        # Draw target lines
        psychopy_line(window,
                      x1=parameters[position + "_x1"],
                      y1=parameters[position + "_y1"],
                      x2=parameters[position + "_x2"],
                      y2=parameters[position + "_y2"],
                      color="red", size=5)


def mullerlyer_image(parameters=None, width=800, height=600, outline=20, background="white", **kwargs):
    """
    Create a PIL image of the Müller-Lyer illusion.
    
    
    The Müller-Lyer illusion is an optical illusion causing the participant to
    perceive two segments as being of different length depending on the shape of
    the arrows.

    Parameters
    ----------
    parameters : dict
        Parameters of the Müller-Lyer illusion generated by `mullerlyer_parameters()`.
    width : int
        Width of the returned image.
    height : int
        Height of the returned image.
    outline : float
        The width of the lines in the illusion, passed into `image_line()`.        
    background : str
        Color of the background.
    **kwargs
        Additional arguments passed into `mullerlyer_parameters()`.

    Returns
    -------
    Image
        Image of the Müller-Lyer 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 as ill
    >>>
    >>> parameters = ill.mullerlyer_parameters(illusion_strength=30, difference=2)
    >>> ill.mullerlyer_image(parameters)

    >>> parameters = ill.mullerlyer_parameters(illusion_strength=30, difference=0)
    >>> ill.mullerlyer_image(parameters)
    """
    # Create white canvas and get drawing context
    if parameters is None:
        parameters = mullerlyer_parameters(**kwargs)

    # Background
    image  = PIL.Image.new('RGB', (width, height), color=background)

    # Distractors lines
    for which in ["TopLeft", "TopRight", "BottomLeft", "BottomRight"]:  #
        for side in ["1", "2"]:
            image = image_line(
                image=image,
                x1=parameters["Distractor_" + which + side + "_x1"],
                y1=parameters["Distractor_" + which + side + "_y1"],
                x2=parameters["Distractor_" + which + side + "_x2"],
                y2=parameters["Distractor_" + which + side + "_y2"],
                color="black",
                size=outline)

    # Target lines (horizontal)
    for position in ["Bottom", "Top"]:
        image = image_line(image=image,
                           x1=parameters[position + "_x1"],
                           y1=parameters[position + "_y1"],
                           x2=parameters[position + "_x2"],
                           y2=parameters[position + "_y2"],
                           color="red",
                           size=outline)

    return image



def mullerlyer_parameters(illusion_strength=0, difference=0, size_min=0.5, distance=1):
    """Compute Parameters for Müller-Lyer Illusion.

    Parameters
    ----------
    illusion_strength : float
        The strength of the arrow shapes in biasing the perception of lines of unequal lengths. A positive sign
        represents the bottom arrows pointing outwards and upper arrows pointing inwards.
        A negative sign represents the bottom arrows pointing inwards and upper arrows pointing outwards.
    difference : float
        The objective length difference of the horizontal lines.
        Specifically, the real difference of upper horizontal line relative to the lower horizontal line. E.g.,
        if ``difference=1``, the upper line will be 100% longer, i.e., 2 times longer than
        the lower line. A negative sign reflects the converse, where ``difference=-1``
        will result in the lower line being 100% longer than the upper line.        
    size_min : float
        Length of lower horizontal line.
    distance : float
        Distance between the upper and lower horizontal lines.

    Returns
    -------
    dict
        Dictionary of parameters of the Müller-Lyer illusion.
    """
    parameters = _ponzo_parameters_topbottom(difference=difference, size_min=size_min, distance=distance)

    length = size_min/2

    if difference >= 0:
        angle = {"Top": -illusion_strength, "Bottom": illusion_strength}
    else:
        angle = {"Top": illusion_strength, "Bottom": -illusion_strength}

    for which in ["Top", "Bottom"]:
        for side in ["Left", "Right"]:
            if side == "Left":
                coord, _, _ = _coord_line(x1=parameters[which + "_x1"], y1=parameters[which + "_y1"], length=length, angle=angle[which])
            else:
                coord, _, _ = _coord_line(x1=parameters[which + "_x2"], y1=parameters[which + "_y2"], length=length, angle=-angle[which])
            x1, y1, x2, y2 = coord

            for c in ["1", "2"]:
                parameters["Distractor_" + which + side + c + "_x1"] = x1
                parameters["Distractor_" + which + side + c + "_y1"] = y1
                parameters["Distractor_" + which + side + c + "_x2"] = x2
                if c == "1":
                    parameters["Distractor_" + which + side + c + "_y2"] = y2
                else:
                    parameters["Distractor_" + which + side + c + "_y2"] = y2 - 2 * (y2 - y1)


    parameters.update({"Illusion": "MullerLyer",
                       "Illusion_Strength": illusion_strength,
                       "Illusion_Type": "Congruent" if illusion_strength > 0 else "Incongruent",
                       "Distractor_Length": length})

    return parameters
  
