import math
import sys
import datetime
import random
from OpenGL.GL import *
from OpenGL.GLU import *
from OpenGL.GLUT import *
from OpenGL.quaternion import *

def vc(v1, v2):
	return (v1[1]*v2[2]-v1[2]*v2[1], v1[2]*v2[0]-v1[0]*v2[2], v1[0]*v2[1]-v1[1]*v2[0])

def inner_prod(v1, v2):
     sum = 0
     for i in xrange(len(v1)):
            sum += v1[i] * v2[i]
     return sum

def matmult3(m, v):
     return [inner_prod(r, v) for r in m]

class Side:
	def __init__(self):
		self.color = (1, 1, 1)

	def __init__(self, r, g, b):
		self.color = (r, g, b)

	def render(self, sc, quad):
		quads = [[]] * 4
		quads[0] = [(0.0, 0.0), (1.0, 0.0), (0.5, 0.5)]
		quads[1] = [(1.0, 0.0), (1.0, 1.0), (0.5, 0.5)]
		quads[2] = [(1.0, 1.0), (0.0, 1.0), (0.5, 0.5)]
		quads[3] = [(0.0, 1.0), (0.0, 0.0), (0.5, 0.5)]


		no=vc((sc[3], sc[4], sc[5]), (sc[6], sc[7], sc[8]))
		glNormal3f(no[0], no[1], no[2])
		glColor3f(self.color[0], self.color[1], self.color[2])

		for v in range(3):
			#m = 0.8 + v * 0.1
			x = sc[0] + quads[quad][v][0] * sc[3] + quads[quad][v][1] * sc[6]
			y = sc[1] + quads[quad][v][0] * sc[4] + quads[quad][v][1] * sc[7]
			z = sc[2] + quads[quad][v][0] * sc[5] + quads[quad][v][1] * sc[8]
			glVertex3f(x, y, z)

colors = []
colors.append(Side(1, 0, 0))
colors.append(Side(0, 1, 0))
colors.append(Side(0, 0, 1))
colors.append(Side(1, 1, 0))
colors.append(Side(0, 1, 1))
colors.append(Side(1, 0, 1))
sides = [[]] * 24


corners = [()] * 8
corners[0] = (-0.5, -0.5, -0.5)
corners[1] = ( 0.5, -0.5, -0.5)
corners[2] = ( 0.5, -0.5,  0.5)
corners[3] = (-0.5, -0.5,  0.5)
corners[4] = (-0.5,  0.5, -0.5)
corners[5] = ( 0.5,  0.5, -0.5)
corners[6] = ( 0.5,  0.5,  0.5)
corners[7] = (-0.5,  0.5,  0.5)


rotation = [()] * 8

rotation[0] = (7, 6, 20, 23, 9, 8)
rotation[1] = (6, 5, 13, 12, 21, 20)
rotation[2] = (5, 4, 0, 3, 14, 13)
rotation[3] = (4, 7, 8, 11, 1, 0)
rotation[4] = (10, 9, 23, 22, 18, 17)
rotation[5] = (22, 21, 12, 15, 19, 18)
rotation[6] = (15, 14, 3, 2, 16, 19)
rotation[7] = (2, 1, 11, 10, 17, 16)

x2 = 0
y2 = 0
xr = 0.0
yr = 0.0
rq = quaternion(1, 0, 0, 0)
rq = quaternion(0.9238, 0.3826, 0, 0) * rq
rq = quaternion(0.9238, 0, 0.3826, 0) * rq
rotate = False
quo = None
cor = 0

def rotatecorner(cor):
	global sizes, rotation
	r = rotation[cor]
	s1 = [sides[r[4]], sides[r[5]]]
	sides[r[5]] = sides[r[3]]
	sides[r[4]] = sides[r[2]]
	sides[r[3]] = sides[r[1]]
	sides[r[2]] = sides[r[0]]
	sides[r[1]] = s1[1]
	sides[r[0]] = s1[0]

def initcube():
	global sides
	global colors
	for s in range(6):
		for c in range(4):
			sides[s*4+c] = colors[s]
#			sides[s*4+c] = Side(s * 0.1, c * 0.2, (s+c) * 0.09)
	random.seed()
	for i in range(40):
		rotatecorner(random.randint(0, 7))

def display():
	global sides, rq, corners, quo, cor
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)

	glPushMatrix()
	m = [
		[ 1-2*rq[2]*rq[2]-2*rq[3]*rq[3], 2*rq[1]*rq[2]+2*rq[0]*rq[3], 2*rq[1]*rq[3]-2*rq[0]*rq[2], 0],
		[ 2*rq[1]*rq[2]-2*rq[0]*rq[3], 1-2*rq[1]*rq[1]-2*rq[3]*rq[3], 2*rq[2]*rq[3]+2*rq[0]*rq[1], 0],
		[ 2*rq[1]*rq[3]+2*rq[0]*rq[2], 2*rq[2]*rq[3]-2*rq[0]*rq[1], 1-2*rq[1]*rq[1]-2*rq[2]*rq[2], 0],
		[ 0,0,0,1]
	]
	
	glMultMatrixf(m);
	ma = glGetFloatv(GL_MODELVIEW_MATRIX)
	mb = [
	 [0,0,0,0],
	 [0,0,0,0],
	 [0,0,0,0],
	 [0,0,0,0]
	]
	for i in range(4):
		for j in range(4):
			mb[j][i] = ma[i][j]
	z = -50
	for i in range(len(corners)):
		v = matmult3(mb, [corners[i][0], corners[i][1], corners[i][2], 1])
		if v[2] > z:
			cor = i
			z = v[2]

	glBegin(GL_TRIANGLES)

	# base, i, j
	sidec = [()] * 6
	sidec[0] = (  0.5, -0.5,  0.5, -1.0,  0.0,  0.0,  0.0,  1.0,  0.0 )
	sidec[1] = ( -0.5, -0.5,  0.5,  1.0,  0.0,  0.0,  0.0,  0.0, -1.0 )
	sidec[2] = ( -0.5, -0.5,  0.5,  0.0,  0.0, -1.0,  0.0,  1.0,  0.0 )
	sidec[3] = (  0.5,  0.5, -0.5,  0.0, -1.0,  0.0,  0.0,  0.0,  1.0 )
	sidec[4] = (  0.5,  0.5,  0.5, -1.0,  0.0,  0.0,  0.0,  0.0, -1.0 )
	sidec[5] = ( -0.5, -0.5, -0.5,  1.0,  0.0,  0.0,  0.0,  1.0,  0.0 )

	for s in range(6):
		for q in range(4):
			sides[s*4+q].render(sidec[s], q)

	glEnd()

	glPushMatrix()
	glTranslatef(corners[cor][0], corners[cor][1], corners[cor][2])
	glColor3f(1, 1, 1)
#	glDisable(GL_LIGHTING)
	gluSphere(quo, 0.05, 8, 8)
#	glEnable(GL_LIGHTING)

	glPopMatrix()

	glPopMatrix()
	glutSwapBuffers()

def displayrot(corner, angle):
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
	global sides, rq, corners, quo, cor

	glPushMatrix()
	m = [
		[ 1-2*rq[2]*rq[2]-2*rq[3]*rq[3], 2*rq[1]*rq[2]+2*rq[0]*rq[3], 2*rq[1]*rq[3]-2*rq[0]*rq[2], 0],
		[ 2*rq[1]*rq[2]-2*rq[0]*rq[3], 1-2*rq[1]*rq[1]-2*rq[3]*rq[3], 2*rq[2]*rq[3]+2*rq[0]*rq[1], 0],
		[ 2*rq[1]*rq[3]+2*rq[0]*rq[2], 2*rq[2]*rq[3]-2*rq[0]*rq[1], 1-2*rq[1]*rq[1]-2*rq[2]*rq[2], 0],
		[ 0,0,0,1]
	]
	
	glMultMatrixf(m);

	notrot = filter(lambda x: x not in rotation[corner], range(24))

	# base, i, j
	sidec = [()] * 6
	sidec[0] = (  0.5, -0.5,  0.5, -1.0,  0.0,  0.0,  0.0,  1.0,  0.0 )
	sidec[1] = ( -0.5, -0.5,  0.5,  1.0,  0.0,  0.0,  0.0,  0.0, -1.0 )
	sidec[2] = ( -0.5, -0.5,  0.5,  0.0,  0.0, -1.0,  0.0,  1.0,  0.0 )
	sidec[3] = (  0.5,  0.5, -0.5,  0.0, -1.0,  0.0,  0.0,  0.0,  1.0 )
	sidec[4] = (  0.5,  0.5,  0.5, -1.0,  0.0,  0.0,  0.0,  0.0, -1.0 )
	sidec[5] = ( -0.5, -0.5, -0.5,  1.0,  0.0,  0.0,  0.0,  1.0,  0.0 )

	n = [()] * 8
	n[0] = (1, 3, 4)
	n[1] = (0, 2, 5)
	n[2] = (1, 3, 6)
	n[3] = (2, 0, 7)
	n[4] = (0, 5, 7)
	n[5] = (6, 4, 1)
	n[6] = (5, 2, 7)
	n[7] = (6, 4, 3)

#	glDisable(GL_LIGHTING)
	glBegin(GL_TRIANGLES)
	glColor3f(0.8, 0.7, 0.5)
	glVertex3f(corners[n[corner][0]][0], corners[n[corner][0]][1], corners[n[corner][0]][2])
	glVertex3f(corners[n[corner][1]][0], corners[n[corner][1]][1], corners[n[corner][1]][2])
	glVertex3f(corners[n[corner][2]][0], corners[n[corner][2]][1], corners[n[corner][2]][2])
	glEnd()
#	glEnable(GL_LIGHTING)

	glBegin(GL_TRIANGLES)


	for nr in notrot:
		sides[nr].render(sidec[nr/4], nr % 4)

	glEnd()

	glPushMatrix()
	glRotatef(angle, corners[corner][0], corners[corner][1], corners[corner][2])

	glBegin(GL_TRIANGLES);
	for nr in rotation[corner]:
		sides[nr].render(sidec[nr/4], nr % 4)

	glEnd()

	glPopMatrix()

	glPopMatrix()
	glutSwapBuffers()

def mouse(button, state, x, y):
	global x2, y2, rotate, phase, phaseangle
	if(state == GLUT_DOWN and button == GLUT_RIGHT_BUTTON):
		x2 = x
		y2 = y
		rotate = True
	if(state == GLUT_UP and button == GLUT_RIGHT_BUTTON):
		rotate = False

	if(state == GLUT_DOWN and button == GLUT_LEFT_BUTTON):
		b = datetime.datetime.now()
		span = 200000.0
		while True:
			c = datetime.datetime.now()
			e = (c - b).microseconds
			if e > span:
				break
			displayrot(cor, - (e/ span) * 120)
		rotatecorner(cor)
		for n in range(len(sides)):
			if n % 4 == 0:
				c = sides[n].color
			else:
				if c != sides[n].color:
					break
		else:
			for n in sides:
				n.color = (1, 1, 1)
			
		glutPostRedisplay()

def motion(x, y):
	global x2, y2, rotate, xr, yr, rq
	if rotate:
		xr = (x - x2) / 50.0
		yr = (y - y2) / 50.0

		q1 = quaternion(math.cos(yr/2.0), math.sin(yr/2.0), 0, 0)
		q2 = quaternion(math.cos(xr/2.0), 0, math.sin(xr/2.0), 0)
		rq = q1 * rq
		rq = q2 * rq

		x2 = x
		y2 = y
		glutPostRedisplay()

def reshape(w, h):
	global xr, yr
	glMatrixMode(GL_PROJECTION)
	glViewport(0, 0, w, h)
	glLoadIdentity()
	glFrustum(-1, 1, -1, 1, 1.5, 20)
	glScalef(1, float(w) / float(h), 1)
	glMatrixMode(GL_MODELVIEW)

initcube()

glutInit(sys.argv)
glutInitWindowSize(400, 300)
glutInitDisplayMode(GLUT_DOUBLE | GLUT_DEPTH | GLUT_RGB)
glutCreateWindow('teh cube')

glClearColor(0.2, 0, 0.3, 0)
glShadeModel(GL_SMOOTH)
glEnable(GL_DEPTH_TEST)
glEnable(GL_COLOR_MATERIAL);
glColorMaterial(GL_FRONT, GL_AMBIENT_AND_DIFFUSE)
#glEnable(GL_LIGHTING)
#glEnable(GL_LIGHT0)

#glLightfv(GL_LIGHT0, GL_POSITION, [0, 0, -2, 0])
#glLightfv(GL_LIGHT0, GL_SPECULAR, [0, 0, 0, 0])
#glLightfv(GL_LIGHT0, GL_AMBIENT, [0.2, 0.2, 0.2, 0.2])

glMatrixMode(GL_MODELVIEW)
glLoadIdentity()
gluLookAt(0, 0, 3, 0, 0, 0, 0, 1, 0)
quo = gluNewQuadric()

glutDisplayFunc(display)
glutReshapeFunc(reshape)
glutMouseFunc(mouse)
glutMotionFunc(motion)
glutMainLoop()
