#!/usr/bin/python3
# -*- coding: utf-8 -*-
# -*- mode: Python -*-


'''

Square
v0.1.0

Anaglyphed rotating square (for red and green glasses)

This scene uses the drawing functions provided by LJ in lj23.py

LICENCE : CC

by Sam Neurohack


'''
import sys
import os
print()
ljpath = r'%s' % os.getcwd().replace('\\','/')

# import from shell

sys.path.append(ljpath +'/../libs3/')

#import from LJ
sys.path.append(ljpath +'/libs3/')
print(ljpath+'/../libs3/')

import lj23layers as lj

sys.path.append('../libs3')
from OSC3 import OSCServer, OSCClient, OSCMessage
import redis
import math
import time
import argparse

OSCinPort = 8013

print ("")
print ("Arguments parsing if needed...")
argsparser = argparse.ArgumentParser(description="Square example for LJ")
argsparser.add_argument("-r","--redisIP",help="IP of the Redis server used by LJ (127.0.0.1 by default) ",type=str)
argsparser.add_argument("-s","--scene",help="LJ scene number (0 by default)",type=int)
#argsparser.add_argument("-l","--laser",help="Laser number to be displayed (0 by default)",type=int)
argsparser.add_argument("-v","--verbose",help="Verbosity level (0 by default)",type=int)
argsparser.add_argument("-m","--myIP",help="Local IP (127.0.0.1 by default) ",type=str)

args = argsparser.parse_args()

if args.scene:
	ljscene = args.scene
else:
	ljscene = 0
'''
if args.laser:
	plnumber = args.laser
else:
	plnumber = 0
'''

# Redis Computer IP
if args.redisIP  != None:
	redisIP  = args.redisIP
else:
	redisIP = '127.0.0.1'

print("redisIP",redisIP)

# myIP
if args.myIP  != None:
	myIP  = args.myIP
else:
	myIP = '127.0.0.1'

print("myIP",myIP)

if args.verbose:
	debug = args.verbose
else:
	debug = 0

# Useful variables init.
white = lj.rgb2int(255,255,255)
red = lj.rgb2int(255,0,0)
blue = lj.rgb2int(0,0,255)
green = lj.rgb2int(0,255,0)

width = 800
height = 600
centerX = width / 2
centerY = height / 2

# 3D to 2D projection parameters
fov = 256
viewer_distance = 2.2

# Anaglyph computation parameters for right and left eyes.
# algorythm come from anaglyph geo maps 
eye_spacing = 100
nadir = 0.5
observer_altitude = 30000
map_layerane_altitude = 0.0

# square coordinates : vertices that compose each of the square.
vertices = [
	(- 1.0, 1.0,- 1.0),
	( 1.0, 1.0,- 1.0),
	( 1.0,- 1.0,- 1.0),
	(- 1.0,- 1.0,- 1.0)
	]

face = [0,1,2,3]

#
# LJ inits
# 

layer = 0

# Setup LJ library mandatory properties.
lj.Config(redisIP, ljscene, "square")

# You can define properties for each drawn "element" : name, intensity, active, xy, color, red, green, blue, layer , closed
Leftsquare = lj.FixedObject('Leftsquare', True, 255, [], red, 255, 0, 0, layer , True)
Rightsquare = lj.FixedObject('Rightsquare', True, 255, [], blue, 0, 255, 0, layer , True)

# 'Destination' for given layer : name, number, active, layer , scene, laser
Dest0 = lj.DestObject('0', 0, True, 0 , 0, 0) 	# Dest0 will send layer 0 points to scene 0, laser 0 


#
# Anaglyph computation : different X coordinate for each eye
#

def LeftShift(elevation):

			diff = elevation - map_layerane_altitude
			return nadir * eye_spacing *  diff / (observer_altitude - elevation)

def RightShift(elevation):

			diff = map_layerane_altitude - elevation
			return (1 - nadir) * eye_spacing * diff / (observer_altitude - elevation)

#
# OSC
#

oscserver = OSCServer( (myIP, OSCinPort) )
oscserver.timeout = 0


# this method of reporting timeouts only works by convention
# that before calling handle_request() field .timed_out is 
# set to False
def handle_timeout(self):
    self.timed_out = True

# funny python's way to add a method to an instance of a class
import types
oscserver.handle_timeout = types.MethodType(handle_timeout, oscserver)


# OSC callbacks

# /square/ljscene
def OSCljscene(path, tags, args, source):

    print("Got /square/ljscene with value", args[0])
    lj.WebStatus("square to virtual "+ str(args[0]))
    ljscene = args[0]
    lj.Ljscene(ljscene)


def Proj(x,y,z,angleX,angleY,angleZ):

		rad = angleX * math.pi / 180
		cosa = math.cos(rad)
		sina = math.sin(rad)
		y2 = y
		y = y2 * cosa - z * sina
		z = y2 * sina + z * cosa
		
		rad = angleY * math.pi / 180
		cosa = math.cos(rad)
		sina = math.sin(rad)
		z2 = z
		z = z2 * cosa - x * sina
		x = z2 * sina + x * cosa

		rad = angleZ * math.pi / 180
		cosa = math.cos(rad)
		sina = math.sin(rad)
		x2 = x
		x = x2 * cosa - y * sina
		y = x2 * sina + y * cosa


		""" Transforms this 3D point to 2D using a perspective projection. """
		factor = fov / (viewer_distance + z)
		x = x * factor + centerX
		y = - y * factor + centerY
		return (x,y)


#
# Main 
#

def Run():


	Left = []
	Right = []
	counter =0
	lj.WebStatus("Square")
	lj.SendLJ("/square/start 1")

	# OSC Server callbacks
	print("Starting OSC server at",myIP," port",OSCinPort,"...")
	oscserver.addMsgHandler( "/square/ljscene", OSCljscene )

	# Add OSC generic plugins commands : 'default", /ping, /quit, /pluginame/obj, /pluginame/var, /pluginame/adddest, /pluginame/deldest
	lj.addOSCdefaults(oscserver)

	try:

		while lj.oscrun:

			lj.OSCframe()
			Left = []
			Right = []
	
			x = vertices[0][0]
			y = vertices[0][1]
			z = vertices[0][2]
	
			# LJ tracers will "move" the laser to this first point in black, then move to the next with second point color.
			# For more accuracy in dac emulator, repeat this first point.

			# Generate all points in square.
			for point in face:
				x = vertices[point][0]
				y = vertices[point][1]
				z = vertices[point][2]

				Left.append(Proj(x+LeftShift(z*25),y,z,0,counter,0))
				Right.append(Proj(x+RightShift(z*25),y,z,0,counter,0))	
	

			lj.PolyLineOneColor(Left, c = Leftsquare.color , layer = Leftsquare.layer, closed = Leftsquare.closed)
			lj.PolyLineOneColor(Right, c = Rightsquare.color , layer = Rightsquare.layer, closed = Rightsquare.closed)

			lj.DrawDests()
	
			
			time.sleep(0.1)
	
			counter += 1
			if counter > 360:
				counter = 0

	except KeyboardInterrupt:
		pass

	# Gently stop on CTRL C

	finally:

		lj.ClosePlugin()


Run()
