#!/usr/bin/python
#
# wiicontrol: Control your Internet Tablet from your wiimote
# (c) 2008 Jose Luis Diaz
#
#    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 README)


wiimote_mac='00:1C:BE:11:8D:FC'   # Use your own MAC. Get it with discover.py

import dbus
import dbus.glib
import gobject
import time
import gtk
import bluetooth

import sys
import string
import os
import hildon
import osso
from threading import Thread
from time import sleep
import sys
global mapping, pressed, mapping_b, Wii, 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={
    'U': ["k Up"],
    'D': ["k Down"],
    'L': ["k Left"],
    'R': ["k Right"],
    'A': ["k Return"],
    '+': ["k F7"],       # Zoom/volume +
    '-': ["k F8"],       # Zoom/volume -
    '1': ["k F4"],       # Application Menu
    '2': ["t 25 25"],    # Main desktop menu
    'H': ["k Return"],              # Home button is broken. Do not use it
    'GU': ["a Up"],
    'GD': ["a Down"],
    'GL': ["a Left"],
    'GR': ["a Right"],
    'Z': ["a z"],        
    'C': ["a c"]    
    }

mapping={
    'U': ["a Left"],
    'D': ["a Right"],
    'L': ["a Down"],
    'R': ["a Up"],
    'A': ["k F4"],       # Application menu
    '+': ["k F7"],       # Zoom/volume +
    '-': ["k F8"],       # Zoom/volume -
    '1': ["a space"],       
    '2': ["a Return"],   
    'H': ["k Return"],              # Home button is broken. Do not use it
    'GU': ["a Up"],
    'GD': ["a Down"],
    'GL': ["a Left"],
    'GR': ["a Right"],
    'Z': ["a z"],        
    'C': ["a c"]    
    }

mapping_l={
    'U': ["a Left"],
    'D': ["a Right"],
    'L': ["a Down"],
    'R': ["a Up"],
    'A': ["k F4"],       # Application menu
    '+': ["k F7"],       # Zoom/volume +
    '-': ["k F8"],       # Zoom/volume -
    '1': ["a space"],       
    '2': ["a Return"],   
    'H': ["k Return"],              # Home button is broken. Do not use it
    'GU': ["a w"],
    'GD': ["a s"],
    'GL': ["a a"],
    'GR': ["a d"],
    'Z': ["a z"],        
    'C': ["a c"]    
    }

mapping_snes={
    'U': ["a Left"],
    'D': ["a Right"],
    'L': ["a Down"],
    'R': ["a Up"],
    'A': ["a x"],       
    '+': ["k F7"],       # Zoom/volume +
    '-': ["k F8"],       # Zoom/volume -
    '1': ["a c"],       
    '2': ["a d"],   
    'H': ["k Return"],              # Home button is broken. Do not use it
    'GU': ["a w"],
    'GD': ["a s"],
    'GL': ["a a"],
    'GR': ["a d"],
    'Z': ["a z"],        
    'C': ["a c"]    
    }

pressed={
    'U': [False],
    'D': [False],
    'L': [False],
    'R': [False],
    'A': [False],
    '+': [False],
    '-': [False],
    '1': [False],
    '2': [False],   
    'H': [False],              # Home button is broken. Do not use it
    'GU': [False],
    'GD': [False],
    'GL': [False],
    'GR': [False],
    'Z': [False],        
    'C': [False]    
    }
mapping_b={
    'U': ["k F6"],        # Fullscreen
    'D': ["t 770 25"],    # Tap on close window icon
    'L': ["k Escape"],    # Escape key
    'R': ["t 45 450"],    # Tap on window-list icon
    'A': ["t 25 25", "s 3", "t 25 25"],
    '+': ["k F6"],        # Fullscreen
    '-': ["t 770 25"],    # Tap on close window icon
    '1': ["a 1"],
    '2': ["a 2"],
    'H': ["k Return"],              # Home button is broken. Do not use it
    'GU': ["a Up"],
    'GD': ["a Down"],
    'GL': ["a Left"],
    'GR': ["a Right"],
    'Z': ["a z"],        
    'C': ["a c"]    
    }

#Make sure BT is open
command="dbus-send --system --type=method_call --dest=org.bluez /org/bluez/hci0 org.bluez.Adapter.SetMode string:discoverable"
os.system(command)
forceEnabled=False
class wiicontrolGUI(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_button4 = gtk.Button("SNES")
        self.connect_button4.connect("clicked", self.setSnes)
        
        self.connect_button3 = gtk.Button("Gamepad + Gyro")
        self.connect_button3.connect("clicked", self.setLandscapeGyro)
        
        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.set_from_file("/tmp/f27.jpg")
        self.image.show()
        
        
        self.label = gtk.Label("Press 1+2 on your wiimote now.")
        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_button4, True, True, 0)
        hbox.pack_start(self.connect_button3, 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()                
        while gtk.events_pending():
            gtk.main_iteration()                
        #gtk.main() #We won't start the mainloop yet here.... Maybe later on.
    
    def setLandscape(self, widget):
        global mapping, mapping_l
        print "setLandscape"
        mapping=mapping_l
        

    def setLandscapeGyro(self, widget):
        global mapping, mapping_l, forceEnabled, Wii
        print "setLandscape Gyro"
        mapping=mapping_l
        if not forceEnabled:
            forceEnabled=True
            Wii.enable_force()
            signalUser('Gyros up and running! ')
            

    def setPortrait(self, widget):
        global mapping, mapping_p
        print "setPortrait"
        mapping=mapping_p
        #Wii.enable_nunchuck()


    def setSnes(self, widget):
        global mapping, mapping_snes
        print "setPortrait"
        mapping=mapping_snes
        

 

class keyEventThread(Thread):
   def __init__ (self):
      Thread.__init__(self)

   def handleForces(self, False, xpon, ypon, xnon, ynon):
       print "..."


   def run(self):
      global mapping, pressed, mapping_b, Wii, device, buttonmap
      buttons='+UDLRH-A12'
      self.Stop=False
      xpon=False
      ypon=False
      xnon=False
      ynon=False
      gyroTime=time.time()
      TOLERANCE=6
      try:
          while not Wii.done and not self.Stop:
            Wii._getpacket()
            if forceEnabled and time.time()-gyroTime>0.10:
               gyroTime=time.time()
               #print Wii.force_str()
               gx=Wii.force_gx()
               gy=Wii.force_gy()
               if gx>90: gx=gx-128
               if gy>90: gy=gy-128               
               #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"])


            for bb in buttons:
              if (Wii.buttonmask & buttonmap[bb]):          
                  if (Wii.buttonmask & buttonmap["B"]):
                      ExecuteScript(mapping_b[bb])
                      pressed[bb]=True                      
                  else:
                      ExecuteScript(mapping[bb])
                      pressed[bb]=True
                  
              elif (not (Wii.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 "Did you turn off the wiimote?"
      finally:
        Wii.disconnect()
        print "Wiimote connection closed"


    
def signalUser(usertext):
        global app
        app.label.set_text(usertext)
        while gtk.events_pending():
            gtk.main_iteration()     
        print usertext
        

def disc_started_signal():
        signalUser('Press 1+2 in your wiimote and wait a moment')

def rem_dev_found_signal(address, cls, rssi):
        pass

def rem_dev_name_signal(address, name):
	global wiimote_mac
	print '%20s => %s' % (name, address)
	if name.find("Nintendo")>-1:
		signalUser( "NINTENDO WIIMOTE FOUND AT: " + address)
		wiimote_mac=address
		main_loop.quit()

def disc_completed_signal():
	print "No more bluetooth devices found"
        main_loop.quit()


def do_Discovery():
    bus = dbus.SystemBus()
    bus.add_signal_receiver(disc_started_signal, 'DiscoveryStarted', 'org.bluez.Adapter', 'org.bluez', '/org/bluez/hci0')
    bus.add_signal_receiver(rem_dev_found_signal, 'RemoteDeviceFound', 'org.bluez.Adapter', 'org.bluez', '/org/bluez/hci0')
    bus.add_signal_receiver(rem_dev_name_signal, 'RemoteNameUpdated', 'org.bluez.Adapter', 'org.bluez', '/org/bluez/hci0')
    bus.add_signal_receiver(disc_completed_signal, 'DiscoveryCompleted', 'org.bluez.Adapter', 'org.bluez', '/org/bluez/hci0')
    obj = bus.get_object('org.bluez', '/org/bluez/hci0')
    adapter = dbus.Interface(obj, 'org.bluez.Adapter')
    adapter.DiscoverDevices()
    gobject.threads_init()
    dbus.glib.init_threads()
    
# Use Wiimote module (from pyaxelwii project)
from Wiimote import Wiimote,WiiDiscoverer, buttonmap
    
#Setting GUI and discovery    
app = wiicontrolGUI()

#Bluez based discovery - should update to bluez 4 - cannot use in fremantle until ported
#do_Discovery()

#the hard way to do discovery
d = WiiDiscoverer()
d.find_devices()
while not d.done:
    d.process_event()
if not d.wiimotes:
    print "No wiimotes found."
    sys.exit(1)
wiimotes=d.wiimotes
	
#commented out for a while until multiple wiimote support in.
#for w in wiimotes:
#    w.connect() 

#signalUser( "Locked to mote:"+wiimote_mac)			



# 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("wiicontrol", "0.0.8", False)
device=osso.DeviceState(osso_c)

#print 'Press 1+2 in your wiimote'
#Wii=Wiimote(wiimote_mac,0)

Wii=wiimotes[0]
Wii.connect()

signalUser( 'Wiimote connected. Have fun!')

eventThread=keyEventThread()
eventThread.start()
#gtk.main()
#Wii.disconnect() #Just making sure
#eventThread.Stop=True


