#!/usr/bin/python2
# -*- coding: utf-8 -*-

# sudo pip install tornado
#
# Websocket example with tornade web server.
#
# Gerhard Hepp, 2017
# 
import tornado.ioloop
import tornado.web
import tornado.websocket

import threading
import time
import Queue

# debug = True: set for a blink signal, no GPIO usage
# debug = False: set for GPIO usage

debug = False

if not debug:
    import RPi.GPIO as GPIO
   
# messages from Periphaeral Class to Websocket
sendQueue= Queue.Queue()

class PeripheralDebug:
    def __init__(self, sendQueue):
        self.sendQueue = sendQueue
       
    def start(self):
        self.runit = True
        blinkThread = threading.Thread(target=self.blink)
        blinkThread.start()
 
    def stop(self):
        self.runit = False
               
    def blink(self):
        cnt = 0
        while self.runit:
            self.sendQueue.put( { 'data': 'on', 'cnt' : cnt } )
            cnt += 1
            time.sleep(0.5)
            self.sendQueue.put( { 'data': 'off', 'cnt' : cnt } )
            cnt += 1
            time.sleep(0.5)

class PeripheralGPIO:
    def __init__(self, sendQueue):
        self.sendQueue = sendQueue
       
        GPIO.setmode(GPIO.BCM)
        GPIO.setwarnings(False)
       
        self.channel = 4
        GPIO.setup(self.channel, GPIO.IN, pull_up_down=GPIO.PUD_UP)

    def start(self):
        self.runit = True
        blinkThread = threading.Thread(target=self.run)
        blinkThread.start()

    def stop(self):
        self.runit = False
       
    def run(self):
        cnt = 0
        prev = None
        while self.runit:
            res =  GPIO.input(self.channel)
            if prev != res:
                if res:
                    self.sendQueue.put( { 'data': 'on', 'cnt' : cnt } )
                else:
                    self.sendQueue.put( { 'data': 'off', 'cnt' : cnt } )
                cnt += 1
                prev = res
            # for debouncing and limiting number of events per time
            time.sleep(0.01)
           

class PeripheralFactory:
    @staticmethod
    def getPeripheral(debug):
        if debug:
            return PeripheralDebug(sendQueue)
        else:
            return PeripheralGPIO(sendQueue)
                   
peripheral = PeripheralFactory.getPeripheral(debug)
peripheral.start()

class MainHandler(tornado.web.RequestHandler):
    def get(self):
        self.write( """<html>
    <head>
        <title>Websocket sample</title>
    </head>
    <body>
        Sample connection to a Raspberry Pi
        <hr/>
        <div>
           <div  style="position:relative; width:400px; height:80px;" >
               <div id="addr" style="width:400px; height:20px;">    addr    </div>
               <div id="status" style="width:400px; height:20px;">  status  </div>
               <div id="msg" style="width:400px; height:20px;">     message </div>
           </div>
        </div>

        <hr/>

        Connection Status
        <div id="connection" style="position:relative; width:200px; height:40px;background:lightgrey; font: 30px arial, sans-serif;" >
            connection
        </div>
        Message display
        <div id="feld" style="position:relative; width:200px; height:40px;background:lightgrey; font: 30px arial, sans-serif;" >
            message
        </div>
   
 
         <script type="text/javascript">
    "use strict";
   
    console.log("starting...");
    var addr = "ws://" + window.location.hostname + ":" + window.location.port + "/ws";
    console.log(addr);
    document.getElementById("addr").innerHTML = addr;
    var websocket = new WebSocket( addr );
   
    websocket.onmessage = function(e){
        var server_message = e.data;
        var obj = JSON.parse(server_message);
       
        document.getElementById("feld").innerHTML = obj.data;
        if ( obj.data == "on" )
        {
            document.getElementById("feld").style.background = 'yellow';
        }
        else
        {
            document.getElementById("feld").style.background = 'lightblue';
        }
        console.log(server_message);
        document.getElementById("msg").innerHTML = server_message;
    }
   
    websocket.onopen = function(){
       console.log('Connection open!');
       document.getElementById("connection").style.background = 'lightgreen';
       document.getElementById("status").innerHTML = 'connected !';
    }
   
    websocket.onclose = function(){
       console.log('Connection closed');
       document.getElementById("connection").style.background = 'red';
       document.getElementById("status").innerHTML = 'disconnected';
    }
   
    function onClick() {
              try {
                websocket.send( JSON.stringify( { click:1 } ) );
            }
            catch(err) {
                console.log( err.message );
            }
    }
     
    document.getElementById("feld").addEventListener("click", onClick);
  </script>
    </body>
</html>""" )
 
runMessageSend = True       

class ClientWebSocketHandler(tornado.websocket.WebSocketHandler):

    def __init__(self, args, kwargs):
        tornado.websocket.WebSocketHandler.__init__(self, args, kwargs)
        print("ClientWebSocketHandler.init")
       
        self.my_thread = threading.Thread(target = self.run)
        self.my_thread.start()
       
    def run(self):
        while runMessageSend:
            try:
                s = sendQueue.get(block=True, timeout=0.1)
            except Exception:
                continue 
            if self.ws_connection is None:
                print("discard ", s) 
            else:
                print("send ", s) 
                try:
                    self.write_message(s )
                except Exception:
                    pass
           
    def open(self, *args, **kwargs):
        print("open", args, kwargs)

    def on_close(self, *args, **kwargs):
        print ("on_close", args, kwargs)

    def on_message(self, m):
        print ( "received", m )
       
def make_app():
    return tornado.web.Application(
                 [
                     (r"/"  , MainHandler),
                     (r"/ws", ClientWebSocketHandler),
                 ]
                )

if __name__ == "__main__":
    print("start")
    app = make_app()
    app.listen(8080)
    try:
        tornado.ioloop.IOLoop.current().start()
    except KeyboardInterrupt:
        peripheral.stop()
        runMessageSend = False
        tornado.ioloop.IOLoop.current().stop()
    print("stopped")