# -*- coding: utf-8 -*-
"""
８個のボールが動き回る（反射ブロックを追加）（加点）
"""
# 加点なし
# 運動方向を完全に反転させているので反射としては不自然

import tkinter as tk
import random


# 個々のボールの情報は専用のクラスに
class Ball:
    r = 20  # ボールの半径（当たり判定に使用）

    # クリックされたところにボールを生成
    def __init__(self, event:tk.Event):
        self.x = event.x
        self.y = event.y
        self.canvas:tk.Canvas = event.widget
        # 移動速度は乱数
        self.dx = random.random()*20-10
        self.dy = random.random()*20-10
        # x,y を中心にするため，±rで円を描く
        self.id = self.canvas.create_oval(self.x-self.r, self.y-self.r, self.x+self.r, self.y+self.r)
        # 動作開始
        self.move()

    # 動き回らせる
    # まとめて扱ってもよいが，こちらの方がオブジェクト指向的
    def move(self):
        # 横方向の移動．端に行ったら移動を反転＋乱数
        self.x += self.dx
        if   self.x-self.r < 0:
            self.dx =  abs(self.dx) + random.random()*5-2
        elif self.x+self.r > self.canvas.winfo_width():
            self.dx = -abs(self.dx) + random.random()*5-2

        """ 指示ここから """
        # このコメントブロックの下に，
        # 縦方向の移動．端に行ったら移動を反転＋乱数
        # という動きになるよう，命令文を書きなさい．
        # なお，キャンバスの縦幅は .winfo_height() で取得できる．

        self.y += self.dy
        if   self.y-self.r < 0:
            self.dy =  abs(self.dy) + random.random()*5-2
        elif self.y+self.r > self.canvas.winfo_height():
            self.dy = -abs(self.dy) + random.random()*5-2
        """ 指示ここまで """

        # 移動を反映し，次を呼び出し
        self.canvas.moveto(self.id, self.x-self.r, self.y-self.r)
        self.canvas.after(50, self.move)
    
    # ボールの色を変える
    def change_fill(self, color):
        self.canvas.itemconfig(self.id, fill=color)


# ボール全体の情報をキャンバスに持たせる
class CanvasBox(tk.Canvas):

    # キャンバスの初期設定
    def __init__(self, master):
        super().__init__(master, width=400, height=300)
        # ここからブロックの追記
        self.bl_x0 = 100
        self.bl_y0 = 100
        self.bl_x1 = 300
        self.bl_y1 = 200
        self.create_rectangle(self.bl_x0, self.bl_y0, self.bl_x1, self.bl_y1, fill="blue")
        # ここまで
        self.ball_id:list[Ball] = []
        self.bind("<Button>", self.add_ball)
        self.wrap_ball()
        self.ref_block()

    # ボールを追加するが，８個を超えたら古いものを消す
    def add_ball(self, event:tk.Event):
        self.ball_id.append(Ball(event))
        if len(self.ball_id) > 8:
            # list型データに .pop(0) をすると
            # 先頭要素が取り出され，かつ，list内部から消える
            self.delete(self.ball_id.pop(0).id)
    
    # ボールは黒色だが，重なると赤色にする
    # 個々のボールで行ってもよいが，個から全体を把握するのは難しい
    def wrap_ball(self):
        for ball1 in self.ball_id:
            color = "black"  # デフォルトは黒色
            for ball2 in self.ball_id:
                # 自分以外のボールとの二乗距離が一定以下なら赤色
                if ball1 != ball2:
                    dx2 = (ball1.x - ball2.x)**2
                    dy2 = (ball1.y - ball2.y)**2
                    # ball1もball2も半径が20なので，二乗距離は1600
                    if (dx2 + dy2) < (ball1.r + ball2.r)**2:
                        color = "red"
                        break
            ball1.change_fill(color)
        
        # 次を呼び出し
        self.after(50, self.wrap_ball)
    
    # ブロックでの反射
    def ref_block(self):
        for ball1 in self.ball_id:
            if ((self.bl_x0-ball1.r) < ball1.x < (self.bl_x1+ball1.r)) \
              and ((self.bl_y0-ball1.r) < ball1.y < (self.bl_y1+ball1.r)):
                # とりあえず反転
                ball1.dx = - ball1.dx
                ball1.dy = - ball1.dy
        # 次を呼び出し
        self.after(50, self.ref_block)



def main():
    base = tk.Tk()
    base.title("$u_id")

    canvas = CanvasBox(base)
    canvas.pack()

    base.mainloop()  # GUIを待機


if __name__ == "__main__":
    main()
