#! /usr/bin/env python

# Stage Director receiver
# Copyright 2014 Stefan Schuermans <stefan@schuermans.info>
# Copyleft: GNU public license - http://www.gnu.org/copyleft/gpl.html

import datetime
import fcntl
import os
import select
import socket
import struct
import sys
import time

scriptdir = os.path.dirname(os.path.abspath(__file__))

class Receiver:

  def __init__(self):
    """construct a Stage Director Receiver object"""
    # set constants
    self.select_timeout = 0.1 # timeout (s) for select syscall
    # create member variables
    self.sock = None
    # startup
    self.sockSetup()

  def __del__(self):
    """deconstruct object"""
    self.sockClose()

  def posyParse(self, data):
    """parse received PoSy packet"""
    if len(data) < 76 or data[0:4] != "PoSy":
      return False
    flags, name, pos_ms = struct.unpack("!I64sI", data[4:76])
    name_end = name.find("\0")
    if name_end >= 0:
      name = name[:name_end]
    if flags & 1:
      pause = True
    else:
      pause = False
    # store info from PoSy packet
    print("name=%s pos=%.3fs pause=%s" % (name, pos_ms * 1e-3, pause))

  def run(self):
    """run application"""
    try:
      while True:
        self.waitForInput()
    except KeyboardInterrupt:
      pass

  def sockRecv(self):
    """receive data from socket"""
    if self.sock is None:
      return
    # receive message
    data = self.sock.recv(4096)
    print("data from socket: %d bytes" % len(data))
    # parse (ignore message on error)
    self.posyParse(data)

  def sockClose(self):
    """close UDP socket"""
    if self.sock is not None:
      self.sock.close()
      self.sock = None

  def sockSetup(self):
    """create a new UDP socket and bind it"""
    self.sockClose()
    try:
      self.sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
      self.sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
      self.sock.bind(("0.0.0.0", 5740))
    except:
      self.sockClose()

  def waitForInput(self):
    """wait for input from UDP socket"""
    # poll for data
    inputs = []
    outputs = []
    errors = []
    wait_txt = ""
    if self.sock is not None:
      inputs.append(self.sock)
      wait_txt += " socket"
    print("waiting for input:" + wait_txt)
    rds, wrs, exs = select.select(inputs, outputs, errors, self.select_timeout)
    # obtain available data
    for rd in rds:
      if self.sock is not None and rd is self.sock:
        print("input from socket")
        self.sockRecv()

# main function
def main(argv):
  # run application
  app = Receiver()
  app.run()
  # done
  return 0

# main application entry point
if __name__ == "__main__":
  sys.exit(main(sys.argv))