# Python 2.7.7 Code
# Pygame 1.9.1 (for Python 2.7.7)
# Jonathan Frech 15th of April, 2016
#         edited 16th of April, 2016

# importing needed modules
import pygame, sys, time, math, os, random, re

# load an image
IMAGE_PATH = os.getcwd() + "/sliding-puzzle_apple.png"

""" CLASSES """
# piece class
class Piece():
	# initialize
	def __init__(self, gamepos, n):
		self.n = n
		self.size = [200, 200]
		self.setgamepos(gamepos)
		self.pos = [random.randint(0, G.width - self.size[0]), random.randint(0, G.height - self.size[1])]
		
		
		self.surface = pygame.Surface(self.size)
		self.surface.blit(G.image, [-self.gamepos[0] * self.size[0], -self.gamepos[1] * self.size[1]])

	# gamepos in range [0:3] and [0:3]
	def setgamepos(self, gamepos):
		self.gamepos = gamepos
		self.dest = [self.gamepos[0] * self.size[0], self.gamepos[1] * self.size[1]]

	# check if piece was clicked
	def clicked(self, gamepos):
		if gamepos[0] == self.gamepos[0] and gamepos[1] == self.gamepos[1]:
			return True

		return False

	# tick
	def tick(self):
		for _ in range(0, 2):
			dif = (self.dest[_] - self.pos[_]) / 10.
			if abs(dif) < .1:
				self.pos[_] = self.dest[_]
			else:
				self.pos[_] += dif

	# render
	def render(self, surface):
		surface.blit(self.surface, intpos(self.pos))

""" FUNCTIONS """
# returns an integer version of given positon
def intpos(_pos):
	return [int(_pos[0]), int(_pos[1])]

# load an image from string
def loadimg(string = None):
	# standard string
	if not string:
		string = "(1, 1)<<<Here the stringified image would go... The only problem is that that would men 5,000,000 characters...>>>"

	# figure out size
	size = re.findall("\(\d*, \d*\)", string)[0]
	string = string[len(size):len(string)]
	size = size[1:len(size)-1]
	size = re.split(", ", size)
	size = [int(size[0]), int(size[1])]
	
	# draw image
	img = pygame.Surface(size)
	for _ in range(0, len(string), 3):
		img.set_at([(_/3)%size[0], (_/3)/size[1]], [ord(string[_]), ord(string[_+1]), ord(string[_+2])])

	# return
	return img

# save an image to string
def getimgstr(path):
	# load image
	image = pygame.image.load(os.getcwd() + "/" + path)
	rect = image.get_rect()
	
	# generate string
	string = "(" + str(rect.right) + ", " + str(rect.bottom) + ")"
	for y in range(0, rect.bottom):
		for x in range(0, rect.right):
			color = image.get_at([x, y])
			string += chr(color[0]) + chr(color[1]) + chr(color[2])

	# escape and return
	return string#.encode("string-escape")


""" GAME """
# game class
class GAME():
	# initialize program
	def __init__(self):
		self.width, self.height = 800, 800
		self.size = [self.width, self.height]
		self.surface = pygame.Surface(self.size)
		self.screen = pygame.display.set_mode(self.size)

		self.ticks = 0
		self.running = True

		self.image = pygame.image.load(IMAGE_PATH)
		self.image = pygame.transform.scale(self.image, self.size)

		# functions
		pygame.display.set_caption("Sliding Puzzle")
	
	# initialize game
	def init(self):
		self.pieces = []
		n = 0
		for x in range(0, 4):
			for y in range(0, 4):
				self.pieces.append(Piece([x, y], n))
				n += 1

		self.pieces.pop(-1)

		self.scrambling = False

	# do one random move
	def scramble(self):
		moved = False
		while not moved:
			moved = self.move([random.randint(0, self.width), random.randint(0, self.height)])

	def move(self, mousepos):
		for piece in self.pieces:
			if piece.clicked([mousepos[0] / piece.size[0], mousepos[1] / piece.size[1]]):
				for _ in [[-1, 0], [1, 0], [0, -1], [0, 1]]:
					pos = [mousepos[0] / piece.size[0] + _[0], mousepos[1] / piece.size[1] + _[1]]
					occupied = False
					for p in self.pieces:

						if p.clicked(pos) or pos[0] < 0 or pos[0] >= 4 or pos[1] < 0 or pos[1] >= 4:
							occupied = True

					if not occupied:
						piece.setgamepos(pos)
						return True

		return False

	# tick function
	def tick(self):
		# handle events
		for event in pygame.event.get():
			if event.type == pygame.QUIT:
				self.quit()
			if event.type == pygame.KEYDOWN:
				# screnshot
				if event.key == pygame.K_F1:
					self.screenshot()

				# scramble
				if event.key == pygame.K_F2:
					self.scrambling = not self.scrambling

				# solve (that is cheating!)
				if event.key == pygame.K_F3:
					self.init()

			# handle mouse button presses
			if event.type == pygame.MOUSEBUTTONDOWN:
				if event.button == 1:
					mousepos = list(pygame.mouse.get_pos())
					self.move(mousepos)
		
		if self.scrambling and self.ticks % 2 == 0:
			self.scramble()

		for piece in self.pieces:
			piece.tick()

	# render function
	def render(self):
		# fill
		self.surface.fill([0, 0, 0])

		for piece in self.pieces:
			piece.render(self.surface)

		# blit and flip
		self.screen.blit(self.surface, [0, 0])
		pygame.display.flip()
	
	# quits
	def quit(self):
		self.running = False

	# takes a screenshot
	def screenshot(self):
		path = os.getcwd() + "/out/"
		
		try:
			if not os.path.isdir(path):
				os.mkdir(path)
			
			name = "img" + str(len(os.listdir(path))) + ".png"
			pygame.image.save(self.surface, path + name)
		except:
			pass

	# run function (uses tick() and render())
	def run(self):
		ticksPerSecond = 60
		lastTime = time.time() * 1000000000
		nsPerTick =  1000000000.0 / float(ticksPerSecond)
	
		ticks = 0
		frames = 0
	
		lastTimer = time.time() * 1000
		delta = 0.0
		
		while self.running:
			now = time.time() * 1000000000
			delta += float(now - lastTime) / float(nsPerTick)
			lastTime = now
			shouldRender = False
					
			while delta >= 1:
				ticks += 1
				self.ticks += 1
				self.tick()
				delta -= 1
				shouldRender = True
			
			if shouldRender:
				frames += 1
				self.render()
			
			if time.time() * 1000 - lastTimer >= 1000:
				lastTimer += 1000
				
				# debug
				#print("Frames: " + str(frames) + ", ticks: " + str(ticks))
			
				frames = 0
				ticks = 0

# start game
G = GAME()
G.init()
G.run()
