# L-18 MCS 507 Mon 2 Oct 2023 : basic GUI for billiard ball

"""
This basic GUI to model a billiard ball consists
of a canvas, a start and stop button.
"""

from tkinter import Tk, Canvas, Button, W, E
from math import cos, sin, pi
from random import randint, uniform

class BilliardBall(object):
    """
    GUI to simulate billiard ball movement.
    """
    def __init__(self, wdw, dimension, increment, delay):
        """
        determines the layout of the GUI
        """
        wdw.title('a pool table')
        self.dim = dimension # dimension of the canvas
        self.inc = increment
        self.dly = delay
        self.togo = False # state of animation
        # initial coordinates of the ball
        self.xpos = randint(10, self.dim-10)
        self.ypos = randint(10, self.dim-10)
        self.cnv = Canvas(wdw, width=self.dim, \
            height=self.dim, bg ='green')
        self.cnv.grid(row=0, column=0, columnspan=2)
        self.startbut = Button(wdw, text='start', \
            command = self.start)
        self.startbut.grid(row=1, column=0, sticky=W+E)
        self.stopbut = Button(wdw, text='stop', \
            command = self.stop)
        self.stopbut.grid(row=1, column=1, sticky=W+E)

    def map_to_table(self, pos):
        """
        keeps the ball on the pool table
        """
        if pos < 0:
            (quot, remd) = divmod(-pos, self.dim)
        else:
            (quot, remd) = divmod(pos, self.dim)
        if quot % 2 == 1:
            remd = self.dim - remd
        return remd

    def draw_ball(self):
        """
        draws the ball on the pool table
        """
        xvl = self.map_to_table(self.xpos)
        yvl = self.map_to_table(self.ypos)
        self.cnv.delete('dot')
        self.cnv.create_oval(xvl-6, yvl-6, xvl+6, yvl+6, \
            width=1, outline='black', fill='red', \
            tags='dot')

    def animate(self):
        """
        performs the animation
        """
        self.draw_ball()
        angle = uniform(0, 2*pi)
        xdir = cos(angle)
        ydir = sin(angle)
        while self.togo:
            xvl = self.xpos
            yvl = self.ypos
            self.xpos = xvl + xdir*self.inc
            self.ypos = yvl + ydir*self.inc
            self.cnv.after(self.dly)
            self.draw_ball()
            self.cnv.update()

    def start(self):
        """
        starts the animation
        """
        self.togo = True
        self.animate()

    def stop(self):
        """
        stops the animation
        """
        self.togo = False

def main():
    """
    launches the GUI
    """
    top = Tk()
    dimension = 400 # dimension of pool table
    increment = 10  # increment for coordinates
    delay = 60      # how much sleep before update
    BilliardBall(top, dimension, increment, delay)
    top.mainloop()

if __name__ == "__main__":
    main()
