#!/usr/bin/env python

# WS server example that synchronizes state across clients

import asyncio
import json
import logging
import websockets
import redis
import time
import serial
import numpy as np
import RPi.GPIO as GPIO
import threading

import os

def is_raspberrypi():
    try:
        with io.open('/sys/firmware/devicetree/base/model', 'r') as m:
            if 'raspberry pi' in m.read().lower(): return True
    except Exception: pass
    return False
print(is_raspberrypi)


# Colordefinition for console print
class pcol:
    HEADER = '\033[95m'
    OKBLUE = '\033[94m'
    OKGREEN = '\033[92m'
    WARNING = '\033[93m'
    FAIL = '\033[91m'
    ENDC = '\033[0m'
    BOLD = '\033[1m'
    UNDERLINE = '\033[4m'


	
#################################################################################
#
#
# G L O B A L S
#

# DMX array config
DMXDATA  = np.zeros([513], dtype='uint8')

# DMX Startcode
DMXDATA[0] = 0				# 0 = Lighting Control Data

# DMX Reset [µs]/float
DMXRST = 88.0				# typical 88µs

# DMX Break [µs]/float
DMXBRK = 8.0				# 8µs (min) to 1s (max)

# DMX Sequence sleep [ms]/float
DMXSLP = 3000.0				# 0µs (min) to 1s (max)

# Buffer for RDM
DMXRDM = "<initial>"
DMXRDMFLAG = True				# 1 for radlines() is active to buffer rdm packages




# 
# Websocket variables
#

# Set to store user-data
USERS = set()

# Set to store other data served by the websocket
# STATE = {}
# DummyFill
# for i in range(1, 10):
# 	STATE.update({str(i): 0}) 





#################################################################################
#
#
# F U N C T I O N S
#
#
###########################
#
# DMX
#
###########################


def sendDMX(data):
    #continuously sending the signal; to receive changed values: Threading
	global DMXRDMFLAG
	global DMXRDM
	global DMXSLP
    
	# UART configuration and definition
	DMXSER = serial.Serial('/dev/serial0', baudrate=250000, timeout=(DMXSLP/1000.0), stopbits=serial.STOPBITS_TWO, bytesize=8)

	while True:
		DMXSER.reset_input_buffer()
		GPIO.output(18, GPIO.LOW)
		
		wr = bytearray(DMXDATA)
		
		print("start read for ", DMXSLP, "ms")
		DMXRDM = DMXSER.read(400)
		print("end read, input is ", DMXRDM)
		
		# GPIO_18 to switch RS-485 driver (IC1) to TX-mode
		GPIO.output(18, GPIO.HIGH)
		
		# send reset
		DMXSER.send_break(DMXRST / 1000000.0)
		
		# break-time
		time.sleep(DMXBRK / 1000000.0)
		
		# send complete data array
		DMXSER.write(wr)
		  
			





###########################
#
# THREADING
#
###########################
slicy = threading.Thread(target=sendDMX, args=(DMXDATA, ))
slicy.start()





###########################
#
# Websockset
#
###########################

async def rdm_state():
	global DMXRDM
	global DMXRDMFLAG
	if USERS and DMXRDMFLAG:
		#message = "rdm=" + DMXRDM + ";"
		print("RDM EVENT   ", DMXRDM)
		await asyncio.wait([user.send("rdm=test;") for user in USERS])



# Notify_State is triggered each time a new connection is established to the websocket
#   and only sent to the new user, not to all!
#   Serving all infos once as complete string, so we only have to send delta from that time on
async def notify_state():
    if USERS:       # asyncio.wait doesnt accept an empty list
        message = state_event()
        await asyncio.wait([user.send(message) for user in USERS])

# Generate message-text for Notify_State
def state_event():

    # we have to send a string
    rtn = ""

    # dont have to send DMXDATA[0], this is the start code
    for index in range(1, len(DMXDATA)):
        rtn += (str(index) + "=" + str(DMXDATA[index]) + ";")

    # give debug feedback
    print("state_event returned ", rtn)
    return rtn







# Users-Event is triggered each time a new connection is established to the websocket
#   and sent to all other but not the new user
async def notify_users():
    if USERS:       # asyncio.wait doesnt accept an empty list
        message = users_event()
        await asyncio.wait([user.send(message) for user in USERS])

# Generate message-text for Notify_Users
def users_event():
	
	# only send the new value
    return "users=" + str(len(USERS)) + ";"


# Handling (un-)registering
async def register(websocket):
    USERS.add(websocket)
    print("New user registered ")
    await notify_users()

async def unregister(websocket):
    USERS.remove(websocket)
    print("Known user left")
    await notify_users()




#
# Sending delta-reports to all users when a value changed except user count
#
async def notify_delta(delta):
    if USERS:       # asyncio.wait doesnt accept an empty list
        await asyncio.wait([user.send(delta) for user in USERS])









#
# Websocket working thread
#
async def websworker(websocket, path):
    
    # register(websocket) sends user_event() to websocket
    await register(websocket)
    try:
        await websocket.send(state_event())
        while True:
            # waiting for a new message resceived on the websocket
            message = await websocket.recv()

            # split message from multiple commands in one string
            data = message.split(";")

            # process each command
            for index in range(len(data)):
                # act[0] will be the key
                # act[1] will be the value
                act = data[index].split("=")

                try:
                    # if key is a number, we will have e change on a dmx value
                    # otherwise, we have a string to check which will be processed in except...
                    # poor python - there is no other chance to check if a string can be a int without errors
                    dmxchannel = int(act[0])
                    dmxvalue = int(act[1])

                    # We have integer, but are they in a valid range?
                    if( dmxchannel < 1 or dmxchannel > 512 or dmxvalue < 0 or dmxvalue > 255):
                        # this must be an exception, we will continue in the exception-area anyway now
                        dmxchannel = int("will raise")

                    # looks valid, so take them to the buffer
                    DMXDATA[dmxchannel] = dmxvalue

                    # notifying our connected users
                    # we can send the resceived command back since the syntax is matched :)
                    await notify_delta(data[index])

                    # debug
                    print("DMX value for channel " + act[0] + " changed to " + act[1])
                
                   




                # It does not look like we have a dmx key-value, so we have to process the command
                except Exception:

                    if act[0] == "manual":
                        sendDMX(DMXDATA)
                        print("Manual send data: ", bytearray(DMXDATA))
                    
                    elif act[0] == "sec":
                        print("")
                    
                    else:
                        print("received message, but can not handle: ", act)

    finally:
        await unregister(websocket)
        





###########################
#
# GPIO
#
###########################
# We have to set GPIO
GPIO.setmode(GPIO.BCM)
GPIO.setwarnings(False)

GPIO.setup(18, GPIO.OUT)
GPIO.setup(4, GPIO.OUT)

# GPIO_18 to switch RS-485 driver (IC1) to RX-mode
#GPIO.output(18, GPIO.LOW)
# V2: will be done in send-loop for rdm

# Powercycle 3V3 on HAT to ensure a cleaned up buffer
GPIO.output(4, GPIO.HIGH)
time.sleep(0.25)
GPIO.output(4, GPIO.LOW)









###########################
#
# Start the websocket
#
###########################

asyncio.get_event_loop().run_until_complete(
    websockets.serve(websworker, port=6789))
asyncio.get_event_loop().run_forever()
