"""
This is a port of http://code.google.com/p/pymunk/wiki/SlideAndPinJointsExample to
NodeBox, instead of PyGame, as in the tutorial.
"""

print "-- START --"

import pymunk as physics
import math

global width, height
width = 600
height = 600

size(width, height)

speed(50) # Bigger numbers go faster ;-)

def setup():
    global world
    global width, height
    
    world = Stage(width, height)

def draw():
    global world
    
    world.update()
    world.draw()


class Ball(object):
    def __init__(self):
        self.mass = 1
        self.radius = 14
        
        # All bodies must have their moment of inertia set. If our object is 
        # a normal ball we can use the predefined function moment_for_circle
        # to calculate it given its mass and radius. 
        self.inertia = physics.moment_for_circle(self.mass, 0, self.radius, (0,0))
        
        # After we have the inertia we can create the body of the ball.
        self.body = physics.Body(self.mass, self.inertia)
        
        self.x = random(600)
        self.y = 600
        
        # set its position
        self.body.position = self.x, self.y
        
        # In order for it to collide with things, it needs to have one (or many) collision shape(s)
        self.shape = physics.Circle(self.body, self.radius, (0,0))


class Stage(object):
    def __init__(self, width, height):
        self.width = width
        self.height = height
        
        # The first thing we should do before anything else is to initialize pymunk
        physics.init_pymunk()
    
        # We then create a space and set its gravity to something good. 
        self.space = physics.Space()
        self.space.gravity = (0.0, -900.0)
        
        self.balls = []
        self.lines = []
        
        self.add_lines()

    def add_lines(self):
        # We create a "static" body with infinite mass and inertia. The important step is to never
        # add it to the space. pm.inf is actually just a variable (1e100) so big it is converted
        # to infinity, and you can use 1e100 or another big number if it makes you feel better :) 
        rotation_center_body = physics.Body(physics.inf, physics.inf)
        rotation_center_body.position = (300, 300)
        
        #rotation_limit_body = physics.Body(physics.inf, physics.inf)
        #rotation_limit_body.position = (200, 200)
        
        body = physics.Body(10, 10000)
        body.position = (300, 300)
        
        # A line shaped shape is created here.
        l1 = physics.Segment(body, (-150.0, 0), (255.0, 0.0), 5.0)
        l2 = physics.Segment(body, (-150.0, 0), (-150.0, 50.0), 5.0)
        
        # A pin joint allow two objects to pivot about a single point. In our case one of the
        # objects will be stuck to the world.
        rotation_center_joint = physics.PinJoint(body, rotation_center_body, (0, 0), (0, 0))

        # Create a slide joint. It behaves like pin joints but have a minimum and maximum distance. 
        #joint_limit = 25
        #rotation_limit_joint = physics.SlideJoint(body, rotation_limit_body, (-100, 0), (0, 0), 0, joint_limit)
        
        # Remember to not add the body to the shape as we want it to be static
        self.space.add(l1, l2, body, rotation_center_joint) #, rotation_limit_joint)

        self.lines.append(l1)
        self.lines.append(l2)

    def add_ball(self):
        print "Adding a Ball!"
        b = Ball()
        
        # Add the body and shape to the space to include it in our simulation
        self.space.add(b.body, b.shape)

        self.balls.append(b)

    def update(self):
        # In our main loop we call the step() function on our space.
        # It will do a physics step. Note: It is best to keep the stepsize
        # constant and not adjust it depending on the framrate. The physic
        # simulation will work much better with a constant step size.
        self.space.step(1/50.0)
        
        if (random(10) > 8):
            self.add_ball()
    
    def draw(self):
        stroke(0.1)
        
        # Hard-code draw the pivot
        oval(295, self.height-305, 10, 10)
        
        # Hard-code draw the limit thing
        #oval(200, self.height-375, 75, 75)
        
        for l in self.lines:
            e1 = l.body.position + l.a.rotated(math.degrees(l.body.angle))
            e2 = l.body.position + l.b.rotated(math.degrees(l.body.angle))

            line(e1.x, self.height - e1.y, e2.x, self.height - e2.y)
        
        fill(0.6, 0.0, 0.0)

        dead_balls = []
        
        for ball in self.balls:
            if (ball.body.position.y < 100):
                dead_balls.append(ball)
                continue

            #print "Draw at", ball.body.position.x, ",", ball.body.position.y
            oval(ball.body.position.x, self.height - (ball.body.position.y + 20), 20, 20)

        for ball in dead_balls:
            self.space.remove(ball.shape, ball.body)
            self.balls.remove(ball)


