from OpenGL.GL import *
from OpenGL.GLUT import *
from OpenGL.GLU import *
import sys
from math import pi, sqrt
from time import perf_counter
import random

from vector import multivector
from gyro import gyro

dimensions = int(sys.argv[1]) if len(sys.argv)>1 else 3
wheel_radius = 1
tick_length = 20

width = 800
height = 800

factor = wheel_radius * 2

gravforce = 0.1
gravity = multivector.vectorize([0,-gravforce]+[0]*(dimensions-2))

starting_impulse = 2

global wheel
last = perf_counter()

def tick():
	update()
	display()

def update():
	global last
	global wheel
	new = perf_counter()
	
	wheel.impulse(gravity*(new-last), wheel.point(1))
	
	wheel.update(new - last)
	last = new

def to_color(a):
	return (a+factor)/(2*factor)

def colors(v):
	if len(v) < 3: # In 2 dimensions, don't bother doing color stuff
		return [1, 1, 1]
	elif len(v) == 3: # In 3 dimensions, use bright-dark to indicate 3d
		a = 1 - to_color(v[2])
		return [a, a, a]
	elif len(v) == 4: # In 4 dimensions, use ??? for 3d and red-blue for 4d
		a = 1# - to_color(v[2])
		b = to_color(v[3])
		c = 1 - b
		return [a*b, a*min(b,c), a*c]
	elif len(v) == 5: # In 5 dimensions, use bright-dark for 3d, red for 4d, green for 5d
		a = 1 - to_color(v[2])
		b = to_color(v[3])
		c = to_color(v[4])
		return [a*b, a*c, a*min(b,c)]
	else: # In higher dimensions, use bright-dark, red, green, blue, and ignore the rest
		a = 1 - to_color(v[2])
		b = to_color(v[3])
		c = to_color(v[4])
		d = to_color(v[5])
		return [a*b, a*c, a*d]
#	a = to_color(v[2]) if v.dim > 2 else 0 # Reddishness
#	b = to_color(v[3]) if v.dim > 3 else 1-a # Greenishness
#	c = to_color(v[4]) if v.dim > 4 else 1-b # Bluishness
#	return [a, b, c]

def draw_velocity():
	vec0 = multivector.vectorize([0,0,0])
	vec1 = multivector.vectorize([0,1.5,0])
	vec3 = wheel.velocity[2] @ vec1 / vec1.magnsqr()
	vec2 = vec3 + vec1
	glBegin(GL_QUADS)
	place_vertex(vec0)
	place_vertex(vec1)
	place_vertex(vec2)
	place_vertex(vec3)
	glEnd()

def draw_details():
	glColor3f(0,1,0)
	glRasterPos2f(-2, 1.9)
	for c in str(wheel.velocity):
		glutBitmapCharacter(GLUT_BITMAP_9_BY_15, ord(c))

def place_vertex(v):
	glColor3fv(colors(v.vector()))
	glVertex3fv(v.projection(3))

def display():
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
	glMatrixMode(GL_MODELVIEW)
	glLoadIdentity()
	
	v = wheel.rotated_points()	
	glBegin(GL_LINE_STRIP)
	for a in v:
		place_vertex(a)
	glEnd()
	
#	draw_velocity()
	draw_details()
	
	glutSwapBuffers()

def setup():
	global wheel
	wheel = gyro(dimensions, constrain_position=True)
	arm = multivector.vectorize([1,0,0]+[0]*(dimensions-3))
	force = multivector.vectorize([0,1,0]+[0]*(dimensions-3))
	wheel.angular_impulse(starting_impulse*force, arm)

def glmain(argv):
	glutInitDisplayMode(GLUT_RGB | GLUT_DEPTH | GLUT_DOUBLE)
	glutInitWindowSize(width, height)
	glutInit(argv)
	glutCreateWindow(b'Gyroscope')
	glutDisplayFunc(tick)
	glutIdleFunc(tick)
	glEnable(GL_DEPTH_TEST)
	glMatrixMode(GL_PROJECTION)
	glLoadIdentity()
	glOrtho(-2.0, 2.0, -2.0, 2.0, -2.0, 2.0)
	glutMainLoop()

def main():
	setup()
	glmain(sys.argv)

main()