import sys
import math
import random
import pyglet

# ask for a plain 640x480 window
w = pyglet.window.Window()

def center_anchor(img):
    '''Center the anchor of the given image.'''
    img.anchor_x = img.width // 2
    img.anchor_y = img.height // 2

# load the ship image, center the anchor
ship_image = pyglet.image.load('data/ship.png')
center_anchor(ship_image)
# create a sprite with that image, intial position in the center of the
# screen and not moving or rotating
ship = pyglet.sprite.Sprite(ship_image)
ship.position = (w.width/2, w.height/2)
ship.dx = ship.dy = ship.dr = 0
ship.gun_cooldown = 0

# load the bullet image for later sprites, center anchored
bullet_image = pyglet.image.load('data/bullet.png')
center_anchor(bullet_image)
# keep track of the currently-active bullet sprites
bullets = []

# load the three sizes of asteroid image and center their anchors
small_asteroid_image = pyglet.image.load('data/small_asteroid.png')
center_anchor(small_asteroid_image)
medium_asteroid_image = pyglet.image.load('data/medium_asteroid.png')
center_anchor(medium_asteroid_image)
big_asteroid_image = pyglet.image.load('data/big_asteroid.png')
center_anchor(big_asteroid_image)
# keep track of the currently-active asteroid sprites
asteroids = []
# create 4 starting big asteroids with random positioning, speed and
# rotation
for i in range(3):
    x = random.randint(0, w.width)
    y = random.randint(0, w.height)
    s = pyglet.sprite.Sprite(big_asteroid_image, x, y)
    s.dx = random.randint(-100, 100)
    s.dy = random.randint(-100, 100)
    s.dr = random.randint(-100, 100)
    asteroids.append(s)

# load up our sound effects
explosion_sound = pyglet.media.load('data/explosion.wav', streaming=False)
bullet_sound = pyglet.media.load('data/bullet.wav', streaming=False)

# set up the game's drawing event handler
@w.event
def on_draw():
    '''Draw the game to the screen.'''
    w.clear()
    for s in asteroids + bullets:
        s.draw()
    ship.draw()

# use the standard pyglet keyboard state event handler
keys = pyglet.window.key.KeyStateHandler()
w.push_handlers(keys)

def distance(a, b):
    '''Determine the distance between two points.'''
    return math.sqrt((a.x-b.x)**2 + (a.y-b.y)**2)
def collide(a, b):
    '''Determine whether two objects with a center point and width
    (diameter) are colliding.'''
    return distance(a, b) < (a.width/2 + b.width/2)

def update(dt):
    '''Update the game for the "dt" seconds that have passed.'''
    # update the ship's speed of rotation based on the combination of
    # left/right arrow being held down
    ship.dr = (keys[pyglet.window.key.RIGHT] - keys[pyglet.window.key.LEFT]) * 360
    # determine the radian angle of rotation and thereby the X and Y
    # components of the ship's heading
    rotation = math.pi * ship.rotation / 180.0
    rotation_x = math.cos(-rotation)
    rotation_y = math.sin(-rotation)
    # if the thrust (up) key is pressed then accelerate in the current
    # heading by increasing the ship's dx/dy
    if keys[pyglet.window.key.UP]:
        ship.dx += 200 * rotation_x * dt
        ship.dy += 200 * rotation_y * dt

    # if the ship's gun has recently been fired then cool it down,
    # otherwise fire a new bulled if the fire (space) key has been pressed
    if ship.gun_cooldown:
        ship.gun_cooldown = max(0, ship.gun_cooldown - dt)
    elif keys[pyglet.window.key.SPACE] and len(bullets) < 2:
        # the gun is firing so create a new bullet over the ship with a
        # speed of roughly 500 pixels per second in the same heading as the
        # ship.
        b = pyglet.sprite.Sprite(bullet_image, ship.x,
            ship.y)
        b.dx = rotation_x * 500
        b.dy = rotation_y * 500
        b.dr = 0
        # set the bullet to live for 1 second
        b.life = 1
        # retain the bullet in our bullet sprites list
        bullets.append(b)
        # set the cooldown to 2 shots per second
        ship.gun_cooldown = .5
        # play a nice sound effect
        bullet_sound.play()

    # kill off any old bullets
    for b in list(bullets):
        b.life -= dt
        if b.life < 0:
            bullets.remove(b)   

    # update the position and rotation of all our sprites based on their
    # dx, dy and dr attributes
    for a in asteroids + [ship] + bullets:
        a.x += a.dx*dt
        a.y += a.dy*dt
        a.rotation += a.dr * dt
        # wrap all sprites once they're off the screen
        if a.x - a.width/2 > w.width:
            a.x -= w.width + a.width
        elif a.x + a.width/2 < 0:
            a.x += w.width + a.width
        if a.y - a.height/2 > w.height:
            a.y -= w.height + a.height
        elif a.y + a.height/2 < 0:
            a.y += w.height + a.height

    # check collisions with the asteroids
    for a in list(asteroids):
        # if the ship collides then it's GAME OVER
        if collide(a, ship):
            sys.exit('GAME OVER')
        # if any bullet collides then the asteroid is damaged/destroyed
        for b in list(bullets):
            if collide(a, b):
                bullets.remove(b)
                asteroids.remove(a)
                explosion_sound.play()
                if a.image is big_asteroid_image.texture:
                    # if it's a big asteroid then make two medium asteroids in
                    # its place
                    for i in range(2):
                        s = pyglet.sprite.Sprite(medium_asteroid_image, a.x, a.y)
                        # modify the parent speed/rotation by some random
                        # amount
                        s.dx = a.dx + random.randint(-50, 50)
                        s.dy = a.dy + random.randint(-50, 50)
                        s.dr = a.dr + random.randint(-50, 50)
                        asteroids.append(s)
                elif a.image is medium_asteroid_image.texture:
                    # if it's a medium asteroid then make two small asteroids in
                    # its place
                    for i in range(2):
                        s = pyglet.sprite.Sprite(small_asteroid_image, a.x, a.y)
                        # modify the parent speed/rotation by some random
                        # amount
                        s.dx = a.dx + random.randint(-50, 50)
                        s.dy = a.dy + random.randint(-50, 50)
                        s.dr = a.dr + random.randint(-50, 50)
                        asteroids.append(s)

    # if there's no more asteroids then the player has won!
    if not asteroids:
        sys.exit('YOU WIN')

# schedule our update function to be called 30 times per second
pyglet.clock.schedule_interval(update, 1./30)

# invoke the pyglet runtime (event handling, clock ticking)
pyglet.app.run()