#!/usr/bin/python
__doc__ = '''A flying saucers game (vertical scrolling shoot'em-up). 
Version 5 adds a persistent scoreboard using the JSON serialization library. as well as an initial splash screen'''

import pygame
import pygame.event
from pygame.locals import *

from random import randint

X, Y = 500, 500 # Controls the screen size
FPS = 30 # Controls the timing, one frame every FPS milliseconds
S = 20 # Controls the step size in pixels
F = 10 # Controls the frequency of enemies. One enemy appears every 1/F T time units
FONTSIZE = 36

def rand_color():
	return randint(0,255),randint(0,255), randint(0,255)

class Saucer(object):
	'''Description of a saucer, characterized by position (x,y), radius r, and color (r,g,b)'''
	def __init__(self, size=None, color=None, pos=None):
		if not size : size=randint(10,40)
		if not color : color=rand_color()
		if not pos : pos=(randint(0,X),0)
		self.radius=size
		self.color=color
		self.pos=pos

	def collide(self, other):
		from math import sqrt
		x1, y1 = self.pos
		x2, y2 = other.pos
		dist=sqrt((x1-x2)**2+(y1-y2)**2)
		if dist<self.radius+other.radius :
			return True
		return False

class Missile(Saucer):
	'''Missile objects are similar to Saucers, but simpler'''
	def __init__(self, pos):
		self.radius=5
		self.pos=pos
		self.color=(250,100,100)
		

class GameBoard(object):
	'''Description of the game board and all the related functionalities'''
	def __init__(self, X,Y):
		self.X=X
		self.Y=Y
		pygame.init()
		# Display and other intializations
		self.srect=Rect(0,0,self.X, self.Y)
		self.screen=pygame.display.set_mode(self.srect.size,0)
		self.bg = pygame.Surface(self.srect.size)
		self.clock=pygame.time.Clock()
		pygame.display.set_caption('Saucers')
		pygame.key.set_repeat(FPS,FPS)
		self.load_scoreboard()

	def new_game(self):
		# Game actors
		self.player=Saucer(30, (0,120,255), (self.X/2, self.Y/2))
		self.objects=[]
		self.missiles=[]
		# Score
		self.kills=0
		self.fired=0
		self.objs=0
		self.player_name='player'

	def __call__(self):
		pygame.event.clear()
		while True :
			self.screen.fill((20,20,20))
			self.print_text_to_line("The Saucers Game",0,size=2)
			self.print_text_to_line('Press "Enter" to start game',2)
			self.print_text_to_line('Press "Shift" to see the scoreboard',3)
			self.print_text_to_line('Press "Q" to quit',4)
			pygame.display.update()
			for e in pygame.event.get():
				print e
				if e.type==KEYDOWN :
					cmd=pygame.key.get_pressed()
					if cmd[K_RETURN] : 
						self.new_game()
						self.run()
						self.save_scoreboard()
					if cmd[K_RSHIFT]: self.print_scoreboard()
					if cmd[K_q] : exit(1)
		
	def load_scoreboard(self):
		import json
		try : 
			sb_file = open("scoreboard.json","r")
			self.scoreboard = json.load(sb_file)
			sb_file.close()
			self.scoreboard.sort(key=lambda x: x[1],reverse=True)
			self.scoreboard=self.scoreboard[:12]
		except Exception :
			self.scoreboard = []
		
	def save_scoreboard(self):
		import json
		self.scoreboard.append((self.player_name,self.kills,self.fired,self.objs))
		self.scoreboard.sort(key=lambda x: x[1],reverse=True)
		self.scoreboard=self.scoreboard[:12]
		sb_file = open("scoreboard.json","w")
		json.dump(self.scoreboard,sb_file)
		sb_file.close()

	def print_text_to_line(self, s, line, size=1):
		font = pygame.font.Font(None, FONTSIZE*size)
		text = font.render(s, 1, (100, 200, 200))
		textpos = text.get_rect()
		textpos.centerx = self.srect.centerx
		textpos.top = self.srect.top + (line * FONTSIZE)
		self.screen.blit(text, textpos)

	def print_scoreboard(self):	
		self.screen.fill((0,0,0))
		self.print_text_to_line('Player     Kills     Fired     Saucers',0)
		i=0
		for player, kills, fired, objs in self.scoreboard :
			i+=1
			self.print_text_to_line(player+'     '+`kills`+'     '+`fired`+'     '+`objs`,i)
		pygame.display.update()
		pygame.event.clear()
		while True :
			for e in pygame.event.get():
				if e.type==KEYDOWN :
					return
			
	def blitAll(self):
		# Draw black screen
		self.screen.fill((0,0,0))
		for o in self.objects:
			pygame.draw.circle(self.screen, o.color, o.pos, o.radius)
		for o in self.missiles:
			pygame.draw.circle(self.screen, o.color, o.pos, o.radius)
		pygame.draw.circle(self.screen, self.player.color, self.player.pos, self.player.radius)
		pygame.display.update()

	def random_gen(self, freq=F):
		if randint(1,freq)==1:
			self.objs+=1
			self.objects.append(Saucer())

	def move_all(self):
		for o in self.objects :
			x, y = o.pos
			o.pos = x, y+S
		self.objects = [ o for o in self.objects if o.pos[1]<self.Y and o.pos[0]<self.X ]
		for o in self.missiles :
			x, y = o.pos
			o.pos = x, y-(3*S/2)
		self.missiles = [ o for o in self.missiles if o.pos[1]<self.Y and o.pos[0]<self.X and o.pos[1]>0 and o.pos[0]>0 ]

	def collision(self):
		for o in self.objects :
			if self.player.collide(o):
				print "Collision!!"
				for r in range(0,255,10):
					pygame.draw.circle(self.screen, (r,0,0), self.player.pos, r)
					self.clock.tick(FPS)
					pygame.display.update()
				return True
		return False

	def object_collision(self):
		self.kills += len([ o for o in self.objects if sum([ o.collide(p) for p in self.missiles]) ])
		self.objects = [ o for o in self.objects if not sum([ o.collide(p) for p in self.missiles]) ]

	def constrain_pos(self):
		'''limit position to 0-X, 0-Y'''
		x, y = self.player.pos
		x = self.X if x > self.X else x
		x = 0 if x < 0 else x
		y = self.Y if y>self.Y else y
		y = 0 if y < 0 else y
		self.player.pos = x, y
		
	def run(self):
		while True :
			self.random_gen()
			for e in pygame.event.get():
				if e.type==QUIT : self.quit()
				if e.type==KEYDOWN :
					cmd=pygame.key.get_pressed()
					x, y = self.player.pos
					if cmd[K_q] : return
					if cmd[K_SPACE] or cmd[K_f] : 
						self.fired+=1
						self.missiles.append(Missile(self.player.pos))
					if cmd[K_a] or cmd[K_LEFT] : self.player.pos = x-S, y
					if cmd[K_d] or cmd[K_RIGHT] : self.player.pos = x+S, y
					if cmd[K_s] or cmd[K_DOWN] : self.player.pos = x, y+S
					if cmd[K_w] or cmd[K_UP] : self.player.pos = x, y-S
					self.constrain_pos()
			self.blitAll()
			self.object_collision()
			if self.collision() : return
			self.clock.tick(FPS)
			self.move_all()

if __name__ == "__main__" :
	game=GameBoard(X,Y)
	game()
