#!/usr/bin/env python3

## The Game of Life using pyglet. A quite slow implementation.

import pyglet
from pyglet.window import key
import numpy


class Application(pyglet.window.Window):

	def __init__(self):
		"""Window initialization"""
		super().__init__(caption="The Game of Life", resizable=True)
		self.size = 10
		self.cells = numpy.zeros((self.width // self.size,
								self.height // self.size))
		self.running = False
		self.full = False
		self.frequency = 1 / 120
		pyglet.gl.glClearColor(0, 0.13, 0.16, 1.0)

	def on_mouse_press(self, x, y, button, modifiers):
		"""Turn alive the cell under the pointer"""
		value = False if modifiers == key.MOD_COMMAND else True
		if x >= 0 and y >= 0:
			try:
				self.cells[x // self.size][y // self.size] = value
			except IndexError:
				pass

	def on_mouse_drag(self, x, y, dx, dy, button, modifiers):
		"""Turn cells alive while dragging"""
		self.on_mouse_press(x, y, button, modifiers)

	def on_mouse_scroll(self, x, y, scroll_x, scroll_y):
		self.size += scroll_y
		if self.size == 0:
			self.size += 1
		self.resize_space()

	def on_key_press(self, button, modifiers):
		# Toggle update
		if button == key.SPACE:
			if self.running:
				pyglet.clock.unschedule(self.update)
				self.running = False
			else:
				pyglet.clock.schedule_interval(self.update, self.frequency)
				self.running = True
		# Reset space
		elif button == key.BACKSPACE:
			self.cells.fill(0)
		# Toggle fullscreen
		elif modifiers == key.MOD_COMMAND and button == key.F:
			if self.fullscreen:
				self.set_fullscreen(False)
				self.full = False
			else:
				self.set_fullscreen()
				self.full = True
		# Change update frequency
		if button == key.SLASH:
			self.frequency += 0.1
			if self.running:
				pyglet.clock.unschedule(self.update)
				pyglet.clock.schedule_interval(self.update, self.frequency)
		elif button == key.BRACKETRIGHT:
			self.frequency -= 0.1
			if self.running:
				pyglet.clock.unschedule(self.update)
				pyglet.clock.schedule_interval(self.update, self.frequency)

	def on_resize(self, width, height):
		super().on_resize(width, height)
		self.resize_space()

	def on_draw(self):
		"""Draw the frame"""
		self.clear()
		pyglet.gl.glColor3f(0.15, 0.6, 0.5)
		for x, row in enumerate(self.cells):
			for y, cell in enumerate(row):
				if cell:
					self.draw_cell(x, y)
		if self.size > 7:
			self.draw_grid()

	def resize_space(self):
		self.cells.resize((self.width // self.size, self.height // self.size))

	def draw_grid(self):
		"""Draw the grid"""
		pyglet.gl.glColor3f(0.06, 0.17, 0.21)
		for i in range(self.height // self.size):
			pyglet.graphics.draw(2, pyglet.gl.GL_LINES,
			('v2i', (0, i * self.size, self.width, i * self.size)))
		for j in range(self.width // self.size):
			pyglet.graphics.draw(2, pyglet.gl.GL_LINES,
			('v2i', (j * self.size, 0, j * self.size, self.height)))

	def draw_cell(self, x, y):
		"""Draw a square"""
		x, y = x * self.size, y * self.size
		x1, y1, x2, y2 = x, y, x + self.size, y + self.size
		pyglet.graphics.draw(4, pyglet.gl.GL_QUADS,
			('v2f', (x1, y1, x1, y2, x2, y2, x2, y1)))

	def update(self, dt):
		"""Calculate the next frame"""
		copy = numpy.copy(self.cells)
		for x, row in enumerate(self.cells):
			for y, cell in enumerate(row):
				copy[x][y] = self.alive(x, y)
		self.cells = numpy.copy(copy)

	def alive(self, x, y):
		"""Calculate if a cell will be alive in the next frame"""
		livings = 0
		for i in (-1, 0, 1):
			for j in (-1, 0, 1):
				try:
					if (self.cells[x + i][y + j]
						and x + i > 0 and y + j > 0):
						livings += 1
				except IndexError:
					continue
		if self.cells[x][y]:
			livings -= 1
		if not self.cells[x][y] and livings == 3:
			return 1
		elif self.cells[x][y] and livings in (2, 3):
			return 1
		else:
			return 0

app = Application()
pyglet.app.run()