# pylint: disable=line-too-long
"""
Will store all the functions and modules for generation of rectangle layer
in Lottie format
"""

import sys
from lxml import etree
from common.misc import approximate_equal
from common.Vector import Vector
from synfig.animation import to_Synfig_axis
from properties.shapePropKeyframe.helper import add, insert_dict_at, quadratic_to_cubic
sys.path.append("../../")


def gen_list_rectangle(lottie, layer):
    """
    Generates a shape layer corresponding to rectangle layer by manipulating the
    parameters of the rectangle

    Args:
        lottie (dict) : Lottie format rectangle layer will be stored in this
        layer  (common.Layer.Layer) : Synfig format rectangle layer

    Returns:
        (None)
    """
    ################### SECTION 1 #########################
    # Inserting waypoints if not animated and finding the first and last frame
    window = {}
    window["first"] = sys.maxsize
    window["last"] = -1

    point1 = layer.get_param("point1")
    point2 = layer.get_param("point2")
    expand = layer.get_param("expand")
    bevel = layer.get_param("bevel")
    bevCircle = layer.get_param("bevCircle")

    if expand.get() is None:    # Means filled rectangle layer
        st = "<param name='expand'><real value='0.0'/></param>"
        expand = etree.fromstring(st)
        expand = layer.add_param("expand", expand)

    if bevel.get() is None:     # For rectangle layer in stable version 1.2.2
        st = "<param name='bevel'><real value='0.0'/></param>"
        bevel = etree.fromstring(st)
        bevel = layer.add_param("bevel", bevel)
        st = "<param name='bevCircle'><bool value='false'/></param>"
        bevCircle = etree.fromstring(st)
        bevCircle = layer.add_param("bevCircle", bevCircle)

    # Animating point1
    point1.update_frame_window(window)
    point1.animate("vector")

    # Animating point2
    point2.update_frame_window(window)
    point2.animate("vector")

    # Animating expand
    expand.update_frame_window(window)
    expand.animate("real")

    # Animating bevel
    bevel.update_frame_window(window)
    bevel.animate("real")

    # Animating bevCircle
    bevCircle.update_frame_window(window)
    bevCircle.animate_without_path("bool")

    # Minimizing the window size
    if window["first"] == sys.maxsize and window["last"] == -1:
        window["first"] = window["last"] = 0
    ################# END OF SECTION 1 ###################

    ################ SECTION 2 ###########################
    # Generating values for all the frames in the window

    fr = window["first"]
    while fr <= window["last"]:
        st_val, en_val = insert_dict_at(lottie, -1, fr, False)

        synfig_rectangle(st_val, point1, point2, expand, bevel, bevCircle, fr)
        synfig_rectangle(en_val, point1, point2, expand, bevel, bevCircle, fr + 1)

        fr += 1
    # Setting the final time
    lottie.append({})
    lottie[-1]["t"] = fr


def synfig_rectangle(st_val, point1_p, point2_p, expand_p, bevel_p, bevCircle, fr):
    """
    Calculates the points for the rectangle layer as in Synfig:
    https://github.com/synfig/synfig/blob/678cc3a7b1208fcca18c8b54a29a20576c499927/synfig-core/src/modules/mod_geometry/rectangle.cpp

    Args:
        st_val (dict) : Lottie format rectangle will be stored in this
        point1_p (self.Param.Param) : Lottie format point1 animation
        point2_p (self.Param.Param) : Lottie format point2 animation
        expand_p (self.Param.Param) : Lottie format expand parameter animation
        bevel_p (self.Param.Param) : Lottie format bevel parameter animation
        bevCircle (lxml.etree._Element) : Animation of bevCircle in Synfig format
        fr (int) : Frame Number

    Returns:
        (None)
    """

    expand = abs(to_Synfig_axis(expand_p.get_value(fr), "real"))
    bevel = abs(to_Synfig_axis(bevel_p.get_value(fr), "real"))
    p0 = to_Synfig_axis(point1_p.get_value(fr), "vector")
    p0 = Vector(p0[0], p0[1])
    p1 = to_Synfig_axis(point2_p.get_value(fr), "vector")
    p1 = Vector(p1[0], p1[1])
    if p1[0] < p0[0]:
        p0[0], p1[0] = p1[0], p0[0]
    if p1[1] < p0[1]:
        p0[1], p1[1] = p1[1], p0[1]

    bev_circle = bevCircle.get_value(fr)

    w = p1[0] - p0[0] + 2*expand
    h = p1[1] - p0[1] + 2*expand
    bev = bevel
    if bevel > 1:
        bev = 1
    if bev_circle:
        bevx = min(w*bev/2.0, h*bev/2.0)
        bevy = min(w*bev/2.0, h*bev/2.0)
    else:
        bevx = w*bev/2.0
        bevy = h*bev/2.0

    # Setup chunk list
    chunk_list = []

    if approximate_equal(bevel, 0.0):
        chunk_list.append([Vector(p0[0] - expand, p0[1] - expand), Vector(), Vector()])
        chunk_list.append([Vector(p1[0] + expand, p0[1] - expand), Vector(), Vector()])
        chunk_list.append([Vector(p1[0] + expand, p1[1] + expand), Vector(), Vector()])
        chunk_list.append([Vector(p0[0] - expand, p1[1] + expand), Vector(), Vector()])
    else:
        cur = Vector(p1[0] + expand - bevx, p0[1] - expand)
        chunk_list.append([cur, Vector(), Vector()])
        prev = cur

        cur = Vector(p1[0] + expand, p0[1] - expand + bevy)
        cp1, cp2 = quadratic_to_cubic(cur, Vector(p1[0] + expand, p0[1] - expand), prev)
        chunk_list[-1][2] = cp2 - prev
        chunk_list.append([cur, cur - cp1, Vector()])
        prev = cur

        cur = Vector(p1[0] + expand, p1[1] + expand - bevy)
        chunk_list.append([cur, Vector(), Vector()])
        prev = cur

        cur = Vector(p1[0] + expand - bevx, p1[1] + expand)
        cp1, cp2 = quadratic_to_cubic(cur, Vector(p1[0] + expand, p1[1] + expand), prev)
        chunk_list[-1][2] = cp2 - prev
        chunk_list.append([cur, cur - cp1, Vector()])
        prev = cur

        cur = Vector(p0[0] - expand + bevx, p1[1] + expand)
        chunk_list.append([cur, Vector(), Vector()])
        prev = cur

        cur = Vector(p0[0] - expand, p1[1] + expand - bevy)
        cp1, cp2 = quadratic_to_cubic(cur, Vector(p0[0] - expand, p1[1] + expand), prev)
        chunk_list[-1][2] = cp2 - prev
        chunk_list.append([cur, cur - cp1, Vector()])
        prev = cur

        cur = Vector(p0[0] - expand, p0[1] - expand + bevy)
        chunk_list.append([cur, Vector(), Vector()])
        prev = cur

        cur = Vector(p0[0] - expand + bevx, p0[1] - expand)
        cp1, cp2 = quadratic_to_cubic(cur, Vector(p0[0] - expand, p0[1] - expand), prev)
        chunk_list[-1][2] = cp2 - prev
        chunk_list.append([cur, cur - cp1, Vector()])
        prev = cur

    add(chunk_list, st_val, [0, 0], True)
