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

'''

fromGML
v0.1.0

Display a GML file

See GML specs at the end.
Support the gml spec="1.0 (minimum)"
and header/client/name
and maybe one day drawing/brush/color

LICENCE : CC
by cocoa and Sam Neurohack

Heavy use of : https://github.com/kgn/pygml
'''
from __future__ import print_function
import time
import struct
import argparse
import sys
import xml.etree.ElementTree as etree
#import urllib
from datetime import datetime
import math, random
import ast

name="generator::fromgml"


def debug(*args, **kwargs):
    if( verbose == False ):
        return
    print(*args, file=sys.stderr, **kwargs)

argsparser = argparse.ArgumentParser(description="GML file frame generator")
argsparser.add_argument("-f","--fps",help="Frame Per Second",default=30,type=int)
argsparser.add_argument("-v","--verbose",action="store_true",help="Verbose output")
argsparser.add_argument("-g","--gml",help=".gml file",default="147.gml",type=str)
argsparser.add_argument("-t","--total",help="Total time",default=32,type=int)
argsparser.add_argument("-m","--mode",help="once or anim mode",default="anim",type=str)
argsparser.add_argument("-s","--skip",help="% of points to skip",default="0.4",type=float)
argsparser.add_argument("-r","--rot",help="(angleX, angleY, angleZ) in degree",default="(0,0,270)",type=str)
args = argsparser.parse_args()

fps=args.fps
verbose=args.verbose
mode = args.mode
optimal_looptime = 1 / fps
angles = ast.literal_eval(args.rot)
debug(name+" optimal frame time "+str(optimal_looptime))


TOTAL_TIME=float(args.total)
TIME_STRETCH = 1
ZOOM=1.0
DELTA = 7
width = 500
height = 500
centerX = width / 2
centerY = height / 2
# 3D to 2D projection parameters
fov = 200
viewer_distance = 2.2

skip = args.skip
#skip is the percentage of points that we ignore in order to render
# faster in the laser display. Unfortunately we are not able to render too
# complex content in our display without resulting in a lot of blinking.

# return a list with all points 
def readGML(filename):

    outputData = []
    tree = etree.parse(filename)
    root = tree.getroot()
    '''
    if (root.tag.lower() != "gml"):
        print("Not a GML file.")
        return
    '''
    #~
    tag = root.find("tag")
    header = tag.find("header")
    if header  != None:
      client = header.find("client")
      if client != None:
          debug("Graffiti name :", client.find("name").text)
    drawing = tag.find("drawing")
    environment = header.find("environment")
    if not environment:
        environment = tag.find("environment")
    #screenBounds = environment.find("screenBounds")
    #globalScale = (1.0,1.0,1.0)
    #dim = (float(screenBounds.find("x").text) * globalScale[0], float(screenBounds.find("y").text) * globalScale[1], float(screenBounds.find("z").text) * globalScale[2])
    #dim = (40.0,40.0,40.0)
    #~
    strokes = drawing.findall("stroke")
    for stroke in strokes:
        pointsEl = stroke.findall("pt")
        for pointEl in pointsEl:

            x = float(pointEl.find("x").text) - 0.5 
            y = float(pointEl.find("y").text) - 0.5
            z = float(pointEl.find("z").text) - 0.5
            transpoint = Rot(x,y,z,angles[0],angles[1],angles[2])
            x = (transpoint[0]*ZOOM*width/2) + (width/2)
            y = (transpoint[1]*ZOOM*height/2) + (height/2)
            z = transpoint[2]
            # WIDTH/2 + ZOOM*point[0]*WIDTH/2, HEIGHT/2 + ZOOM*point[1]*HEIGHT/2
            time = float(pointEl.find("time").text)
            outputData.append([x,y,z,time])
        #print(outputData)
    return outputData

def Rot(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

    return (x,y,z)
    

#[x,y,z,time]
def iterPoints():

  for point in gml:
    yield point


# Play once during total time arg
def Once():

  debug(name,"play once mode")
  shape = []
  for point in gml:
    shape.append([point[0],point[1], 65535])
  debug(name + str(shape))

  t0=datetime.now()
  deltat=0

  while deltat<TOTAL_TIME:

    start = time.time()
    print(shape, flush=True); 
    looptime = time.time() - start

    if( looptime < optimal_looptime ):
      time.sleep( optimal_looptime - looptime)
      debug(name+" micro sleep:"+str( optimal_looptime - looptime))

    delta = datetime.now() - t0
    deltat = delta.seconds + delta.microseconds/1000000.0
    deltat = float(deltat)/TIME_STRETCH
  

# Anim
def Anim():

  debug(name+" anim mode")
  t0=datetime.now()
  deltat = 0

  while deltat<TOTAL_TIME:
  
    delta = datetime.now() - t0
    deltat = delta.seconds + delta.microseconds/1000000.0
    deltat = float(deltat)/TIME_STRETCH
  
    if deltat > TOTAL_TIME:
      t0=datetime.now()
  
    first=True
    shape = []
    for point in iterPoints(): 
      if point[3] <= deltat and deltat <= point[3]+DELTA and random.random()<(1-skip):
        if first:
          first=False
        else:
          #LD.draw_point(WIDTH/2 + ZOOM*point.x*WIDTH/2, HEIGHT/2 + ZOOM*point.y*HEIGHT/2)
          shape.append([point[0], point[1], 65535])
    print(shape, flush=True); 
            

debug(name + " Reading : "+args.gml+" in "+mode+" mode.")
gml = readGML(args.gml)

debug(name + " total points : "+ str(len(gml)))

if mode =="once":
  Once()
else:
  Anim()

debug(name + " ends.")
exit()
'''

<gml spec="1.0 (minimum)">

<tag>
    <drawing>
        <stroke>
            <pt>
                <x>0.0</x>
                <y>0.0</y>
            </pt>
        </stroke>
    </drawing>
</tag>

</gml>



<gml spec="1.0">

<tag>

<header>

  <client>                                      <!-- how, who, what and where -->
      <name>Laser Tag</name>                    <!-- application name -->
      <version>2.0</version>                    <!-- application version -->
      <username>MyUserName</username>           <!-- user name on 000000book.com, optional -->
      <permalink>http://000000book.com/data/156/</permalink>         <!-- URL to .gml data on 000000book.com, optional -->
      <keywords>katsu,paris,2010</keywords>      <!-- comma-separated -->
      <uniquekey>28sks922ks992</uniquekey>       <!-- iPhone uuid, MAC address, etc -->
      <ip>192.168.1.1</ip>
      <time>1928372722</time>                    <!-- unixtime -->
      <location>
          <lon>-39.392922</lon>
          <lat>53.29292</lat>
      </location>
  </client>

    <!-- This is all stuff that relates to the orientation and dimensions of the client -->
    <!-- So that we know how to re-map the 0.0-1.0 coordinates that come in for each point -->
    <!-- Also for figuring out the down vector for devices with accelerometers and how that effects drips -->
    <!-- All numbers should be between 0.0 - 1.0 -->
    <environment>
        <offset>
                <x>0.0</x>
                <y>0.0</y>
                <z>0.0</z>
        </offset>
        <rotation>
                <x>0.0</x>
                <y>0.0</y>
                <z>0.0</z>
        </rotation>
        <up>
            <x>0.0</x>          <!-- commonly up for iphone apps -->
            <y>-1.0</y>         <!-- most common -->
            <z>0.0</z>
        </up>
        <screenbounds>     <!-- use this as your multipler to get 0.0 to 1.0 back to right size - pts should never go off 0.0 to 1.0 -->
            <x>1024</x>
            <y>768</y>
            <z>0</z>
        </screenbounds>
        <origin>
            <x>0</x>
            <y>0</y>
            <z>0</z>
        </origin>
        <realscale>     <!-- how these units relate to real world units - good for laser tag -->
            <x>1000</x>
            <y>600</y>
            <z>0</z>
            <unit>cm</unit>
        </realscale>
        <audio>youraudio.mp3</audio>                        <!-- path to audio file -->
        <background>yourimage.jpg</background>     <!-- path to image file -->
    </environment>
    
</header>

<drawing>
    <!-- for all stroke and movement stuff it helps to have everything inside the stroke tag -->
    <!-- this way it is easy to get a sense of order to events -->
    
    <stroke isdrawing="false">     <!-- for non drawing mouse movements -->
        <pt>
            <x>0.0</x>
            <y>0.0</y>
            <z>0.0</z>         <!--this is optional -->
            <t>0.013</t>       <!-- time is optional too -->
                       <!-- NOTE: older versions of GML use <time> instead of <t> -->
        </pt>
    </stroke>
    
    <stroke>                                 <!-- by default stroke drawing is true -->
    
    <!-- each stroke could be drawn with a different brush -->
    <!-- if no brush tag is found for a stroke then it inherits the previous settings -->
    <brush>
        <mode>0</mode>     <!-- same as uniqueStyleID but an internal reference -->
        <uniquestyleid>LaserTagArrowLetters</uniquestyleid> <!-- unique blackbook string for your style -->
        <!-- see note about spec at the bottom - like unique style but with extra info -->
        <spec>http://aurltodescribethebrushspec.com/someSpec.xml</spec>
        <width>10</width>
        <speedtowidthratio>1.5</speedtowidthratio>     <!-- put 0 for fixed width -->
        <dripamnt>1.0</dripamnt>
        <dripspeed>1.0</dripspeed>
        <layerabsolute>0</layerabsolute>     <!--Think photoshop layers-->
        <color>
            <r>255</r>
            <g>255</g>
            <b>255</b>
            <a>255</a>     <!-- optional -->
        </color>
        <dripvecrelativetoup>     <!-- what angle do our drips go in relation to our up vector -->
            <x>0</x>
            <y>1</y>
            <z>0</z>
        </dripvecrelativetoup>
    </brush>
    
        <pt>
            <x>0.0</x>
            <y>0.0</y>
            <z>0.0</z>                     <!--this is optional -->
            <t>0.013</t>       <!-- time is optional too -->
        </pt>

        <pt>
            <x>0.0</x>
            <y>0.0</y>
           <z>0.0</z>                     <!--this is optional -->
            <t>0.023</t>       <!-- time is optional too -->
        </pt>


    </stroke>

    <!-- this stroke inherits the previous stroke properties -->
    <!-- but changes color and draws on the layer below -->
    <stroke>
            <info>     <!-- optional info - more stuff soon-->
                <curved>true</curved>
            </info>
        <brush>
             <color>
                <r>255</r>
                <g>255</g>
                <b>0</b>
            </color>
            <layerrelative>     <!-- this means one layer bellow the previous layer  -->
                -1
            </layerrelative>
        </brush>
        
        <pt>
            <x>0.0</x>
            <y>0.0</y>
        </pt>

        <pt>
            <x>0.0</x>
            <y>0.0</y>
        </pt>
    
    </stroke>
    
    <stroke>
      <pt>
        <pres>0.5</pres>     <!-- Optional. Preasure range from 0 to 1 -->
        <rot>0.5</rot>     <!-- Optional. Rotation range from 0 to 1 for 0 to 2*PI -->
        <dir>                  <!-- Optional Direction -->
            <x></x>                  <!-- range from 0 to 1 -->
            <y></y>                  <!-- range from 0 to 1 -->
            <z></z>                  <!-- Optional inside direction. Range from 0 to 1 -->
        </dir>
      </pt>
    </stroke>
</drawing>

</tag>

</gml>
'''