#!/usr/bin/env python
# Copyright (C) 2003, 2004 Josef Spillner <josef@ggzgamingzone.org>
# Published under GNU GPL conditions

import pygame
import pygame.image
from pygame.locals import *
from Numeric import *
import socket
import sys
import time
import random
import os, pwd

import ggzmod

try:
	import ggzsettings
	sys.path = [ggzsettings.MODULEPATH + "/common/"] + sys.path
except:
	sys.path = ["../lib/"] + sys.path

import playertable

def username():
	return pwd.getpwuid(os.geteuid())[0]

def playernames(surface, player1, player2):
	font3 = pygame.font.Font(None, 30)
	player1 = font3.render(player1, 1, (165, 140, 0))
	vs = font3.render("vs.", 1, (165, 140, 0))
	player2 = font3.render(player2, 1, (165, 140, 0))

	surface.blit(player1, (610 - player1.get_width(), 285))
	surface.blit(vs, (610 - vs.get_width(), 315))
	surface.blit(player2, (610 - player2.get_width(), 345))

class Proto:
	def __init__(self):
		self.move_invalid = 0
		self.move_ok = 1
		self.move_again = 2

class Game:
	def __init__(self):
		self.seat = -1
		self.domove = 0

	def tomove(self, x2, y2, x, y):
		move = 0
		if x == x2 - 1 and y == y2 - 1:
			move = 7
		elif x == x2 and y == y2 - 1:
			move = 8
		elif x == x2 + 1 and y == y2 - 1:
			move = 9
		elif x == x2 + 1 and y == y2:
			move = 6
		elif x == x2 + 1 and y == y2 + 1:
			move = 3
		elif x == x2 and y == y2 + 1:
			move = 2
		elif x == x2 - 1 and y == y2 + 1:
			move = 1
		elif x == x2 - 1 and y == y2:
			move = 4

		return move

	def frommove(self, move):
		p = (0, 0)
		if move == 1:
			p = (-1, 1)
		elif move == 2:
			p = (0, 1)
		elif move == 3:
			p = (1, 1)
		elif move == 4:
			p = (-1, 0)
		elif move == 5:
			p = (0, 0)
		elif move == 6:
			p = (1, 0)
		elif move == 7:
			p = (-1, -1)
		elif move == 8:
			p = (0, -1)
		elif move == 9:
			p = (1, -1)

		return p

class Network:
	def __init__(self):
		self.sock = None
		self.gamefd = -1

		self.MSG_SEAT = 0
		self.MSG_PLAYERS = 1
		self.MSG_MOVE = 2
		self.MSG_GAMEOVER = 3
		self.REQ_MOVE = 4
		self.RSP_MOVE = 5
		self.SND_SYNC = 6
		self.MSG_OPTIONS = 7
		self.REQ_OPTIONS = 8
		self.MSG_CHAT = 9
		self.RSP_CHAT = 10
		self.SND_STATS = 11

		self.SND_MOVE = 0
		self.REQ_SYNC = 1
		self.SND_OPTIONS = 2
		self.REQ_NEWGAME = 3
		self.REQ_STATS = 4

		self.ERR_STATE = -1
		self.ERR_TURN = -2
		self.ERR_BOUND = -3
		self.ERR_FULL = -4

		self.SEAT_OPEN = 1

		self.game = Game()

	def init(self, fd):
		self.sock = socket.fromfd(fd, socket.AF_INET, socket.SOCK_STREAM)

	def getbyte(self):
		opstr = self.sock.recv(4)
		op = ord(opstr[0]) * 256 * 256 * 256
		op += ord(opstr[1]) * 256 * 256
		op += ord(opstr[2]) * 256
		op += ord(opstr[3])
		if socket.ntohl(op) == op:
			op = socket.ntohl(op)
		return op

	def getchar(self):
		opstr = self.sock.recv(1)
		op = ord(opstr[0])
		return op

	def getstring(self):
		length = self.getbyte()
		opstr = self.sock.recv(length)
		return opstr

	def sendbyte(self, byte):
		nbyte = byte
		if socket.htonl(nbyte) == nbyte:
			nbyte = socket.htonl(nbyte)
		c1 = (nbyte >> 24) & 0xFF
		c2 = (nbyte >> 16) & 0xFF
		c3 = (nbyte >> 8) & 0xFF
		c4 = (nbyte >> 0) & 0xFF
		#self.sock.send(chr(c1))
		#self.sock.send(chr(c2))
		#self.sock.send(chr(c3))
		#self.sock.send(chr(c4))
		s = chr(c1) + chr(c2) + chr(c3) + chr(c4)
		self.sock.send(s)

	def sendchar(self, char):
		self.sock.send(chr(char))

	def sendstring(self, str):
		self.sendbyte(len(str))
		self.sock.send(str)

def handle_server(fd):
	global net

	ggzmod.setState(ggzmod.STATE_PLAYING)

	net.gamefd = fd
	net.init(net.gamefd)

def network():
	global net
	global blobpos
	global trail
	global madeturn
	global surface
	global winner

	print "network!"

	op = net.getbyte()

	if op == net.MSG_PLAYERS:
		print "- players"
		seattype = net.getbyte()
		print "seattype:", seattype
		if seattype != net.SEAT_OPEN:
			name1 = net.getstring()
			print "name:", name1
		seattype = net.getbyte()
		print "seattype:", seattype
		if seattype != net.SEAT_OPEN:
			name2 = net.getstring()
			print "name:", name2
		playernames(surface, name1, name2)
	elif op == net.MSG_SEAT:
		print "- seat"
		seat = net.getbyte()
		print "seat:", seat
		net.game.seat = seat
	elif op == net.MSG_MOVE:
		print "- move"
		direction = net.getbyte()
		print "direction:", direction

		(xd, yd) = net.game.frommove(direction)
		(x, y, player) = blobpos
		blobpos = (x + xd, y + yd, 1)
		trail.append(blobpos)
	elif op == net.SND_STATS:
		print "- stats"
		wins = net.getbyte()
		losses = net.getbyte()
		ties = net.getbyte()
		print "wins:", wins, "losses:", losses, "ties:", ties
	elif op == net.SND_SYNC:
		print "- sync"
		boardheight = net.getchar()
		goalwidth = net.getchar()
		wallwidth = net.getchar()
		turn = net.getchar()
		x = net.getbyte()
		y = net.getbyte()
		print "boardheight:", boardheight, "goalwidth:", goalwidth, "wallwidth:", wallwidth
		print "turn:", turn, "x:", x, "y:", y

		for p in range(0, goalwidth + wallwidth * 2):
			for q in range(0, boardheight):
				for d in range(1, 10):
					c = net.getchar()
	elif op == net.MSG_GAMEOVER:
		print "- gameover"
		winner = net.getchar()
		print "winner:", winner
	elif op == net.RSP_MOVE:
		print "- rspmove"
		status = net.getchar()
		move = net.getbyte()
		print "status:", status, "move:", move
	elif op == net.REQ_MOVE:
		print "- reqmove"
		net.game.domove = 1
		madeturn = 0
	elif op == net.REQ_OPTIONS:
		print "- reqoptions"

		boardheight = 11
		goalwidth = 1
		wallwidth = 4

		net.sendbyte(net.SND_OPTIONS)
		net.sendchar(boardheight)
		net.sendchar(goalwidth)
		net.sendchar(wallwidth)
	elif op == net.MSG_OPTIONS:
		print "- options"
		boardheight = net.getchar()
		goalwidth = net.getchar()
		wallwidth = net.getchar()
		print "boardheight:", boardheight, "goalwidth:", goalwidth, "wallwidth:", wallwidth
	else:
		print "- unknown opcode"

def handle_network():
	global net

	ret = ggzmod.autonetwork()
	if ret:
		network()

def validate((blobpos, trail, x, y)):
	global proto

	(bx, by, bplayer) = blobpos

	found = proto.move_invalid
	if (abs(x - bx) == 1 and abs(y - by) <= 1) or (abs(x - bx) <= 1 and abs(y - by) == 1):
		found = proto.move_ok
	if x < 0 or y < 0 or x > 8 or y > 11:
		found = proto.move_invalid
	elif (x == 0 and bx == 0) or (x == 8 and bx == 8) or (y == 0 and by == 0) or (y == 11 and by == 11):
		found = proto.move_invalid
	else:
		index = 0
		for t in trail:
			(tx, ty, tplayer) = t
			if tx == x and ty == y:
				if found == proto.move_ok:
					found = proto.move_again
				if index > 0:
					(tx2, ty2, tplayer2) = trail[index - 1]
					if tx2 == bx and ty2 == by:
						found = proto.move_invalid
				if index < len(trail) - 1:
					(tx2, ty2, tplayer2) = trail[index + 1]
					if tx2 == bx and ty2 == by:
						found = proto.move_invalid
			index += 1

	return found

def ai((blobpos, trail)):
	global proto

	time.sleep(1)

	(bx, by, bplayer) = blobpos

	found = proto.move_invalid
	tries = 0
	while found == proto.move_invalid and tries < 100:
		tries += 1
		x = random.randint(bx - 1, bx + 1)
		y = random.randint(by - 1, by + 1)
		if x != bx or y != by:
			found = validate((blobpos, trail, x, y))

	if tries < 100:
		blobpos = (x, y, 1 - bplayer)
		trail.append(blobpos)

	return (blobpos, trail)

def main(ggzmode):
	global net
	global blobpos
	global trail
	global madeturn
	global surface
	global winner
	global proto

	proto = Proto()

	""" Pygame setup """

	pygame.init()
	pygame.display.set_caption("Escape/SDL")

	screen = pygame.display.set_mode((640, 480), DOUBLEBUF)

	pygame.mouse.set_visible(0)

	surface = pygame.Surface((640, 480), 0, 24)

	""" Game board background """

	for i in range(0, 240):
		x = sin(i / 15.0) * 20
		surface.fill(((255 - i) / 2, 0, i / 2), ((10, i * 2), (400 + x, 1)))

	""" Menu """

	for i in range(0, 480):
		x = sin(i / 30.0) * 20
		surface.fill((165, 140, 0), ((400 + x + 10, i), (2, 1)))
		surface.fill((195, 160, 0), ((400 + x + 12, i), (2, 1)))
		surface.fill((225, 180, 0), ((400 + x + 14, i), (2, 1)))
		surface.fill((255, 200, 0), ((400 + x + 16, i), (639 - (400 + x + 16) - 10, 1)))

		surface.fill((165, 140, 0), ((0, i), (10, 1)))
		surface.fill((165, 140, 0), ((628, i), (10, 1)))

	""" Menu captions """

	font = pygame.font.Font(None, 44)
	escape = font.render("ESCAPE", 1, (165, 140, 0))
	sdl = font.render("SDL", 1, (165, 140, 0))
	font2 = pygame.font.Font(None, 70)
	slash = font2.render("/", 1, (165, 140, 0))

	surface.blit(escape, (420, 100))
	surface.blit(sdl, (550, 130))
	surface.blit(slash, (540, 105))

	if not ggzmode:
		playernames(surface, username(), _("opponent"))

	""" Points on the game board """

	stone = pygame.Surface((16, 16))
	for j in range(0, 16):
		x = sin(j / 5.0) * 8
		stone.fill((255, 255, 255), ((8 - x, j), (2 * x, 1)))
	stone.set_alpha(30, RLEACCEL)

	blob = pygame.Surface((32, 32))
	for j in range(0, 32):
		x = sin(j / 10.0) * 16
		blob.fill((255, 255, 255), ((16 - x, j), (2 * x, 1)))
	blob.set_alpha(30, RLEACCEL)

	for j in range(1, 10):
		for i in range(1, 8):
			surface.blit(stone, (i * 40 + 30, j * 40 + 30))

	""" Frame around points """

	vline = pygame.Surface((8, 400))
	vline.fill((255, 255, 255))
	vline.set_alpha(30, RLEACCEL)

	hline = pygame.Surface((150, 8))
	hline.fill((255, 255, 255))
	hline.set_alpha(30, RLEACCEL)

	surface.blit(vline, (30, 38))
	surface.blit(vline, (360, 38))

	surface.blit(hline, (30, 38))
	surface.blit(hline, (368 - 150, 38))
	surface.blit(hline, (30, 430))
	surface.blit(hline, (368 - 150, 430))

	""" Network setup """

	ggzsuccess = 0

	if ggzmode:
		ggzmod.setHandler(ggzmod.EVENT_SERVER, handle_server)

		ggzsuccess = ggzmod.connect()
		if ggzsuccess:
			net = Network()

	if ggzmode:
		ggzfont = pygame.font.Font(None, 20)
		if ggzsuccess:
			ggz = ggzfont.render(_("Running on GGZ"), 1, (165, 140, 0))
			surface.blit(ggz, (420, 140))
			hint = ggzfont.render(_("Press 'p' for player table"), 1, (165, 140, 0))
			surface.blit(hint, (420, 160))
		else:
			ggz = ggzfont.render(_("GGZ unavailable"), 1, (165, 140, 0))
			surface.blit(ggz, (420, 140))

	""" Main loop """

	madeturn = 0
	againturn = 0
	aierror = 0
	humanerror = 0
	gameover = 0
	winner = 0

	mouse = stone
	pressed = 0

	blobpos = (4, 5, -1)
	trail = [blobpos]

	pygame.event.clear()

	while 1:
		if ggzsuccess:
			handle_network()
		pygame.event.pump()

		keyinput = pygame.key.get_pressed()

		if keyinput[K_ESCAPE] or pygame.event.peek(QUIT):
			break

		if keyinput[K_p]:
			playertable.show()
			# ??? eat escape key
			#pygame.event.poll()
			while 1:
				pygame.event.pump()
				keyinput = pygame.key.get_pressed()
				if not keyinput[K_ESCAPE]:
					break

		(b1, b2, b3) = pygame.mouse.get_pressed()
		if b1 and not pressed and not madeturn:
			if not ggzmode or (ggzsuccess and net.game.domove):
				(x, y) = pygame.mouse.get_pos()
				x = (x - 30 + 20) / 40
				y = (y - 30 + 20) / 40
				if x >= 0 and x < 9 and y >= 0 and y < 11:
					val = validate((blobpos, trail, x, y))
					if val is not proto.move_invalid:
						(x2, y2, player) = blobpos
						blobpos = (x, y, 0)
						trail.append(blobpos)
						if val is not proto.move_again:
							madeturn = 1
							againturn = 0
						else:
							againturn = 1
						humanerror = 0

						if ggzsuccess and net.game.domove:
							move = net.game.tomove(x2, y2, x, y)
							net.sendbyte(net.SND_MOVE)
							net.sendbyte(move)
					else:
						humanerror = 1
			pressed = 1

		if not b1 and pressed:
			pressed = 0

		screen.blit(surface, (0, 0))

		x = -1
		for t in trail:
			if x > -1:
				(x2, y2, player) = t
				p1 = (x * 40 + 30 + 8, y * 40 + 30 + 8)
				p2 = (x2 * 40 + 30 + 8, y2 * 40 + 30 + 8)
				p = [p1, p2]
				if player == 1:
					color = (255, 0, 0)
				else:
					color = (0, 0, 255)
				pygame.draw.lines(screen, color, 0, p, 5)
				x = x2
				y = y2
			(x, y, player) = t

		(x, y, player) = blobpos
		screen.blit(blob, (x * 40 + 30 - 8, y * 40 + 30 - 8))

		screen.blit(mouse, pygame.mouse.get_pos())

		if (y == 0 and x == 4) or (y == 10 and x == 4):
			if y == 0:
				winner = 2
			else:
				winner = 1

		if winner:
			font4 = pygame.font.Font(None, 24)
			if winner == 2:
				waiting = font4.render(_("Game over, you lost"), 1, (165, 140, 0))
			else:
				waiting = font4.render(_("Game over, you won"), 1, (165, 140, 0))
			screen.blit(waiting, (610 - waiting.get_width(), 400))
			gameover = 1
			madeturn = 1

		if madeturn and not gameover:
			font4 = pygame.font.Font(None, 24)
			if aierror:
				waiting = font4.render(_("AI error, game over"), 1, (165, 140, 0))
			else:
				waiting = font4.render(_("Waiting for opponent"), 1, (165, 140, 0))
			screen.blit(waiting, (610 - waiting.get_width(), 400))

		if humanerror:
			font4 = pygame.font.Font(None, 24)
			waiting = font4.render(_("Not allowed, try again"), 1, (165, 140, 0))
			screen.blit(waiting, (610 - waiting.get_width(), 400))
		elif againturn:
			font4 = pygame.font.Font(None, 24)
			again = font4.render(_("Move again"), 1, (165, 140, 0))
			screen.blit(again, (610 - again.get_width(), 400))

		pygame.display.flip()

		if not ggzmode and madeturn and not aierror and not gameover:
			origpos = blobpos
			(blobpos, trail) = ai((blobpos, trail))
			if origpos != blobpos:
				madeturn = 0
			else:
				aierror = 1

if __name__ == "__main__":
	if len(sys.argv) == 2 and sys.argv[1] == "--ggz":
		main(1)
	else:
		main(0)

