#!/usr/bin/python
# 
# Zeecontrol ((c) 2010 Ruediger Gad) lets you control your N900 
# via a Zeemote bluetooth remote control. Zeecontrol is 
# based on wiicontrol.
#   wiicontrol copyright notice: 
#     wiicontrol: Control your Internet Tablet from your wiimote
#     (c) 2008 Jose Luis Diaz
#
# Zeecontrol is like wiicontrol released under the terms of the
# GNU General Public License:
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.

# ================================================================
# Configurable part (see wiicontrol README)

import gobject
import time
import gtk
import dbus
import sys
import string
import os
import hildon
import osso
from threading import Thread
sys.path.append("/usr/lib/zeecontrol")
from Zeemote import ZeeDiscover, Zeemote, buttonmap
global mapping, pressed, mapping_b, Zee, device, buttonmap

# Some useful key names:
#   Up, Down, Left, Right  (D-pad)
#   Return (this is also the central button of the D-pad)
#   Escape
#   F6  (Fullscreen key)
#   F7  (Zoom/volume -)
#   F8  (Zoom/volume +) 
#   F4  (Menu key)
mapping_p={
    'A': ["a d"],    
    'B': ["a x"],  
    'C': ["a w"],       
    'D': ["a s"],   
    'GU': ["a Up"],
    'GD': ["a Down"],
    'GL': ["a Left"],
    'GR': ["a Right"]
    }

mapping={
    'A': ["a x"],    
    'B': ["a d"],  
    'C': ["a w"],       
    'D': ["a c"],   
    'GU': ["a Up"],
    'GD': ["a Down"],
    'GL': ["a Left"],
    'GR': ["a Right"]
    }

mapping_l={
    'A': ["a d"],    
    'B': ["a w"],  
    'C': ["a a"],       
    'D': ["a s"],   
    'GU': ["a Up"],
    'GD': ["a Down"],
    'GL': ["a Left"],
    'GR': ["a Right"]
    }

mapping_snes={
    'A': ["a d"],    
    'B': ["a w"],  
    'C': ["a a"],       
    'D': ["a s"],   
    'GU': ["a Up"],
    'GD': ["a Down"],
    'GL': ["a Left"],
    'GR': ["a Right"]
    }

pressed={
    'A': [False],
    'B': [False],
    'C': [False],
    'D': [False],
    'GU': [False],
    'GD': [False],
    'GL': [False],
    'GR': [False]
    }

mapping_b={
    'A': ["a d"],    
    'B': ["a w"],  
    'C': ["a Space"],       
    'D': ["k F6"],   
    'GU': ["a Up"],
    'GD': ["a Down"],
    'GL': ["a Left"],
    'GR': ["a Right"]
    }

class zeecontrolGUI(hildon.Program):
  def __init__(self):
    hildon.Program.__init__(self)
    self.saveOn=True
    self.window = hildon.Window()
    self.window.connect("destroy", gtk.main_quit)  
    self.add_window(self.window)
        
    self.connect_button1 = gtk.Button("Gamepad mode")
    self.connect_button1.connect("clicked", self.setLandscape)
        
    self.connect_button2 = gtk.Button("Browser mode")
    self.connect_button2.connect("clicked", self.setPortrait)
        
    self.connect_button3 = gtk.Button("Gamepad + Gyro")
    self.connect_button3.connect("clicked", self.setLandscapeGyro)
        
    self.connect_button4 = gtk.Button("SNES")
    self.connect_button4.connect("clicked", self.setSnes)
        
    vbox = gtk.VBox(False, 0)
    self.window.add(vbox)
    vbox.show()
        
    hbox = gtk.HBox(False, 0)        
    hbox2 = gtk.HBox(False, 0)        
    hbox3 = gtk.HBox(False, 0)        
       
    self.image = gtk.Image()
    self.image.show()
        
    self.label = gtk.Label("Trying to connect to Zeemote...")
    vbox.pack_start(self.label, True, True, 0)
        
    self.label.show()   
    vbox.pack_start(hbox)
    hbox.show()
        
    hbox.pack_start(self.connect_button1, True, True, 0)
    hbox.pack_start(self.connect_button2, True, True, 0)
    hbox.pack_start(self.connect_button3, True, True, 0)
    hbox.pack_start(self.connect_button4, True, True, 0)
               
    self.connect_button1.show()
    self.connect_button2.show()
    self.connect_button3.show()
    self.connect_button4.show()
    self.window.show_all()
    while gtk.events_pending():
      gtk.main_iteration()                
    
  def setLandscape(self, widget):
    global mapping, mapping_l
    print "setLandscape"
    mapping=mapping_l

  def setLandscapeGyro(self, widget):
    global mapping, mapping_l, forceEnabled, Zee 
    print "setLandscape Gyro"
    mapping=mapping_l
            
  def setPortrait(self, widget):
    global mapping, mapping_p
    print "setPortrait"
    mapping=mapping_p

  def setSnes(self, widget):
    global mapping, mapping_snes
    print "setPortrait"
    mapping=mapping_snes
        
class keyEventThread(Thread):
  def __init__ (self):
    Thread.__init__(self)
    self.setDaemon(True)

  def run(self):
    global mapping, pressed, mapping_b, Zee, device, buttonmap
    buttons='ABCD'
    xpon=False
    ypon=False
    xnon=False
    ynon=False
    gyroTime=time.time()
    TOLERANCE=18000

    discover = ZeeDiscover()
    Zee = discover.getZeemote()
    state = Zee.getState()

    signalUser( 'Zeemote connected. Have fun!')

    while state.isConnected() :
      Zee.waitForData()

      state = Zee.getState()

#      print "button mask %d" % state.getButtonMask()

      # Adjust sign and axis association here in order
      # to keep the code taken from wiicontrol below.
      gy=-state.getX()
      gx=-state.getY()

#      print str(gx)+ " " + str(gy)

      #X is positive
      if gx > TOLERANCE and not xpon:
        xpon = True
        ExecuteScript(mapping["GU"])               
      if xpon and gx < TOLERANCE and gx > -TOLERANCE:
        xpon = False
        ExecuteReleasedScript(mapping["GU"])
      #X is negative
      if gx < -TOLERANCE and not xnon:
        xnon = True
        ExecuteScript(mapping["GD"])
      if xnon and gx < TOLERANCE and gx > -TOLERANCE:
        xnon = False
        ExecuteReleasedScript(mapping["GD"])
             
      #Y is positive
      if gy > TOLERANCE and not ypon:
        ypon = True
        ExecuteScript(mapping["GL"])
      if ypon and gy < TOLERANCE and gy > -TOLERANCE:
        ypon = False
        ExecuteReleasedScript(mapping["GL"])
      #Y is negative
      if gy < -TOLERANCE and not ynon:
        ynon = True
        ExecuteScript(mapping["GR"])
      if ynon and gy < TOLERANCE and gy > -TOLERANCE:
        ynon = False
        ExecuteReleasedScript(mapping["GR"])

      buttonmask = state.getButtonMask()
      for bb in buttons:
        if (buttonmask & buttonmap[bb]):          
          if (buttonmask & buttonmap["B"]):
            ExecuteScript(mapping_b[bb])
            pressed[bb]=True                      
          else:
            ExecuteScript(mapping[bb])
            pressed[bb]=True
        elif (not (buttonmask & buttonmap[bb])):
          if pressed[bb]==True:
            pressed[bb]=False                          
            ExecuteReleasedScript(mapping[bb])
            device.display_state_on()

      while gtk.events_pending():
      	gtk.main_iteration()                

    print "Leaving event thread..."
    gtk.main_quit()

def signalUser(usertext):
  global app
  app.label.set_text(usertext)
  while gtk.events_pending():
    gtk.main_iteration()     
  print usertext
   
def setBT(state):
  bus = dbus.SystemBus()
  root = bus.get_object("org.bluez", "/")
  manager = dbus.Interface(root, "org.bluez.Manager")
  defaultAdapter = manager.DefaultAdapter()
  obj = bus.get_object("org.bluez", defaultAdapter)
  adapter = dbus.Interface(obj, "org.bluez.Adapter")
  adapter.SetProperty("Powered", state)

#Setting GUI and discovery    
setBT(True)
app = zeecontrolGUI()
	
# To generate X-window events, we use XTest library
# Use XTest extensions, by directly loading the shared library
from ctypes import CDLL
Xtst = CDLL("libXtst.so.6")
Xlib = CDLL("libX11.so.6")

# Get the current display
d = Xtst.XOpenDisplay(None)

# Generate a keypress, given the keyname.
def SendKey(key):
  sym = Xlib.XStringToKeysym(key)
  code = Xlib.XKeysymToKeycode(d, sym)
  Xtst.XTestFakeKeyEvent(d, code, True, 0)
  Xtst.XTestFakeKeyEvent(d, code, False, 0)
  Xlib.XFlush(d)

def SendKeyDown(key):
  sym = Xlib.XStringToKeysym(key)
  code = Xlib.XKeysymToKeycode(d, sym)
  Xtst.XTestFakeKeyEvent(d, code, True, 0)
  Xlib.XFlush(d)

def SendKeyUp(key):
  sym = Xlib.XStringToKeysym(key)
  code = Xlib.XKeysymToKeycode(d, sym)
  Xtst.XTestFakeKeyEvent(d, code, False, 0)
  Xlib.XFlush(d)

def SendCtrlKey(key):
  SendKeyDown("Control_R")
  SendKey(key)
  SendKeyUp("Control_R")

# Move the mouse to the given (absolute) coordinates
def MouseMove(x, y):
  Xtst.XTestFakeMotionEvent(d, -1, x, y, 0);
  Xlib.XFlush(d)

def MouseMoveRelative(x, y):
  Xtst.XTestFakeRelativeMotionEvent(d, -1, x, y, 0);
  Xlib.XFlush(d)

# Click a mouse button (1:left, 2:right)
def MouseClick(button):
  Xtst.XTestFakeButtonEvent( d, button, True, 0 );
  Xtst.XTestFakeButtonEvent( d, button, False, 0 );
  Xlib.XFlush(d)

def StylusTap(x, y):
  Xtst.XTestFakeMotionEvent(d, -1, x, y, 0)
  Xtst.XTestFakeButtonEvent(d, 1, True, 0 );
  Xtst.XTestFakeButtonEvent(d, 1, False, 0 );
  Xlib.XFlush(d)

# Given a list of strings, each one being a valid command, execute the script
# Valid commands are:
#   "k name"   -> Sendkey(name)
#   "m x y"    -> MouseMove(x,y)
#   "b button" -> MouseClick(button)
#   "t x y"    -> StylusTap(x,y)
#   "s time"   -> sleep(time)
def ExecuteScript(script):
  for c in script:
    cmd=c.split()
    if cmd[0]=='k':
      aux=cmd[1].split('-')
      if len(aux)==1:
        SendKey(cmd[1])
      else:
        SendCtrlKey(aux[1])
    elif cmd[0]=='a':
      SendKeyDown(cmd[1])              
    elif cmd[0]=='b':
      MouseClick(int(cmd[1]))
    elif cmd[0]=='m':
      MouseMove(int(cmd[1]), int(cmd[2]))
    elif cmd[0]=='r':
      MouseMoveRelative(int(cmd[1]), int(cmd[2]))
    elif cmd[0]=='t':
      StylusTap(int(cmd[1]), int(cmd[2]))
    elif cmd[0]=='s':
      sleep(float(cmd[1]))
    else: break

def ExecuteReleasedScript(script):
  for c in script:
    cmd=c.split()  
    if cmd[0]=='a':
      SendKeyUp(cmd[1])
    else: break

# Use osso library to activate the screen backlit on wiimote button
#import osso
#osso_c=osso.Context("zeecontrol", "0.0.8", False)
#device=osso.DeviceState(osso_c)

gobject.threads_init()
eventThread=keyEventThread()
eventThread.start()
gtk.main()

if 'Zee' in globals():
  print "Disconnecting zeemote..."
  Zee.disconnect()

setBT(False)
