"""
    Анимация квадратов
    https://stepik.org/lesson/468998/step/4?unit=459819

    3: "Квадрат двигается справа налево"
    4: "Два квадрата двигаются в противоположных направлениях"

    Библиотека graph: https://kpolyakov.spb.ru/download/pygraph.zip
"""

from graph import *

from typing import NamedTuple


class Point(NamedTuple):
    """
    Вспомогательный класс для упрощения работы с координатами (сложение)
    """

    x: int | float
    y: int | float

    def __add__(self, other):
        if isinstance(other, self.__class__):
            return self.__class__(self.x + other.x, self.y + other.y)
        if isinstance(other, (int, float)):
            return self.__class__(self.x + other, self.y + other)
        return NotImplemented


def make_title(n, name, start, font=("Arial", 15), n_color="indigo"):
    pos = Point(10, 20) + start
    n_width = Point(35, 0)
    l1 = label(f"«{n}»", *pos, font=font, foreground=n_color)
    l2 = label(f": {name}", *(pos + n_width), font=font)
    return l1, l2


def square_left_to_right(start_pos=None):
    start_pos = start_pos or Point(0, 0)

    make_title(3, "Квадрат двигается справа налево", start_pos)
    title_height = Point(0, 60)

    start_pos = start_pos + title_height
    scene_sz = Point(300, 300)
    box_sz = Point(40, 40)
    box_offset = Point(220, 5)

    brushColor("Blue")
    rectangle(*start_pos, *(start_pos + scene_sz))  # Размер фона
    brushColor("Yellow")
    obj = rectangle(
        *(start_pos + box_offset), *(start_pos + box_offset + box_sz)
    )  # Создаем объект, который у нас в виде прямоугольника

    data = {"done": False}  # состояние объекта для контроля таймера из вне функции

    def update():  # Создаем процедуру, которая отвечает за анимацию
        # Она будет перемещать объект obj по оси x на -5 по оси y на 0 пикселей
        moveObjectBy(obj, -5, 0)
        if xCoord(obj) <= 1:  # Если x координата меньше 1
            killTimer(timer)
            data["done"] = True

    timer = onTimer(update, 40)  # Каждые 50 мс будет запускаться процедура update
    return (data,)


def two_squares(start_pos=None):
    start_pos = start_pos or Point(0, 0)

    make_title(4, "Два квадрата двигаются в\nпротивоположных направлениях", start_pos)
    title_height = Point(0, 90)

    start_pos = start_pos + title_height
    scene_sz = Point(300, 300)
    box_sz = Point(40, 40)
    box1_offset = Point(220, 5)
    box2_offset = Point(0, 250)

    brushColor("Blue")
    rectangle(*start_pos, *(start_pos + scene_sz))
    brushColor("Yellow")
    box1 = rectangle(*(start_pos + box1_offset), *(start_pos + box1_offset + box_sz))
    box2 = rectangle(*(start_pos + box2_offset), *(start_pos + box2_offset + box_sz))
    timer1 = None
    timer2 = None

    data1 = {"done": False}
    data2 = {"done": False}

    def update_box1():
        moveObjectBy(box1, -5, 0)
        if xCoord(box1) <= 1:
            killTimer(timer1)
            data1["done"] = True

    def update_box2():
        moveObjectBy(box2, 5, 0)
        if xCoord(box2) >= scene_sz.x - box_sz.x:
            killTimer(timer2)
            data2["done"] = True

    timer1 = onTimer(update_box1, 40)
    timer2 = onTimer(update_box2, 40)

    return data1, data2


def main():
    canvasSize(400, 800)
    state = [*square_left_to_right(), *two_squares(Point(0, 350))]

    watch_timer = None

    # выход по завершении всех анимаций
    def watch_done():
        if all(x["done"] for x in state):
            killTimer(watch_timer)
            close()

    watch_timer = onTimer(watch_done, 6000)
    run()


main()