#!/usr/bin/python

import wx
from time import sleep
from thread import start_new_thread
from random import randrange
from math import sin,cos,pi,sqrt


CONST_DT        = 0.1
CONST_G         = 9.8
CONST_EY        = 0.8
CONST_EX        = 0.8
CONST_ALPHA     = 2.0/5.0
CONST_NBALLS    = 5
CONST_STOPAT    = 50.0

class BallPanel(wx.Panel):
    def __init__(self,parent):
        wx.Panel.__init__(self,parent)
        self.Bind(wx.EVT_PAINT,     self.OnPaint)
        self.Bind(wx.EVT_TIMER,     self.OnTimer)

        self.SetBackgroundColour("white")
        self.timer = wx.Timer(self)
        self.timer.Start(10)

        self.balls = []
        a = pi/(2.0*CONST_NBALLS)
        for i in range(CONST_NBALLS):
            b = Ball(35.0,35.0,10.0)
            b.velocity = [50.0*cos(i*a),0.0]
            self.balls.append(b)
        self.time = 0
        self.active = True

    def Start(self):
        start_new_thread(self.Simulate,())

    def OnPaint(self, evt):
        dc = wx.PaintDC(self)
        for i in range(CONST_NBALLS):
            self.balls[i].DrawHistory(dc)
        for i in range(CONST_NBALLS):
            self.balls[i].Draw(dc)
        dc.SetPen(wx.Pen(wx.Colour(0,0,0)))
        dc.DrawText("time: %.2f" % self.time,2,2)
        
    def OnTimer(self, evt):
        self.Refresh(True)

    def Simulate(self):
        while self.active:
            self.time += CONST_DT
            for i in range(CONST_NBALLS):
                self.balls[i].Update(CONST_DT,self.GetClientSizeTuple())
                if not self.active: break
            if not self.active: break
            if self.time >= CONST_STOPAT: break
            sleep(0.1)

class Ball:
    def __init__(self,x=0,y=0,r=20):
        self.position = [x,y]
        self.velocity = [0.0,0.0]
        self.r = r
        self.history = []

    def DrawHistory(self,dc):
        dc.SetPen(wx.Pen(wx.Colour(255,0,0)))
        dc.DrawLines(self.history)

    def Draw(self,dc):
        dc.SetBrush(wx.Brush(wx.Colour(102,102,102)))
        dc.DrawCircle(self.position[0],self.position[1],self.r)

    def Update(self,dt,bounds):
        self.velocity[1] += CONST_G * dt
        self.position[0] += self.velocity[0]*dt
        self.position[1] += self.velocity[1]*dt

        if (self.position[1] + self.r) >= bounds[1]:
            self.velocity[1] *= -CONST_EY
            self.position[1] = bounds[1]-self.r
        elif (self.position[1] - self.r) <= 0:
            self.velocity[1] *= -CONST_EY
            self.position[1] = self.r

        if (self.position[0] + self.r) >= bounds[0]:
            self.velocity[0] *= -CONST_EX
            self.position[0] = bounds[0]-self.r
        elif (self.position[0] - self.r) <= 0:
            self.velocity[0] *= -CONST_EX
            self.position[0] = self.r

        self.history.append(wx.Point(self.position[0],self.position[1]))

class BallApp(wx.App):
    def __init__(self,w,h):
        wx.App.__init__(self,0)
        frm = wx.Frame(None)
        frm.SetClientSize(wx.Size(w,h))
        frm.Bind(wx.EVT_CLOSE, self.OnFrameClose)
        self.bpn = BallPanel(frm)
        frm.Show(True)
        self.bpn.Start()

    def OnFrameClose(self,evt):
        self.bpn.active = False
        self.ExitMainLoop()

    def OnExit(self):
        self.bpn.active = False

if __name__ == '__main__':
    app = BallApp(800,600)
    app.MainLoop()
